Linux 进程控制

打印 上一主题 下一主题

主题 901|帖子 901|积分 2703

目录

一、进程停止
1、进程退出场景
2、进程常见退出方法
a. 正常停止(可以通过 echo $? 检察进程退出码):
b. 异常退出:
3、分类
a. main函数返回值(return 退出)
I. 退出码: 
II. 错误码: 
III. 异常信号:用 kill -l 检察
 b. _exit 函数&&exit 函数
二、进程等待 
1、为什么要进行进程等待?
2、进程等待的方法
a. wait && waitpid
b. 获取子进程status 
​编辑 3、阻塞等待&&非阻塞等待 
a. 阻塞等待
b. 非阻塞等待 
三、进程程序更换
1、更换原理
2、更换函数
execlp:
execv:
execvp:
execle,execve,execvpe:
总结:


一、进程停止

1、进程退出场景

   1、代码运行完毕,结果正确   2、代码运行完毕,结果错误   3、代码异常停止
  2、进程常见退出方法

a. 正常停止(可以通过 echo $? 检察进程退出码):

I.  从main返回   II.  调用 exit    III.  _exit
b. 异常退出:

ctrl + c,信号停止
3、分类

a. main函数返回值(return 退出)

main函数返回值,叫做进程的退出码,一般0,表示进程实行乐成,非0表示失败。
I. 退出码: 

进程的退出码(exit_code),也称为退出状态或返回值,是一个整数,用于表示进程实行结束时的状态,通常由进程的主函数(C语言中是main函数)返回,或者是在进程碰到异常或者错误是由操作体系进行设置。可以利用 echo $? 下令检察近来一次进程退出的退出码信息。$? 是一个特殊的shell变量,保存了上一个下令的退出码

II. 错误码: 

进程的错误码( errno ) 用于表示进程在实行体系调用或库函数时碰到的错误情况,每个错误码都对应一个特定的错误情况,是的程序能够辨认并处置惩罚这些错误。而进程的退出码是一个整数,用于表示进程实行结束时的状态。
错误码errno通常是一组预界说的数值,每个值对应一个特定的错误情况。这些界说通常包含在体系的头文件<errno.h>中。当体系调用或库函数失败时,它们会设置全局变量errno为相应的错误码。
假如errno的值为0,则表示体系调用乐成,假如errno的值不为0,则表示体系调用失败,并且其值对应着特定的错误代码。常见处置惩罚errno的方法,如利用perror函数或者strerror函数,perror函数可以将errno的值映射为对应错误信息,并将其打印到标准错误流(stderr),而strerror函数则可以将errno的值转化为对应的错误字符串。

 

III. 异常信号:用 kill -l 检察



   所以,任何进程最终的实行情况,我们都可以利用两个数字表明具体的实行情况
  

 b. _exit 函数&&exit 函数


  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. int main()
  7. {
  8.    while(1)
  9.    {
  10.      printf("I am a process: %d\n", getpid());
  11.      sleep(1);
  12.      exit(3); // exit 终止进程,status:进程退出时候,退出码                                                                                                            
  13.    }                                         
  14. }  
复制代码

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. void Print()
  7. {
  8.    printf("hello\n");
  9.    exit(5);
  10. }
  11. int main()
  12. {
  13.    while(1)
  14.    {
  15.      printf("I am a process: %d\n", getpid());
  16.      sleep(1);
  17.      Print();                                                                                                                                                           
  18.      //exit(3); // exit 终止进程,status:进程退出时候,退出码
  19.    }
  20. }
复制代码

这就告诉我们,exit就是用来停止进程的,exit(退出码)。
那么这个_exit又是什么呢?把上面的代码中的 exit 改成 _exit 验证一下,结果和exit是一样。岂非说exit 和 _exit 是一样的吗?它们到底有什么区别?


所以,可以得出,exit 会支持刷新缓冲区,而_exit不支持。

二、进程等待 

1、为什么要进行进程等待?


2、进程等待的方法

a. wait && waitpid


对于waitpid中的参数:
status: 
WIFEXITED(status): 若为正常停止子进程返回的状态,则为真(用于检察进程是否退出)。
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码(用于检察你进程的退出码)。

options:
WNOHANG: 若pid指定的子进程还没有结束,则waitpid()函数返回0,不予等待。若正常结束,则返回该子进程的ID。
对于waitpid中的返回值:
正常返回的时间waitpid返回网络到的子进程的进程ID;
假如设置了选项WNOHANG,而调用中的waitpid发现没有已退出的子进程可网络,则返回0;
假如调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误地点;

   假如子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  假如在任意时间调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  假如不存在该子进程,则立即出错返回。
  
 
  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>  
  8.                                             
  9. int main()  
  10. {                                          
  11.    pid_t id = fork();                       
  12.    if(id == 0)  
  13.    {  
  14.      //child  
  15.      int cnt = 5;  
  16.      while(cnt)  
  17.      {  
  18.        printf("Child is running, pid: %d, ppid: %d\n",getpid(),getppid());  
  19.        sleep(1);  
  20.        cnt--;  
  21.      }  
  22.      exit(1);  
  23.    }  
  24.    int status = 0;  
  25.    pid_t rid = waitpid(id, &status, 0);//阻塞等待  
  26.    if(rid > 0)  
  27.    {  
  28.      printf("wait sucess, rid: %d, status: %d\n", rid, status);                                                                                                         
  29.    }                                                                                                                              
  30.    return 0;                                                                                                                       
  31. }  
复制代码

等待乐成,但是我们发现这个status怎么是256呢?这256从哪来的? 
b. 获取子进程status 

wait 和 waitpid,都有一个status参数,该参数是一个输出型参数,由操作体系填充。
假如传递NULL,则表示不关心子进程的退出状态信息,否则,操作体系会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整型来对待,可以当作位图来对待。
可以用下图表示(只研究status低16比特位):

那么回到刚刚的题目,上面那个代码的256是怎么算的呢?
我们上面说过,任何进程最终实行情况,我们都可以利用两个数字表明具体实行情况。
 3、阻塞等待&&非阻塞等待 


阻塞和非阻塞指的是调用者(程序)在等待返回结果(或输入)时的状态。阻塞时,在调用返回结果前,当前线程会被挂起,并在得到结果之后返回。非阻塞时,假如不能立刻得到结果,则该调用者不会阻塞当进步程。
a. 阻塞等待

  1. #include <stdio.h>   
  2. #include <errno.h>   
  3. #include <string.h>   
  4. #include <unistd.h>   
  5. #include <stdlib.h>   
  6. #include <sys/types.h>   
  7. #include <sys/wait.h>   
  8.    
  9.    
  10. int main()   
  11. {   
  12.   //阻塞等待   
  13.   pid_t id = fork();   
  14.   if(id<0) return 1;   
  15.   else if(id == 0)   
  16.   {   
  17.     printf("I am child, pid: %d, ppid: %d\n",getpid(), getppid());   
  18.     sleep(3);   
  19.     exit(100);   
  20.   }   
  21.   else{   
  22.     int status = 0;   
  23.     pid_t rid = waitpid(id, &status, 0);   
  24.     if(rid > 0)   
  25.     {   
  26.       printf("wait sucess, rid: %d, status: %d, exitnum: %d, signo1: %d, signo2:: %d, isexit: %d\n",   
  27.               rid, status, status&0x7f, (status>>8)&0xff, WEXITSTATUS(status), WIFEXITED(status));   
  28.     }                                                                                                                                                                     
  29.   }   
  30.   return 0;   
  31. }
复制代码
看这串代码中,status&0x7f,(status>>8)&0xff,这两个是什么啊?

b. 非阻塞等待 

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>
  8. int main()
  9. {
  10.   pid_t id= fork();
  11.   if(id < 0) return 1;
  12.   else if( id == 0)
  13.   {
  14.     printf("child is running..., pid is: %d\n",getpid());
  15.     sleep(5);
  16.     exit(10);
  17.   }
  18.   else
  19.   {
  20.     int status = 0;
  21.     pid_t rid = 0;
  22.     do
  23.     {
  24.       rid = waitpid(-1, &status, WNOHANG);//非阻塞等待
  25.       if(rid == 0)
  26.       {
  27.         printf("child is running...\n");
  28.       }
  29.       sleep(1);                                                                                                                                                            
  30.     }while(rid == 0);
  31.     if(WIFEXITED(status) && rid == id)
  32.     {
  33.       printf("wait child 5s success, child return code is: %d\n",WEXITSTATUS(status));
  34.     }
  35.     else{
  36.       printf("wait child failed\n");
  37.       return 1;
  38.     }
  39.   }
复制代码
 
三、进程程序更换

1、更换原理

用fork创建子进程后实行的是和父进程雷同的程序(但有可能实行不同的代码分支),子进程往往要调用一种exec函数以实行另外一个程序。当进程调用一种exec函数时,该进程的用户空间和数据被新程序更换,重新程序的启动例程开始实行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

下面我们来用一个最简单的exec的接口来表示一下更换原理:
 
  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>   
  8.    
  9.    
  10. int main()   
  11. {   
  12.   printf("I am a process, pid: %d\n",getpid());   
  13.   printf("exec begin...\n");   
  14.    
  15.   execl("/usr/bin/ls", "ls", "-a", "-l", NULL);//注意这里是NULL,不是"NULL"   
  16.    
  17.   printf("exec end ...\n");                                                                                                                                                
  18.   return 0;   
  19. }
复制代码
 

这里代码末了的printf并没有被实行?为什么?
上面更换原理说了:当进程调用一种exec函数时,该进程的用户空间和数据被新程序更换,重新程序的启动例程开始实行。
2、更换函数


这些函数假如调用乐成则加载新的程序从启动代码开始实行,不再返回。假如调用出错则返回-1。
所以exec函数只有出错的返回值,而没有乐成的返回值。

那这么多我们该如何记住它们呢?---看命名
带p:PATH,你不用告诉体系,程序在哪里,只要告诉我名字是什么,体系更换的时间,会主动去PATH环境变量中查找。
execlp:

  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>   
  8.                                                                                                                                                                            
  9. int main()
  10. {         
  11.   printf("I am a process, pid: %d\n",getpid());
  12.                                                
  13.   pid_t id=fork();
  14.   if(id == 0)     
  15.   {         
  16.     sleep(3);
  17.     printf("exec begin...\n");
  18.                               
  19.    // execl("/usr/bin/ls", "ls", "-a", "-l", NULL);//注意这里是NULL,不是"NULL"
  20.     execlp("ls", "ls","-a","-l",NULL);                                       
  21.     printf("exec end...\n");         
  22.     exit(1);               
  23.   }         
  24.    
  25.   pid_t rid= waitpid(id,NULL,0);
  26.   if(rid>0)                     
  27.   {        
  28.     printf("wait sucess\n");
  29.   }                        
  30.       
  31.   exit(1);
  32. }
复制代码
 
 

   带v:vector 数组        带l:list,列表,参数列表
  execv:


  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>        
  8.                              
  9. int main()                  
  10. {                           
  11.   printf("I am a process, pid: %d\n",getpid());      
  12.                              
  13.   pid_t id=fork();           
  14.   if(id == 0)               
  15.   {                          
  16.     char *const argv[] = {      
  17.       (char*)"ls",           
  18.       (char*)"-a",           
  19.       (char*)"-l",
  20.        NULL            
  21.     };                       
  22.     sleep(3);               
  23.     printf("exec begin...\n");      
  24.                              
  25.     execv("/usr/bin/ls", argv);                                                                                                                                            
  26.     printf("exec end...\n");                                                                                             
  27.     exit(1);                                                                                                            
  28.   }
  29.   
  30.   pid_t rid= waitpid(id,NULL,0);                                                                                         
  31.   if(rid>0)                                                                                                              
  32.   {                                                                                                                     
  33.     printf("wait sucess\n");
  34.   }
  35.   exit(1);
  36. }
复制代码

execvp:


  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>        
  8.                              
  9. int main()                  
  10. {                           
  11.   printf("I am a process, pid: %d\n",getpid());      
  12.                              
  13.   pid_t id=fork();           
  14.   if(id == 0)               
  15.   {                          
  16.     char *const argv[] = {      
  17.       (char*)"ls",           
  18.       (char*)"-a",           
  19.       (char*)"-l",
  20.        NULL            
  21.     };                       
  22.     sleep(3);               
  23.     printf("exec begin...\n");                                 
  24.     execvp("ls", argv);                                                                                                                                          
  25.     printf("exec end...\n");                                                                                             
  26.     exit(1);                                                                                                            
  27.   }
  28.   
  29.   pid_t rid= waitpid(id,NULL,0);                                                                                         
  30.   if(rid>0)                                                                                                              
  31.   {                                                                                                                     
  32.     printf("wait sucess\n");
  33.   }
  34.   exit(1);
  35. }
复制代码
 

当然对于所有的更换函数,我们都不仅可以和上面一样实行体系的指令,也可以实行本身的程序。
  1. //test.c
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <sys/types.h>
  8. #include <sys/wait.h>        
  9.                              
  10. int main()                  
  11. {                           
  12.   printf("I am a process, pid: %d\n",getpid());      
  13.                              
  14.   pid_t id=fork();           
  15.   if(id == 0)               
  16.   {                          
  17.     char *const argv[] = {      
  18.       (char*)"ls",           
  19.       (char*)"-a",           
  20.       (char*)"-l",
  21.       NULL           
  22.     };                       
  23.     sleep(3);               
  24.     printf("exec begin...\n");                                 
  25.     execl("./mytest", "mytest", "-a", "-b", NULL);//注意这里是NULL,不是"NULL"                                                                                                                                            
  26.     printf("exec end...\n");
  27.                                                                                             
  28.     exit(1);                                                                                                            
  29.   }
  30.   
  31.   pid_t rid= waitpid(id,NULL,0);                                                                                         
  32.   if(rid>0)                                                                                                              
  33.   {                                                                                                                     
  34.     printf("wait sucess\n");
  35.   }
  36.   exit(1);
  37. }
  38. //mytest.cc
  39. #include <iotream>
  40. using namespace std;
  41. int main()
  42. {
  43.     cout<<"hello world"<<endl;
  44.     cout<<"hello world"<<endl;
  45.     cout<<"hello world"<<endl;
  46.     return 0;
  47. }
复制代码
execle,execve,execvpe:


带e:本身维护环境变量  execle,execve,execvpe
先观察下面代码:
  1. //test.c
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <sys/types.h>
  8. #include <sys/wait.h>
  9. int main
  10. {
  11.     printf("I am a process, pid: %d\n", getpid());
  12.     pid_t id=fork();
  13.     if(id == 0)
  14.     {
  15.         sleep(1);
  16.         execl("./mytest","mytest",NULL);
  17.         printf("exec end...\n");
  18.         exit(1);
  19.     }
  20.     pid_t rid = waitpid(id, NULL, 0);
  21.     if(rid > 0)
  22.     {
  23.         printf("wait sucess\n");
  24.     }
  25.     exit(1);
  26. }
  27. //mytest.cc
  28. #include <iostream>   
  29. #include <unistd.h>   
  30. using namespace std;   
  31.    
  32. int main()   
  33. {   
  34.   for(int i = 0; environ[i]; i++)                                                                  
  35.   {   
  36.         printf("env[%d]: %s\n", i, environ[i]);   
  37.   }   
  38.   cout<<"hello world"<<endl;   
  39.    
  40.   return 0;   
  41. }
复制代码
 
这里我们传递环境变量表了吗?? ---没有,子进程默认就拿到了,它是怎么做到的? 
默认可以通过地址空间继承的方式,让所有子进程拿到环境变量,进程更换不会更换环境变量数据
   1、假如我们想让子进程继承全部的环境变量,直接能拿到
  2、假如单纯的新增我们可以利用putenv
  3、那么我想设置全新的环境变量给子进程呢?
  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <sys/types.h>
  7. #include <sys/wait.h>
  8. int main()   
  9. {   
  10.   char* const env[]={   
  11.     (char*)"one=1111111111111",   
  12.     (char*)"two=2222222222222",
  13.     NULL
  14.   };   
  15.    
  16.    
  17.    
  18.   printf("I am a process, pid: %d\n",getpid());   
  19.    
  20.   pid_t id=fork();   
  21.   if(id == 0)   
  22.   {   
  23.     sleep(1);   
  24.     execle("./mytest","mytest",NULL,env);   
  25.     printf("exec end...\n");
  26.     exit(1);
  27.   }
  28.   pid_t rid= waitpid(id,NULL,0);
  29.   if(rid>0)
  30.   {
  31.     printf("wait sucess\n");
  32.   }
  33.   exit(1);
  34. }
复制代码

这样我就设置了全新的环境变量给子进程! 
总结:

   究竟上,只有execve是真正的体系调用,其它五个函数最终都调用 execve,都是体系调用 execve的封装,所以execve在man手册的第2节,其它函数在man手册的第3节。   

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表