【Linux】进程控制

简介: 【Linux】进程控制

1. fork函数写时拷贝的理解

若父子进程数据都不修改,则父子进程指向同一个物理地址,

若子进程数据修改,则拷贝一个物理空间,将新的地址填到子进程对应的页表中,使子进程重新映射,访问到新的空间

进程的内核数据结构,父子各自有一套,彼此双方互不影响,

代码和数据通过写时拷贝的反方式,实现分开

为什么要写时拷贝?

操作系统不允许浪费或者不高效的行为出现的

写时拷贝本质是一种资源筛选,当子进程尝试去修改子进程要用的空间,才进行分配

是一种按需所取资源的策略

2. 进程终止

情况分类


正常执行分为 结果正确, 结果不正确

崩溃了(进程异常)

崩溃的本质:进程因为某些原因,导致进程收到来自操作系统的信号(具体后面解释)

结果对的时候,不会关心过程,但是结果不对,就会关心过程 如: 若小明考了100分,你爸会夸你,但是不会关心你是怎么考的100分

但是若你考砸了,你爸就会问你缘由

进程退出码

int main() return 0;

0代表进程退出码

正确就返回0,不正确就返回非0

供用户进行进程退出健康状态的判定

创建makefile

mytest:test.c
  2   gcc -o $@ $^
  3 .PHONY:clean
  4 clean:
  5   rm -f mytest

修改test.c文件内容

#include<stdio.h>
    2 #include<unistd.h>
    3 #include<assert.h>
    4 int add(int top)
    5 {
    6   int sum=0;  
         int i=0;   
    7   for(i=0;i<100;i++)
    8   {               
    9      sum+=i;     
   10   }
   11   return sum;
   12 }                   
   13 int main()
   14 {          
   15   int result=add(100);
   16   if(result==5050)
   17   {
   18     return 0;
   19   }
   20   else                
   21   {               
   22     return 1;
   23   }                      

使用 echo $? ,查看结果是否正确

[yzq@VM-8-8-centos my]$ make
gcc -o mytest test.c
[yzq@VM-8-8-centos my]$ ./mytest
[yzq@VM-8-8-centos my]$ echo $?
1

结果为1,说明mytest执行结果不正确


[yzq@VM-8-8-centos my]$ ./mytest
[yzq@VM-8-8-centos my]$  echo $?
1
[yzq@VM-8-8-centos my]$ echo $?
0
[yzq@VM-8-8-centos my]$ echo $?
0

echo $? 只会保留最近一次执行的进程的退出码

所以第二次执行 echo $? 执行的是上一个 echo $?(11)的退出码,结果正确返回0

再次修改test.c文件

#include<stdio.h>
    2 #include<unistd.h>
    3 #include<assert.h>
      #include<string.h>>
    4 int main()
    5 {
    6   int i=0;
    7   for(i=0;i<=200;i++)
    8   {
    9    printf("%d %s\n",i,strerror(i));
    10   }
   11 }         

中把错误码转化为描述错误的接口为 strerror

[yzq@VM-8-8-centos my]$ ls tet
ls: cannot access tet: No such file or directory
[yzq@VM-8-8-centos my]$ echo $?
2

ls 进入一个从未定义过的文件 let,就会报错, 使用 echo $? 显示数字2,说明正好对应2的报错信息

退出方式

main函数return退出

其他函数return,仅代表该函数返回

exit

exitc语言函数 表示退出


修改 test.c文件

    #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 int main()
  6 {
  7   int i=0;
  8   for(i=0;i<=200;i++)
  9   {
 10    printf("%d %s\n",i,strerror(i));
 11    exit(123);
 12   }
 13 }                                                                                                                                                                                     

当使用 make生成可执行程序,./mytest运行可执行程序

[yzq@VM-8-8-centos my]$ ./mytest
0 Success
[yzq@VM-8-8-centos my]$ echo $?
123

说明遇到exit后,进程自动释放,使用 echo $? 输出退出码 123

exit(code):code代表进程的退出码


修改test.c文件,在其他函数中使用exit函数

    #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 int add(int top)
  6 {
  7   int sum=0;
  8   int i=0;
  9   for(i=0;i<=top;i++)                                            
 10   {                                                              
 11     sum+=i;                                                      
 12   }                                                              
 13   exit(123);                                                     
 14   return sum;                                                    
 15 }                                                                
 16 int main()                                                       
 17 {                                                                
 18   int sum=add(100);                                              
 19   if(sum==5050)                                                  
 20   {                                                              
 21     return 0;                                                    
 22   }                                                              
 23   else                                                           
 24   {                                                              
 25     return 11;                                                                                                                                                                        
 26   }                                                                                                                  
 27 }                                      

当使用 make生成可执行程序,./mytest运行可执行程序

[yzq@VM-8-8-centos my]$ ./mytest
[yzq@VM-8-8-centos my]$ echo $?
123

发现使用./mytest后,没有任何发生,使用 echo $? 退出码为123

说明在代码的任意地方调用该函数都表示进程退出

_exit

_exit 使用跟 exit功能类似,但是属于系统调用

修改test.c文件内容

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 int add(int top)
  6 {
  7   int sum=0;
  8   int i=0;
  9   for(i=0;i<=top;i++)
 10   {
 11     sum+=i;
 12   }
 13   _exit(123);
 14   return sum;
 15 }                                                                                                                                                                                     
 16 int main()
 17 {
 18   int sum=add(100);
 19   if(sum==5050)
 20   {
 21     return 0;
 22   }
 23   else 
 24   {
 25     return 11;
 26   }                                                              
 27 }     

当使用 make生成可执行程序,./mytest运行可执行程序

[yzq@VM-8-8-centos my]$ echo $?
123

与exit产生结果相同,说明_exit貌似等价于exit

_exit与exit的区别

修改test.c文件内容 并使用exit

#include<stdio.h>  
  2 #include<unistd.h>  
  3 #include<string.h>  
  4 #include<stdlib.h>  
  5 int main()   
  6 {  
  7   printf("hello world");
  8   sleep(2);
  9   exit(123);                                                                                                                                                                          
 10 }   

当使用 make生成可执行程序,./mytest运行可执行程序时发现 会先休眠2秒才会显示hello world

动态演示图在这里


再次修改test.c内容并使用_exit

#include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 #include<stdlib.h>
  5 int main()
  6 {
  7   printf("hello world");
  8   sleep(2);
  9  _exit(123);
 10 }    

当使用 make生成可执行程序,./mytest运行可执行程序时发现 只会休眠2秒没有结果打印出来

exit可以冲刷缓冲区,而_exit直接调用操作系统干掉进程,不会对缓冲区数据做任何刷新

3. 进程等待

如果子进程变成僵尸状态,使用父进程接收子进程的进程退出码,

父进程通过进程等待的方式,回收子进程资源,获取子进程信息

子进程的运行结果 : 代码跑完,结果对

代码跑完 ,结果不对

代码运行异常

前两种使用退出码,来辨别进程结果是否正确

运行异常,通过信号来分析

衡量一个进程 运行 使用 退出码+信号

wait

wait(系统调用)

等待子进程状态的变化

pid_t wait (int*status)

status 现不交代,所以不关系子进程的退出状态,只是回收子进程退出结果

修改test.c文件内容

#include<stdio.h>                                            
  2 #include<unistd.h>                                           
  3 #include<sys/wait.h>                                         
  4 #include<sys/types.h>                                        
  5 #include<stdlib.h>                                           
  6 int main()                                                   
  7 {                                                            
  8   pid_t id=fork();                                           
  9   if(id==0)                                                  
 10   {                                                          
 11     //子进程                                                 
 12     int count=5;                                             
 13     while(count--)                                           
 14     {                                                        
 15       printf("我是子进程,我还活着呢,我还有%dS,pid:%d,ppid:%d\n",count,getpid(),getppid    ());                                                         
 16       sleep(1);                                              
 17     }                                                        
 18     exit(0);//终止进程                                       
 19   }                                                          
 20   sleep(10);                                                 
 21   //父进程                                                   
 22   pid_t ret_id=wait(NULL);                                    23   printf("我是父进程,等待子进程成功,pid:%d,ppid:%d,ret_id:%d\n",ret_id,getpid(),getppi    d());                                                        
 24   sleep(5);                                                                             
 25 }                                                  
~        

子进程运行5秒,再等待5秒后进入父进程,在等待期间子进程处于僵尸状态,父进程将子进程回收,子进程僵尸状态消失,最后再过5秒,父进程退出


复制SSH渠道创建终端2,在保证终端1的mytest可执行程序运行的情况下输入如下指令

while :; do ps axj | head -1 && ps axj | grep mytest | grep -v grep ; sleep 1; echo "---------"; done

在终端1中运行mytest显示

我是子进程,我还活着呢,我还有4S,pid:15839,ppid:15838
我是子进程,我还活着呢,我还有3S,pid:15839,ppid:15838
我是子进程,我还活着呢,我还有2S,pid:15839,ppid:15838
我是子进程,我还活着呢,我还有1S,pid:15839,ppid:15838
我是子进程,我还活着呢,我还有0S,pid:15839,ppid:15838
我是父进程,等待子进程成功,pid:15839,ppid:15838,ret_id:10481

父进程等待成功后,其pid值为子进程的pid值


终端2显示

说明经过等待僵尸进程没有了

waitpid

pid_t waitpid(pid_t pid, int *status, int options);

pid

如果pid>0,表示等待指定的进程

pid=-1,等待任一一个子进程,与wait等效

返回值

如果返回值>0,则表示成功

如果返回值为-1,则表示等待失败

status

是一个输出型参数,类似于一种返回值

期望获取子进程的状态即 获取子进程的退出信号和退出码

kill-l 查看系统提供的信号

[yzq@VM-8-8-centos my]$ kill -l
 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX    

没有0号信号存在,同时信号也是一个数字


int*status

这个指针 不要当做完整的整数,而是看作位图

位图

假设班里8个人,想用数据的方式统计到勤情况

unsigned char exist=0;

0000 0000

用比特位的内容是0还是1来判断当前同学是否到了

从右向左,若到了当前比特位置1,否则置0


修改test.c内容

#include<stdio.h>                                                         
  2 #include<unistd.h>                                                        
  3 #include<sys/wait.h>                                                      
  4 #include<sys/types.h>                                                     
  5 #include<stdlib.h>                                                        
  6 int main()                                                                
  7 {                                                                         
  8   pid_t id=fork();                                                        
  9   if(id==0)                                                               
 10   {                                                                       
 11     //子进程                                                              
 12     int count=5;                                                          
 13     while(count--)                                                        
 14     {                                                                     
 15       printf("我是子进程,我还活着呢,我还有%dS,pid:%d,ppid:%d\n",count,getpid(),getppid    ());                                                                      
 16       sleep(1);                                                           
 17     }                                                                     
 18     exit(12);//终止进程                                                    
 19   }                                                                       
 20   sleep(10);                                                              
 21   //父进程                                                                
 22   int status=0;                                                           
 23   pid_t ret_id=waitpid(id,&status,0);                                  
    24   printf("我是父进程,等待子进程成功,pid:%d,ppid:%d,ret_id:%d,eixt status:%d\n",ret_id,    getpid(),getppid(),status);                                                             
 25   sleep(5);                                                                            
 26 }  

当使用 make生成可执行程序,./mytest运行可执行程序

[yzq@VM-8-8-centos my]$ ./mytest
我是子进程,我还活着呢,我还有4S,pid:30316,ppid:30315
我是子进程,我还活着呢,我还有3S,pid:30316,ppid:30315
我是子进程,我还活着呢,我还有2S,pid:30316,ppid:30315
我是子进程,我还活着呢,我还有1S,pid:30316,ppid:30315
我是子进程,我还活着呢,我还有0S,pid:30316,ppid:30315
我是父进程,等待子进程成功,pid:30316,ppid:30315,ret_id:10481,eixt status:3072

status的返回值为3072,不是exit中的12


进程退出收到的信号,使用最低的7个比特位表示

如果为0,则代表没有收到信号,正常退出

只有当正常退出时,才看退出码,若退出码为0,表示既没有收到信号,又正常结束

若退出码为1、2、3,说明代码正常跑完没有异常,但是结果出错

status有32个比特位,次第8位表示当前进程的退出状态,低7位表示当前进程的退出信号


修改test.c文件内容如下

 #include<stdio.h>                                               
  2 #include<unistd.h>                                              
  3 #include<sys/wait.h>                                            
  4 #include<sys/types.h>                                           
  5 #include<stdlib.h>                                              
  6 int main()                                                      
  7 {                                                               
  8   pid_t id=fork();                                              
  9   if(id==0)                                                     
 10   {                                                             
 11     //子进程                                                    
 12     int count=5;                                                
 13     while(count--)                                              
 14     {                                                           
 15       printf("我是子进程,我还活着呢,我还有%dS,pid:%d,ppid:%d\n",count,getpid(),getppid    ());
 16       sleep(1);                                                 
 17     }                                                           
 18     exit(12);//终止进程                                         
 19   }                                                             
 20   sleep(10);                                                    
 21   //父进程                                                      
 22   int status=0;                                                 
 23   pid_t ret_id=waitpid(id,&status,0);
 24   printf("我是父进程,等待子进程成功,pid:%d,ppid:%d,ret_id:%d,eixt status:%d,child exit     code:%d,child exitsignal:%d\n",ret_id,getpid(),getppid(),status,(status>>8)&0xFF,status    & 0x7F);                                                                                
 25   sleep(5);                                                                        
 26 }                                                                            
~     

查询次低8位退出码和低7位退出信号


当使用 make生成可执行程序,./mytest运行可执行程序

[yzq@VM-8-8-centos my]$ ./mytest
我是子进程,我还活着呢,我还有4S,pid:6118,ppid:6117
我是子进程,我还活着呢,我还有3S,pid:6118,ppid:6117
我是子进程,我还活着呢,我还有2S,pid:6118,ppid:6117
我是子进程,我还活着呢,我还有1S,pid:6118,ppid:6117
我是子进程,我还活着呢,我还有0S,pid:6118,ppid:6117
我是父进程,等待子进程成功,pid:6118,ppid:6117,ret_id:10481,eixt status:3072,child exit code:12,child exitsignal:0

父进程收到子进程的退出码为12,退出信号为0,表示代码正常


父进程在wait的时候,如果子进程没有退出呢,父进程在干什么?

在子进程没有退出的时候,只能一直在调用waitpid进行等待——阻塞等待

父进程一定不是运行状态,所以不在运行队列中,只能在阻塞队列中

非阻塞轮询

马上要考试了,所以李四需要一份复习资料,而张三是一名学霸,所以李四打电话向张三询问,张三说正在整理复习资料中,为了比其他人更快获得资料,所以李四就一直通着电话一直等到张三整理完

下次考试前,李四又找到张三,还是想要复习资料,而张三依旧还在整理复习资料,每过几分钟李四就给张三打电话询问进度,而在等待这个过程中可以干一些其他的事情,直到打电话张三说整理好了

张三可以看作父进程, 打电话 可以看作系统调用waitpid,李四看作子进程

在第一次中,李四给张三打电话一直等待什么都不干,直到说张三说好了才返回即阻塞调用

在第二次中,李四给张三打电话,若张三说没好就挂掉电话,完成一次非阻塞调用,在等待这个过程干别的事情,回头再给李四打电话 即 非阻塞等待

打电话就挂断本质:做了一次张三状态检测,用了多次检测,非阻塞轮询


为了防止子进程一直不退出,而父进程一直在等待,使用非阻塞轮询,使父进程不在一直等待,可以干一些其他事情


waitpid(id,&status,WNOHANG);

WNOHANG 代表非阻塞


#include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/wait.h>
  4 #include<sys/types.h>
  5 #include<stdlib.h>
  6 int main() 
  7 {
  8   pid_t id=fork();
  9   if(id==0)
 10   {
 11     //子进程
 12     int count=5;
 13     while(count--)
 14     {
 15       printf("我是子进程,我还活着呢,我还有%dS,pid:%d,ppid:%d\n",count,getpid(),getppid    ());
 16       sleep(1);
 17     }
 18     exit(12);//终止进程
 19   }
 21   //父进程
 22   while(1)
 23  {
 24   int status=0;
 25   pid_t ret_id=waitpid(id,&status,WNOHANG);
 26   if(ret_id<0)
 27   {                                                                                     
 28     printf("watpid error\n");
 29     exit(1);
 30   }
 31   else if(ret_id==0)
 32   {
       printf("子进程还没退出呢,我做其他的事\n");
 34     sleep(1);
 35     continue;
 36   }
 37   else 
 38   {
 39   printf("我是父进程,等待子进程成功,pid:%d,ppid:%d,ret_id:%d,eixt status:%d,child exit     code:%d,child exitsignal:%d\n",ret_id,getpid(),getppid(),status,(status>>8)&0xFF,status    & 0x7F);
 40   break;
 41   }
 42 
 43  }
 44 }

当使用 make生成可执行程序,./mytest运行可执行程序

[yzq@VM-8-8-centos my]$ ./mytest
子进程还没退出呢,我做其他的事
我是子进程,我还活着呢,我还有4S,pid:27750,ppid:27749
子进程还没退出呢,我做其他的事
我是子进程,我还活着呢,我还有3S,pid:27750,ppid:27749
子进程还没退出呢,我做其他的事
我是子进程,我还活着呢,我还有2S,pid:27750,ppid:27749
子进程还没退出呢,我做其他的事
我是子进程,我还活着呢,我还有1S,pid:27750,ppid:27749
子进程还没退出呢,我做其他的事
我是子进程,我还活着呢,我还有0S,pid:27750,ppid:27749
子进程还没退出呢,我做其他的事
我是父进程,等待子进程成功,pid:27750,ppid:27749,ret_id:10481,eixt status:3072,child exit code:12,child exitsignal:0
相关文章
|
20天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
45 1
|
2月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
49 0
|
9天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
61 13
|
15天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
23天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
1月前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
145 4
linux进程管理万字详解!!!
|
29天前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
1月前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
85 8
|
1月前
|
网络协议 Linux 虚拟化
如何在 Linux 系统中查看进程的详细信息?
如何在 Linux 系统中查看进程的详细信息?
91 1
|
1月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?