LINUX进程控制(上)

重点:

  • 进程创建,fork/vfork
  • 进程等待
  • 进程替换,微型shell,重新认识shell运行原理
  • 进程终止,认识$

一、进程创建:

  fork函数: 从已存在进程中创建一个新进程。新进程为子进程,原进程为父进程。

2、 进程调用fork,当控制到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程

  • 将父进程部分数据结构内容拷贝至子进程

  • 添加子进程到系统进程列表当中

  • fork返回,开始调度器调度

    这里写图片描述

3、写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的。

4、fork常规用法

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如:父进程等待客户端请求,生成子进程来处理请求。
  • 一个进程要执行一个不同的程序。例如进程从fork返回后,调用exec函数。

5、进程调用失败的原因

资源不够用
1、系统中有太多进程
2、实际用户的进程数超过了限制

6、vfork

也是用来创建子进程,但是 vfork用于创建一个子进程,而

子进程和父进程共享地址空间

,fork的子进程具有独立地址空间。

vfork

图解:
这里写图片描述
代码验证结果:
这里写图片描述

父子进程地址一样得原因?

物理内存地址不一样,但其对应的虚拟内存地址一样

fork

图解:
这里写图片描述
代码验证结果:
这里写图片描述

vfork保证子进程先运行

,在它调用exec或(exit)之后父进程才可能被调度运行。
代码:

  1 #include<stdio.h>
  2 #include<stdlib.h>                                   
  3 #include<unistd.h>
  4 int g_val=100;
  5 int main()
  6 {
  7     pid_t id=vfork();
  8     if(id==0){//child
  9        // g_val=200;
 10        sleep(5);
 11         printf("I am child,%d, %p\n",g_val,&g_val);
 12         exit(0); 
 13    }
 14     else if(id>0)
 15     {//parent
 16      sleep(2);
 17       printf("I am parent,%d,%p\n",g_val,&g_val);
 18     }
 19     else{
 20         perror("vfork");
 21         exit(1);
 22     }
 23     return 0;
     }

不加exit(0),会出现死循环。为什么?

这里写图片描述

怎么验证子进程在父进程的地址空间中运行?

代码:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 int g_val=100;
  5 int main()
  6 {
  7     pid_t id=vfork();
  8     if(id==0){//child
  9         g_val=200;
 10        sleep(5);
 11         printf("I am child,%d, %p\n",g_val,&g_val);
 12       exit(0);                                       
 13    }
 14     else if(id>0)
 15     {//parent
 16      sleep(2);
 17       printf("I am parent,%d,%p\n",g_val,&g_val);
 18     }
 19     else{
 20         perror("vfork");
 21         exit(1);
 22     } 
 23     return 0;
     } 

代码运行结果:
这里写图片描述
总结:

子进程直接改变了父进程的变量值,因为子进程在父进程的地址空间中运行。

二、进程终止:

进程常见退出方法
正常终止(可以通过 echo $? 查看进程退出码):

 1、从main返回
 2、调用exit
 3、_exit

异常退出:

  • ctrl + c,信号终止

进程的退出码?

main函数中return的返回值称为程序的退出码,想通过退出码来知道程序运行正确与否。

在linux下如何获得程序的退出码?

$+? 称之为程序的退出码
这里写图片描述

这里写图片描述

No such file or directory

程序执行完了,但是结果不对,因为它的退出码不对,退出码就转化成为错误消息。

1、正常退出

1)return退出:

main函数return和其他函数return有啥区别?

其他函数return表明该函数退出,main函数还表示进程结束,因此要使进程结束必须在main函数中return。
测试:

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 int fun()
  5 {
  6     return 32;
  7 }
  8 int main()
  9 {
 10     fun();                                           
 11   
 12 }  

测试结果:
这里写图片描述

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 int fun()
  5 {
  6     return 32;
  7 }
  8 int main()
  9 {
 10     fun();                                           
 11     return 0;
 12 }  

测试结果:
这里写图片描述

事实证明main函数的返回值是进程结束的标志,其他函数只表示此函数的退出码

2)exit

总结:

在函数的任何地方调用exit都会导致进程退出

在main函数return才会导致进程退出

在程序结束时的数字叫做程序的退出码

3)_exit

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

这里写图片描述
exit 和return 在程序退出时都会刷新缓冲区,输出hello world

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

这里写图片描述
_exit不会刷新缓冲区,不会输出hello world

进程调用exit时,系统要做哪些工作?

数据和代码都要清理,所对应的PCB数据结构里面的内容也要清理。

2、异常退出:

在任意位置退出称之为异常退出

三、进程等待:

1、存在意义:

1)子进程退出,父进程若不管,就造成僵尸进程,造成内存泄漏
2)僵尸进程无法杀死
3)父进程给子进程的任务完成得如何。
4)父进程通过进程等待的方式,回收进程资源,获取子进程退出信息。

2、方法

1)wait

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
  • 返回值:成功返回被等待进程pid,失败返回-1.
  • 参数:输出型参数,获取子进程退出状态,不关心则可设置为NULL

例;

  1 #include<stdio.h>                                    
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8     pid_t id=fork();
  9     if(id==-1){
 10         perror("fork");
 11         exit(1);
 12     }
 13     else  if(id==0){//子进程
 14         printf("I am child: I am pid: %d, I am ppid:%    d ",getpid(),getppid());
 15         sleep(3);
 16         printf("child is done!\n");
 17         exit(1);
 18     }      
 19     else{//父进程
 20            printf("I am parent: I am pid:%d,I am ppid    :%d ",getpid(),getppid());
 21       pid_t id=wait(NULL);
        printf("father is done!\n");  
 23     }
 24        
 25    return 0;
 26     
 27 }                  

加上 pid_t id=wait(NULL)之前:

这里写图片描述

分析: 由于sleep(3)秒,所以子进程存活时间较长,等待父进程结束后,子进程才结束

之后:
这里写图片描述

由于wait的缘故,父进程必须等待子进程结束后,才能结束

2)waitpid方法

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

1、返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;

  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;

  • 如果调用中出错,则返回-1,这时error会被设置成相应的值以指示错误所在。
    2、参数:

  • pid:

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

 pid_t id=waitpid(-1,NULL,0);

运行结果:
这里写图片描述

pid>0. 等待其进程ID与pid相等的子进程。

  • status:

    WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
    WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。

  • options

    WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
    获取子进程status:

    这里写图片描述

  1 #include<stdio.h>                                    
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8     int status=0;
  9     pid_t id=fork();
 10     if(id==-1){
 11         perror("fork");
 12         exit(1);
 13     }
 14     if(id==0){//子进程
 15         printf("I am child: I am pid: %d, I am ppid:%    d \n",getpid(),getppid());
 16         sleep(3);
 17         exit(0);
 18     }      
 19     else{//父进程
 20         pid_t pid = waitpid(id,&status,0);
 21         if(WIFEXITED(status))
 22             printf("%d\n",WEXITSTATUS(status));
 23            printf("I am parent: I am pid:%d,I am ppid    :%d \n",getpid(),getppid());
 24 
 25     }
 26        
 27    return 0;
 28     
 29 }                      


这里写图片描述

没收到信号,结果对不对就要通过子进程的退出码来决定

 8     int status=0;
  9     pid_t id=fork();
 10     if(id==-1){
 11         perror("fork");
 12         exit(1);
 13     }
 14     if(id==0){//子进程
 15         printf("I am child: I am pid: %d, I am ppid:%d \n",getpid(),getppid());
 16         sleep(1);
 17         printf("child is done...!\n");
 18         exit(1);
 19     }      
 20     else{//父进程
 21         pid_t pid = waitpid(id,&status,0);
 22          printf("I am parent: I am pid:%d,I am ppid:%d \n",getpid(),getppid());
 23      if(pid>0)
 24      {
 25             printf("child get sig is :%d,exit code:%d\n",status&0x7F,(status>>8)&0xFF);                           
 26     }
 27       printf("father done..!,my son is quit ,pid is:%d\n",pid);
 28      }

这里写图片描述
这里写图片描述
父进程没收到子进程被杀的信号,父进程知道子进程事有没有办好
异常退出,退出码毫无意义

阻塞式:调度器一直调用子进程,不管父进程,子进程不结束,父进程就一直在等。

  1 #include<stdio.h>                                                                                                 
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8     int status=0;
  9     pid_t id=fork();
 10     if(id==-1){
 11         perror("fork");
 12         exit(1);
 13     }
 14     if(id==0){//子进程
 15         printf("I am child: I am pid: %d, I am ppid:%d \n",getpid(),getppid());
 16         while(1){
 17         sleep(1);
 18 
 19         }
 20         printf("child is done...!\n");
 21         exit(1);
 22     }      
 23     else{//父进程
 24 
 25         pid_t pid = waitpid(id,&status,0);
            printf("I am parent: I am pid:%d,I am ppid:%d \n",getpid(),getppid());
 26         if(pid>0)
 27         {
 28             printf("child get sig is :%d,exit code:%d\n",status&0x7F,(status>>8)&0xFF);
 29         }
 30       printf("father done..!,my son is quit ,pid is:%d\n",pid);
 31      }
 32        
 33    return 0;
 34     
 35 }                                                                                                                 
-- INSERT --              

这里写图片描述
再开一个终端:
这里写图片描述
则:父进程收到子进程被杀掉的信号,则父进程回收子进程的id
这里写图片描述

子进程一直运行,父进程一直等待

非阻塞式:

  1 #include<stdio.h>                                                                                                 
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<sys/wait.h>
  6 int main()
  7 {
  8     int status=0;
  9     pid_t id=fork();
 10     if(id==-1){
 11         perror("fork");
 12         exit(1);
 13     }
 14     if(id==0){//子进程
 15         printf("I am child: I am pid: %d, I am ppid:%d \n",getpid(),getppid());
 16         while(1){
 17         sleep(1);
 18         }
 19         printf("child is done...!\n");
 20         exit(1);
 21     }      
 22     else{//父进程
 23   //      pid_t pid = waitpid(id,&status,0);
           printf("I am parent: I am pid:%d,I am ppid:%d \n",getpid(),getppid());
 25          pid_t ret;
 26          do{
 27          ret=waitpid(-1,&status,WNOHANG);
 28          if(ret==0){
 29              printf("father do other things!\n");
 30          }
 31          else if(ret>0)
 32          {
 33              printf("father wait success!\n");
 34              if(WIFEXITED(status)){
 35                  printf("child exit code is:%d\n",WEXITSTATUS(status));
 36              }
 37              else{
 38                  printf("child is quit by signal!\n");
 39              }
 40              break;
 41          }                                                                                                        
 42          else{
 43              printf("waitpid error!\n");
 44              break;
 45          }
 46          sleep(1);
 47          }while(1);
 48          // if(pid>0)
 49     // {
 50      //       printf("child get sig is :%d,exit code:%d\n",status&0x7F,(status>>8)&0xFF);
 51    // } 
 52       printf("father done..!");
 53      }
 54    return 0;
 55 }                  

这里写图片描述
父进程再检测子进程,但子进程一直没退出,
信号中断:
开另一个终端:
这里写图片描述
结果:
这里写图片描述

父进程成功退出,子进程被信号中断
正常退出:
去掉while(1){ },让子进程睡5秒
这里写图片描述
等待失败:

 1、ret=waitpid(id+4,&status,WNOHANG);
 2else{                                                                                                    
              printf("waitpid error!,%d\n",ret);
       }

这里写图片描述
接受失败,返回-1
进程替换见下篇文章。

猜你喜欢

转载自blog.csdn.net/adzn1/article/details/80196802