【Linux】开始掌握进程控制吧!

这篇具有很好参考价值的文章主要介绍了【Linux】开始掌握进程控制吧!。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
送给大家一句话:
我并不期待人生可以一直过得很顺利,但我希望碰到人生难关的时候,自己可以是它的对手。—— 加缪

1 前言

通过对进程的学习,我们对虚拟地址,页表,物理地址有了大概认识。我们平时使用的地址都是虚拟地址,通过页表可以访问物理地址(统一的视角进行控制,保证数据安全)。也认识到写时拷贝
也认识O(1)调度算法,通过两个队列(活跃队列,过期队列)完成进程的分时控制,通过优先级来放入不同位置,以时间复杂度O(1)快速寻找进程。

2 进程创建

2.1 fork函数初识

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。fork函数具有两个返回值,通过对返回值的判断(if else )可以进行父进程和子进程的不同书写。
注意:进程调用fork,当控制转移到内核中的fork代码后,内核做以下工作:

  1. 分配新的内存块和内核数据结构给子进程
  2. 将父进程部分数据结构内容拷贝至子进程(进程:内核的相关数据管理的数据结构(task_struct + mm_struct + 页表)+ 代码与数据)
  3. 添加子进程到系统进程列表当中
  4. fork返回,开始调度器调度

这里是为了保证父进程和子进程的独立性。

2.2 fork函数返回值

  • 子进程返回0
  • 父进程返回的是子进程的pid

那为什么父进程返回子进程PID ,给子进程返回0呢???
很好理解:就像现实生活中,父母有了孩子,会给他或她起一个名字,父母知道了名字,就可以很好管理孩子。父进程与子进程同理,父进程为了便于管理子进程,所以fork函数会返回对应子进程的pid。

2.3 写时拷贝

通过图解可以很好理解写时拷贝。
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
在创建子进程的时候,子进程的页表映射与父进程一致(默认继承的),一旦子进程要进行修改数据,为了保证进程的独立性(保证父进程安全运行),不得不开辟一个新空间,并修改子进程页表的映射(虚拟地址不变!)。

2.4 fork常规用法

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

一般使用if else 分开书写,也可以通过系统调用打开新的进程。

2.5 fork调用失败的原因

  1. 系统中有太多的进程(数据空间是有限的)
  2. 实际用户的进程数超过了限制(必须是有限的)

2 进程终止

2.1 终止是在做什么

进程终止会进行:

  1. 释放曾经的代码和数据所占据的空间
  2. 释放内核数据结构

但是task_struct会延期处理,因为终止的进程处于Z状态(僵尸进程)

2.2 进程终止的情况

我们的main函数常常会有一个返回值 0 ,那为什么要返回零呢???

  1 #include<stdio.h>  
  2 #include<unistd.h>  
  3 #include<string.h>  
  4   
  5 
  6 int main()
  7 {
  8    
  9   printf("I am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
 10   sleep(2);
 11    
 12   return 100;                                                                                                                                                                 
 13 }  

来看我们返回100(为了效果明显)时
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
echo 打印的是bash 的环境变量,这个100 就是刚才进程返回到父进程(bash)的退出码(环境变量 表示最近一个进程的退出码),一般0表示正常运行,非零表示有问题。
父进程关心子进程的信息,想要知道子进程是否正常运行。不同的退出码表示不一样的失败原因,我们来获取一下:

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<string.h>
    4 
    5 int g_val = 10000;
    6 
    7 int main()
    8 {
    9   
   10   for(int errcode = 0 ;errcode <=255; errcode++)
   11   {
   12     printf("%d: %s\n",errcode,strerror(errcode));
   13                                                                                                                                                                             
   14   }                                                                                                                                                
   15   printf("I am parent process,pid:%d,ppid:%d\n",getpid(),getppid());                                                                               
   16   sleep(1);                                                                                                                                        
   17                                                                                                                                                    
   18   return 0;                                                                                                                                        
   19 }     

这样就可以获取所有的退出码和对应的退出信息了:
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
通过退出码就能获取对应退出信息,来告知用户为什么退出了。其底层实现也好理解,通过数字与字符串的一一对应来做到。
常见进程退出场景:

  1. 代码运行完毕,结果正确(正常结束进程)
  2. 代码运行完毕,结果不正确
  3. 代码异常终止,出现异常提前退出

就像:VS编程运行的时候,如果崩溃了 — 操作系统发现你的进程做了不应该做的事情,OS就杀死进程!!!
一旦出现异常,退出码就没有意义了!!! 为什么出异常才是最重要的!!!
那为什么会出现异常呢??? 原因是:进程出现异常的本质是进程收到来自OS发给进程的信号!(kill -9 就是一个信号)

注意:

  • 先确认是否异常
  • 不是异常就是代码正常跑完,看退出码即可。
  • 可以通过退出信号来判断出现了什么异常

2.3 如何终止

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

  1. 从main函数return,表示进程终止
  2. 调用exit
  3. _exit
    异常退出
    ctrl + c,信号终止

来看手册中如何描述的:
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
调用exit 函数试试:

  1 #include<stdio.h>  
  2 #include<unistd.h>  
  3 #include<string.h>  
  4 #include<stdlib.h>
  5 int g_val = 10000;  
  6   
  7 int main()  
  8 {  
  9   
 10   printf("I am parent process,pid:%d,ppid:%d\n",getpid(),getppid());
 11   sleep(1);
 12   exit(123);                                                                                                                                                                  
 13                                                                                                                                     
 14 }                                                                                                                                                
 15    

运行后是这样的效果:
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
exit比return 直接,调用一次就可以完全退出!

_exit 是一个系统调用(system call),参数与exit一致,使用与exit几乎一模一样。

**但是exit会冲刷缓冲区,而_exit 不会,**因为缓冲区在系统调用之上 ,而exit 是一个C语言库函数。图解:

exit最后也会调用_exit, 但在调用_exit之前,还做了其他工作:

  1. 执行用户通过 atexit或on_exit定义的清理函数。
  2. 关闭所有打开的流,所有的缓存数据均被写入
  3. 调用_exit
    【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++

3 进程等待

3.1 进程等待必要性

  • 子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程
  • 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

也就是说,任何进程在退出时都要被父进程进行等待,不然子进程处于僵尸进程就会造成内存泄漏!!!

3.2 进程等待的方法

  1. wait方法
  2. waitpid方法
  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
  • 如果不存在该子进程,则立即出错返回

wait方法

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

我们测试一下:

  1 #include<stdio.h>                                          
  2 #include<unistd.h>                                         
  3 #include<string.h>                                         
  4 #include<stdlib.h>                                         
  5 #include<sys/types.h>                                      
  6 #include<sys/wait.h>                                       
  7                                                            
  8 void Childrun()                                            
  9 {                                                          
 10   int cnt = 5;                                             
 11   while(cnt)                                               
 12   {                                                        
 13     printf("I am child ,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
 14     sleep(1);                                              
 15     cnt--;                                                 
 16   }                                                        
 17 }                                                          
 18                                                            
 19 int main()                                                 
 20 {                                                          
 21                                                            
 22   printf("I am father ,pid:%d,ppid:%d\n",getpid(),getppid());
 23                                                            
 24   pid_t id = fork();                                       
 25   if(id == 0)                                              
 26   {                                                        
 27     //child                                                
 28     Childrun();                                            
 29     printf("child quit...\n");                             
 30     exit(0);                                               
 31   }                                                        
 32   //father                                                 
 33   sleep(10);                                               
 34   pid_t rid = wait(NULL);                                  
 35   if(rid > 0)                                              
 36   {                                                        
 37     printf("wait success,rid:%d\n",rid);                   
 38   }                                                        
 39   sleep(3);                                                                        
 40   return 0;                                                  
 41 }   

这个程序会在子进程运行结束前等待子进程,并且会存在一段时间的窗口期,此时子进程处于僵尸进程:

在这个父进程等待的过程中,父进程一直在等待子进程的退出,处于阻塞等待状态。父进程本质是等待某种软件条件就绪,那么如何理解阻塞等待子进程呢???
就是把自己列入等待队列,把状态列入不运行状态,等待子进程(类似scanf 的阻塞)。

waitpid方法

#include<sys/types.h>
#include<sys/wait.h>
pid_ t waitpid(pid_t pid, int *status, int options);

注意其中的三个参数!

返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid = -1 , 等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID

pid_t rid = waitpid(-1,NULL,0); 与刚才的wait等价!!!如果id不为-1,是一个对应的id,那么就会等待指定进程!!!,如果id错误(不存在该进程),就会发生等待错误!!!
status 是一个输出型参数,需要我们传入一个指针来获取。来测试一下(子进程退出码设置为1 )

  1 #include<stdio.h>                    
  2 #include<unistd.h>                   
  3 #include<string.h>                   
  4 #include<stdlib.h>                   
  5 #include<sys/types.h>                
  6 #include<sys/wait.h>                 
  7                                      
  8 void Childrun()                      
  9 {                                    
 10   int cnt = 5;                       
 11   while(cnt)                         
 12   {                                  
 13     printf("I am child ,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid(),cnt);
 14     sleep(1);                        
 15     cnt--;                           
 16   }                                  
 17 }                                    
 18                                      
 19 int main()                           
 20 {                                    
 21                                      
 22   printf("I am father ,pid:%d,ppid:%d\n",getpid(),getppid());
 23                                      
 24   pid_t id = fork();                 
 25   if(id == 0)                        
 26   {                                  
 27     //child                          
 28     Childrun();                      
 29     printf("child quit..\n");       
 30     exit(1); //退出码设置为1  
 31   }                                  
 32   //father                           
 33   sleep(10);                         
 34   int status = 0 ;                   
 35   pid_t rid = waitpid(id , &status , 0);
 36   if(rid > 0)                        
 37   {                                  
 38     printf("wait success,rid:%d\n",rid);
 39   }                                  
 40   printf("father quit...,status:%d\n",status);                                     
 41   sleep(3);                                                                  
 42   return 0;                                                                  
 43 } 

【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
这就成功获取了status!
我们需要的是 退出码 和 退出信号,那么我们如何通过status获取这两个数据呢???
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
也就通过位运算就可以成功获取了。
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++这样就可以了:
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
通过两个信息就可以判断进程是否正常运行,如果异常,也能知道异常原因了。

当然,如果使用位运算就有点那啥了,我们可以使用宏:

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

对于第三个参数,就可以让父进程在等待的刚才中区做其他事情。也就是进行非阻塞等待:WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID

  1. 阻塞等待就类似张三给李四打电话帮忙,李四正在忙,告诉张三等一会,然后张三这个电话就不挂了,等着李四完成工作,张三也不做其他事情。
  2. 非阻塞等待类似张三给李四打电话帮忙,李四正在忙,告诉张三等一会,然后张三说李四忙完打回来,张三就先去做其他事情。

下面写入了一段非阻塞轮询等待的代码,这样就能保证父进程在等待的过程中,可以去做其他事情!

 34   while(1)                                                                                                                                 
 35   {                                                                                                                                        
 36     int status = 0 ;                                                                                                                       
 37     pid_t rid = waitpid(id , &status , 0);                                                                                                 
 38     if(rid == 0)                                                                                                                           
 39     {                                                                                                                                      
 40       sleep(1);                                                                                                                            
 41       printf("child is running ,father check next time!\n");                                                                               
 42     }                                                                                                                                      
 43     else if(rid > 0)                                                                                                                        
 44     {                                                                                                                                      
 45       if(WIFEXITED(status))                                                                                                                
 46       {                                                                                                                                    
 47         printf("child quit success,child exit code:%d\n",WEXITSTATUS(status));                                                             
 48       }                                                                                                                                    
 49       else                                                                                                                                 
 50       {                                                                                                                                    
 51         printf("child quit unnormal!\n");                                                                                                  
 52       }                                                                                                                                    
 53       break;                                                                                                                               
 54     }                                                                                                                                      
 55     else                                                                                                                                   
 56     {                                                                                                                                      
 57       printf("waitpid failed!\n");                                                                                                         
 58       break;                                                                                                                                                                  
 59     }                                                            
 60   }

来看运行效果(父进程一直在查):
【Linux】开始掌握进程控制吧!,一起学Linux吧!,linux,java,运维,c语言,算法,学习,c++
这样就完成了。文章来源地址https://www.toymoban.com/news/detail-846440.html

4 总结

  1. 等待很容易理解,等待是必须进行的,回收子进程资源,获取子进程退出信息
  2. 进程等待常用waitpid,并常用非阻塞等待

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

到了这里,关于【Linux】开始掌握进程控制吧!的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】从零开始认识进程 — 前篇

    我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。。——山本耀司 学习进程,我们需要对计算机操作系统 有一个初步的了解,也就是经典的冯诺依曼体系: 计算机的逻辑结构。冯·诺依曼从逻辑入手,他的逻辑设计具有以下特点: (

    2024年03月18日
    浏览(60)
  • 从零开始学习Linux运维,成为IT领域翘楚(十)

    防火墙管理工具 firewalld概述 Centos 系统中集成了多款防火墙管理工具,其中 firewalld服务是默认的防火墙配置管理工具,它拥有基于 CLI(命 令行界面)和基 于 GUI(图形用户界面)的两种管理方式。 firewalld 中常用的区域名称及策略规则 区域 默认策略规则 trusted 允许所有的数

    2024年02月03日
    浏览(56)
  • 从零开始学习Linux运维,成为IT领域翘楚(二)

    文件系统组织结构 登录系统后,在当前命令窗口下输入命令: 用户管理概述 Linux是一个多用户、多任务的操作系统。 用户账号和用户组 用户概念 用户组概念    用户组(group)就是具有相同特征的用户(user)的集合体;比如有时我们要让多个用户具有相同的权限,比如查

    2024年02月01日
    浏览(41)
  • 从零开始学习Linux运维,成为IT领域翘楚(一)

      Linux内核最初只是由芬兰人林纳斯·托瓦兹1991年在赫尔辛基大学上学时出于个人爱好而编写的。 Linux特点 Linux的各个发行版本 Linux应用领域 IT服务器 嵌入式 个人桌面 Linux和Windows区别 主要区别如下 安装VMWare虚拟机 下载CentOS 进入Centos官网找要下载的版本 https://vault.centos

    2024年02月02日
    浏览(90)
  • 从零开始学习Linux运维,成为IT领域翘楚(九)

    Service命令 服务(service) 本质就是进程,但是是运行在后台的,通常都会监听某个端口,等待其它程序的请求,比如(mysql , sshd 防火墙等),因此我们又称为守护进程。 语法: 注意: 示例1 查看当前防火墙的状况,关闭防火墙和重启防火墙。 Systemd命令 Linux的启动一直采用init进

    2024年02月03日
    浏览(52)
  • 《Linux操作系统编程》 第六章 Linux中的进程监控: fork函数的使用,以及父子进程间的关系,掌握exec系列函数

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月11日
    浏览(34)
  • Linux之进程(五)(进程控制)

    目录 一、进程创建 1、fork函数创建进程 2、fork函数的返回值 3、fork常规用法 4、fork调用失败的原因 二、进程终止 1、进程终止的方式 2、进程退出码 3、进程的退出方法 三、进程等待 1、进程等待的必要性 2、wait函数 3、waitpid函数 四、进程程序替换 1、概念 2、原理 3、进程替

    2024年02月04日
    浏览(37)
  • Linux进程控制【进程程序替换】

    ✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes from experience, and a lot of that comes from bad judgment. 好的判断力来自经验,其中很多来自糟糕的判断力。 子进程 在被创建后,共享的是 父进程 的代码

    2024年01月17日
    浏览(62)
  • Linux - 进程控制(下篇)- 进程等待

     为什么进程需要等待?  我们知道,在Linux 当中, 父子进程之间一些结构 就是一些 多叉树 的结构,一个父进程可能管理或者创建了很多个字进程。 而其实我们在代码当中使用fork()函数创建的子进程的父进程,这个父进程其实也是其他的父进程的子进程,我们在命令行

    2024年02月05日
    浏览(40)
  • [Linux 进程控制(二)] 进程程序替换

    首先,我们要认识到,我们之前fork()所创建的子进程,执行的代码,都是父进程的一部分(用if-else分流或者执行同样的代码)! 如果我们想让子进程执行新的程序呢? 执行全新的代码和访问全新的数据,不再和父进程有瓜葛,这种技术就叫做程序替换 ,下面我们就来学习一

    2024年03月14日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包