Linux进程控制


一、进程终止

1.2 进程退出码

我们常见的main函数的返回值就是进程退出码,退出码包含进程退出的信息

退出场景

代码运行完毕,结果正确 ,返回0(退出码)
代码运行完毕,结果不正确,返回非0(退出码)
代码异常终止,返回非0的未知退出码

1.3 进程常见退出场景

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

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

exit和_exit的参数都是退出码,但两者退出方式有所不同。

  • exit在任意地方调用,都代表终止进程,参数是退出码!会进行收尾工作,比如执行用户的清理函数、刷新缓冲区、关闭流等。

  • _出口终止进程,强制终止进程,不要进行进程的后续收尾工作

异常退出:

  1. ctrl + c,信号终止
  2. 程序出错

查看最近一次进程退出的退出码:echo $?
在这里插入图片描述
每一个退出码对应一种退出信息,但已知错误也是有限的,我们可以通过strerror()函数查看每个退出码对应的信息,可以看到Linux下退出对应的有效退出码只到133.
在这里插入图片描述
在这里插入图片描述

那异常退出,返回的错误码是多少呢?
我们可以给程序弄一些运行错误,比如除0,使用野指针等
在这里插入图片描述
可以看到退出是139,没有对应的退出信息。
在这里插入图片描述

二、进程等待

2.1 进程等待必要性

  1. 通过获取子进程退出的信息,能够得知子进程执行结果。
  2. 可以保证:时序问题,子进程先退出,父进程后退出
  3. 进程退出的时候会先进入僵尸状态,会造成内存泄漏的问题,需要通过父进程wait,释放该子进程占用的资源!

2.2 进程等待的方法

在这里插入图片描述

2.3 等待函数wait

wait函数的使用
其实很简单,只需要根据返回值执行结果。
返回值:

  • 成功返回被等待进程pid,失败返回-1。

参数:

  • 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
#include<stdio.h>                                
#include<stdlib.h>    
#include<unistd.h>    
#include<sys/wait.h>    
#include<sys/types.h>    
    
int main()    
{    
    
  pid_t id = fork();//创建一个子进程    
  if(id ==  0)    
  {    
    //创建成功,让子进程做特定的事情    
    int cnt = 5;    
    while(cnt--)    
    {    
      printf("child[%d] running: %d\n",getpid(),cnt);        
      sleep(0);    
    } 
    }
    exit(1);//做完自己的事情直接退出
  }
  printf("father start waitting\n");
  pid_t ret = wait(NULL);//status先置为空
  if(ret > 0)
  {
    //等待成功
    printf("waitting successful:%d\n",ret);
  }
  else 
  {                                              
    printf("waitting fail: %d\n",ret);
  }
  return 0;
}

在这里插入图片描述
关于wait的参数status下一个函数介绍

2.4 等待函数waitpid

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:

  • 0 : 默认行为,阻塞等待
  • WNOHANG: 非阻塞等,若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

2.4.1 参数status

从前面可以知道一个程序的退出大致可分为两种

程序正常结束,有退出码,返回退出码
程序异常结束,没有退出码,返回异常信号

既然父进程等待子进程结束,获取子进程退出信息,那么就需要拿到向对应的退出码或者异常信号。status参数就是用来获取对应信息的,那返回的信息父进程怎么解析呢?

status是一个输出型参数传入的是整型指针,整型有32个比特位,低16个比特位存放退出信息,如下图所示

在这里插入图片描述

那么怎么获取两种信息呢?
假设父进程传参为:
在这里插入图片描述

获取退出码:只需让它右移8位,然后与上低8位全1的数即0xff
获取异常信号:不用移位,直接与上低7位是1的数即可,即0x7f

在这里插入图片描述

程序正常结束
在这里插入图片描述

进程异常结束
在这里插入图片描述

当然也不用每次都这么麻烦
系统给了两个宏定义,可以直接使用

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

2.4.2 参数options

  • 阻塞的本质:其实是进程的PCB被放入了等待队列,并将进程的状态改为S状态
  • 返回的本质:进程的PCB从等待队列拿到R(运行)队列,从而被CPU调度

阻塞状态就是父进程啥也不干,等着子进程
非阻塞状态就是父进程在等子进程的时候,自己去做其他事,做完或者做完一部分,再去询问子进程是否结束,如果子进程结束,那么父进程等待过程结束,否则父进程接着做自己的事。


#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>                                                                                           

int main()
{

  pid_t id = fork();//创建一个子进程
  if(id ==  0)
  {
    //创建成功,让子进程做特定的事情
    int cnt = 5;
  //  int i = cnt/0;
    while(cnt--)
    {
      printf("child[%d] running: %d\n",getpid(),cnt);
      sleep(1);
    }
    exit(1);//做完自己的事情直接退出
  }
  //等待10秒不回收子进程
  //sleep(10);
  printf("father start waitting\n");
  int status = 0;
  while(1)
  {
    pid_t ret = waitpid(id,&status,WNOHANG);
    if(ret == 0)
    {
      //子进程还没有结束
      printf("Do father thing\n");
      sleep(1);//1秒一次 
    }
    else if(ret > 0)
    {
      if(WIFEXITED(status))
      {
        //正常退出
        printf("exit code: %d\n",WEXITSTATUS(status));
      }
      else 
      {
        printf("error, get a signal!\n");
      }
      break;//退出循环
    }
    else 
    {                                                                                                           
      printf("fail\n");
      break;
    }
  }
}

在这里插入图片描述

三、进程替换

3.1 替换原理

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

3.2 替换函数

C语言库里面的相关替换函数
在这里插入图片描述
函数解释:

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

其实它们本质都在调用一个系统接口,只是经过了封装而已。
在这里插入图片描述
命名解释:

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

带 l 的表示,命令行参数以列表传入,如execl、execlp、execle
在这里插入图片描述
带v的表示将命令行参数以数组的形式传入

在这里插入图片描述

带p的表示自动传入环境变量
execlp 使用环境变量,可以直接使用ls,就能找到对应的地址
在这里插入图片描述
带e的表示传入自定义的环境变量给要替换的程序
传入自己的写的环境变量,当然真实的环境环境变量不会这么随意
在这里插入图片描述
在test1.c文件里面打印环境变量,若不用程序替换执行该程序,就会打印全局的环境变量。程序替换传入自己的环境变量后就会打印传入的,入下图:
在这里插入图片描述

在这里插入图片描述
这几个函数就是这四种情况的组合,看参数使用即可。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s_persist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值