【Linux】进程控制

服务器 0

文章目录

    • 1. fork函数写时拷贝的理解
      • 为什么要写时拷贝?
    • 2. 进程终止
      • 进程退出码
      • 退出方式
        • exit
        • _exit
        • _exit与exit的区别
    • 3. 进程等待
      • wait
      • waitpid
      • 非阻塞轮询

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]$ makegcc -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 tetls: 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

exit c语言函数 表示退出


修改 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]$ ./mytest0 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) SIGUSR111) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+338) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+843) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+1348) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-1253) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-758) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-263) 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

也许您对下面的内容还感兴趣: