【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
相关文章
|
1月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
35 0
|
3月前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
638 2
|
3月前
|
Linux Python
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
67 2
|
15天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
46 4
linux进程管理万字详解!!!
|
5天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
42 8
|
14天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
47 4
|
15天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
17天前
|
消息中间件 存储 Linux
|
23天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
25 1
|
1月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
22 1