0

0

Linux-进程控制

爱谁谁

爱谁谁

发布时间:2025-07-20 13:06:10

|

252人浏览过

|

来源于php中文网

原创

前言:

  进程控制不仅仅是管理程序的执行顺序,还涉及到资源的分配等问题,那么话不多说,开始我们今天的话题!

?进程退出函数✈️exit函数

  上次我们说到,进程退出时,都会返回一个退出码,用来表示进程退出的状态,而在更前面,我们曾经说过exit函数用来退出进程:

代码语言:javascript代码运行次数:0运行复制
  1 #include  2 #include  3 #include  4   5 int main()  6 {  7     while(1)  8     {  9         printf("I'm a process, pid=%d\n", getpid()); 10         sleep(1); 11  12         exit(3); 13     } 14     return 0; 15 }
Linux-进程控制

  进程退出函数exit,函数参数可作为进程退出状态:

Linux-进程控制

eixt:退出进程 status:进程退出状态退出码。

✈️_exit函数
Linux-进程控制

  这是man手册里的_exit(),我们通过下面这段代码测试一下他们功能有何异同:

代码语言:javascript代码运行次数:0运行复制
#include#include#include#include#includevoid Print(){    printf("hello Print\n");    _exit(4);}int main(){    while(1)    {        printf("I am a process: %d\n", getpid());        sleep(1);        Print(1);        //exit(3);放出另外一个_exit,反之则放出exit关闭_exit    }    return 0;}
Linux-进程控制

  这是注释掉了exit(),调用Print函数的_exit()的结果。

Linux-进程控制

  这是注释掉Print函数内的_exit,使用exit()函数返回的结果。

  从此看来我们并没有发现什么不同之处,返回的错误码也没问题。他们的区别可以通过一下代码分析:

代码语言:javascript代码运行次数:0运行复制
#include#include#include#include#includeint main(){    printf("hello guy\n");    sleep(1);    exit(1);    return 0;}
Linux-进程控制

  exit函数返回的结果符合我们的预期,如果把打印的\n删除:

Linux-进程控制

  也没什么问题,但是如果换成_exit函数:

Linux-进程控制

  从这里可以看到,当_exit函数遇到像printf打印却没有换行符的时候,就不能正确打印出自己想要的数据。

区别:   exit是用来终止进程的,exit(退出码),在我们进程的任何地方调用exit() 都表示进程退出,而_exit与exit的区别就是,exit() 会刷新缓冲区(以后会详谈)。

  实际上,_exit()函数是Linux下的一种系统调用,为什么要存在exit() 和 _exit() 两个不同的接口呢?exit() 函数是语言层面的封装,使用这种封装的好处就是语言具有 跨平台、可移植性。

  不同系统、平台可能给你提供的系统调用不相同,但是在语言层的封装却都是相同的,所以 C语言具有可移植性、跨平台性。


?进程等待

  我们之前说过,如果一个进程变僵尸那么使用kill -9也无能为力,造成僵尸的原因就是子进程退出了,父进程并没有将资源回收所导致的,所以系统提供了一些系统调用,通过父进程等待的方式获取子进程退出信息。

✈️wait接口

  在Linux中,为了防止进程变僵尸,系统系统了这样一个接口 wait():

Linux-进程控制

  wait接口是用来回收子进程资源的一个接口,我们看到wait接口的参数是一个指针,这其实是一个 输出型参数 ,我们现将其设置为 NULL。

接口功能:

  我们通过代码来认识一下它的功能:

代码语言:javascript代码运行次数:0运行复制
#include#include #include#include#includeint main(){    pid_t id = fork();    if(id == 0)    {        //child        int cnt = 5;        while(cnt)        {            printf("child is running, pid: %d, ppid: %d\n", getpid(), getppid());            sleep(1);            cnt--;        }        printf("child prepare for exit, will be ZM!\n");        exit(0);    }    printf("father is sleep...\n");    sleep(10);    printf("father start recycle\n");    //father    pid_t rid = wait(NULL);    if(rid > 0)    {        printf("wait sucess, rid: %d\n", rid);    }    printf("father get source sucess!\n");    sleep(3);    return 0;}
Linux-进程控制

  图中画的红色框框表示僵尸了一段时间,而当父进程开始回收之后,子进程的僵尸状态就没有了。

  由此我们可以间接得出,fork之后,父进程一定要最后退出!


✈️waitpid接口

  Linux也提供了wait方式来获取子进程退出信息的接口 waitpid():

Linux-进程控制

  其中waitpid返回值与wait的返回值含义相同,第一个参数的pid有很多种表示方法,但是常见的表示有这两种:

pid参数意义:

  status同样是一个输出型参数。options有两种传值,一种是0,一种是非阻塞,我们目前将其设置为0表示阻塞状态。

  通过下面代码感受getpid():

代码语言:javascript代码运行次数:0运行复制
#include#include #include#include#includeint main(){    pid_t id = fork();    if(id == 0)    {        //child        int cnt = 5;        while(cnt)        {            printf("child is running, pid: %d, ppid: %d\n", getpid(), getppid());            sleep(1);            cnt--;        }        exit(1);    }    //father    int status = 0;    pid_t rid = waitpid(id, &status, 0);//阻塞等待    if(rid > 0)    {        printf("wait sucess, rid: %d  status: %d\n", rid, status);//查看rid和status    }    return 0;}
Linux-进程控制

  我们给status的初始值为0,但是这里却变成了256。这里的status并不是指单纯的整数,与进程的 退出码 和 退出信号 有关,而其存储形式类似于位图的存储:

Linux-进程控制在这里插入图片描述

  我们只看比特位的后十六位:

Linux-进程控制

  中间有一个名为core dump的比特位我们在信号部分在详谈。

  所以我们status为什么是256其实就很简单了:

✈️导出错误码

  上面说了status的最后16位比特位有效,并且这十六位由退出码和信号编号所组成,所以我们可以使用位运算的方式将退出码和退出信号提取出来:

代码语言:javascript代码运行次数:0运行复制
#include#include #include#include#includeint main(){    pid_t id = fork();    if(id == 0)    {        //child        int cnt = 50;        while(cnt)        {            printf("child is running, pid: %d, ppid: %d\n", getpid(), getppid());            cnt--;            sleep(1);        }        exit(0);    }    //father    int status = 0;    pid_t rid = waitpid(id, &status, 0);    if(rid > 0)    {      //status & 0x7F: 0000 0000 0000 0000 0000 0000 0111 1111        printf("wait sucess, rid: %d  status: %d  exit signo%d  exit code: %d\n", rid, status, status&0x7F, (status>>8)&0xFF);    }    return 0;}
Linux-进程控制

  使用了kill -9得出的退出信号就是9,进程被杀死退出码为0。如果在代码中出现了逻辑错误,比如除零错误:

Linux-进程控制

  还有类似空指针等情况,有兴趣可以自己尝试,这里就不在试了。

html5 svg滑块控制圆形进度条代码
html5 svg滑块控制圆形进度条代码

一款html5 svg滑块控制圆形进度条代码

下载

  实际上,Linux给我们提供了两个常见的宏定义:

  有了这些宏,我们就不用那么麻烦直接使用位操作了,我们可以直接使用对应的宏:

代码语言:javascript代码运行次数:0运行复制
#include#include #include#include#includeint main(){    pid_t id = fork();    if(id == 0)    {        //child        int cnt = 5;        while(cnt)        {            printf("child is running, pid: %d, ppid: %d\n", getpid(), getppid());            cnt--;            sleep(1);        }        exit(123);    }    //father    int status = 0;    pid_t rid = waitpid(id, &status, 0);    if(rid > 0)    {      //status & 0x7F: 0000 0000 0000 0000 0000 0000 0111 1111       // printf("wait sucess, rid: %d  status: %d  exit signo%d  exit code: %d\n", rid, status, status&0x7F, (status>>8)&0xFF);        if(WIFEXITED(status))//使用宏定义        {             printf("wait sucess, rid: %d  status: %d  exit code: %d\n", rid, status, WEXITSTATUS(status));        }        else         {            printf("child process error!\n");        }    }    return 0;}
Linux-进程控制

  源码中的status:

Linux-进程控制
✈️阻塞与非阻塞

  我们前面代码所采用的等待方式均为阻塞等待,即在回收子进程的资源之前,父进程什么事情可不干,可以用grep 查询:

代码语言:javascript代码运行次数:0运行复制
ps ajx | grep -i mybin#查询当前进程状态
Linux-进程控制

  既然有阻塞,也必然有非阻塞:

表1 阻塞与非阻塞区别

阻塞

非阻塞

方式

当waitpid函数以阻塞模式调用时,父进程等待子进程退出,这期间父进程不做任何事情

当waitpid函数以非阻塞调用时,父进程以轮询的方式每段时间检测子进程是否退出,没退出就返回做父进程的事情

参数

0

WNOHANG

  我们以下面这段代码来理解非阻塞:

代码语言:javascript代码运行次数:0运行复制
#include#include #include#include#includeint main(){    pid_t id = fork();    if(id == 0)    {        //child        int cnt = 5;        while(cnt)        {            printf("child is running, pid: %d, ppid: %d\n", getpid(), getppid());            cnt--;            sleep(1);        }        exit(123);    }    //father    int status = 0;    while(1)    {        pid_t rid = waitpid(id, &status, WNOHANG);//非阻塞调用        if(rid > 0)        {            printf("wait sucess, rid: %d, status: %d, exit code: %d\n", rid, status, WEXITSTATUS(status));            break;         }        else if(rid == 0)        {            printf("father say: child is running, do other things\n");        }        else         {            perror("waitpid");            break;        }        sleep(1);    }    return 0;}
Linux-进程控制

  父进程在等待子进程资源回收的时候自己也在不断地执行,最后也可以成功回收子进程。

  为了更直观看到现象,我们不妨模拟进程等待的环境:

代码语言:javascript代码运行次数:0运行复制
#include#include #include#include#include#define NUM 5typedef void(*fun_t)();fun_t tasks[NUM];void printLog(){    printf("this is a log print task\n");}void printNet(){    printf("this is  a net\n");}void printNPC(){    printf("this is a flush NPC\n");}void initTask(){    tasks[0] = printLog;    tasks[1] = printNet;    tasks[2] = printNPC;    tasks[3] = NULL;}void executeTask(){    for(int i = 0; tasks[i]; ++i) tasks[i]();}int main(){    initTask();    pid_t id = fork();    if(id == 0)    {        //child        int cnt = 5;        while(cnt)        {            printf("child is running, pid: %d, ppid: %d\n", getpid(), getppid());            cnt--;            sleep(1);        }        exit(123);    }    //father    int status = 0;    while(1)    {        pid_t rid = waitpid(id, &status, WNOHANG);        if(rid > 0)        {            printf("wait sucess, rid: %d, status: %d, exit code: %d\n", rid, status, WEXITSTATUS(status));            break;         }        else if(rid == 0)        {            printf("father say: child is running, do other things\n");            printf("##############task begin############\n");            executeTask();            printf("##############task end##############\n");        }        else         {            perror("waitpid");            break;        }        sleep(1);    }    return 0;}

?✏️总结 在Linux下,进程退出提供了两个接口,exit() 和 _exit(),他们的区别就是 _exit()函数没有刷新缓冲区这一功能。 一个创建子进程的父进程有必要进行进程等待,用来防止进程变为僵尸进程从而危害系统。其中进程等待有两个接口 wait() 和 waitpid()。wait和waitpid:

区别

wait

waitpid

参数

*status

id, *status, options

状态

等待 任意 子进程退出,并返回终止子进程id

等待 指定 子进程退出,并返回终止子进程id

阻塞

无(默认阻塞调用)

0 和 WNOHANG(用来支持阻塞非阻塞的宏)

 根据你所期望进程执行的行为从而选择对应的接口。

创作不易,如果对您有帮助的话,还望留下一个小小的赞~~

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

389

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

613

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

353

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

256

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

597

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

524

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

639

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

599

2023.09.22

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Django 教程
Django 教程

共28课时 | 3.2万人学习

C# 教程
C# 教程

共94课时 | 6.9万人学习

Java 教程
Java 教程

共578课时 | 47.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号