【Linux进程篇】Linux中的等待机制与替换策略

W...Y的主页 😊

代码仓库分享💕 

目录

​编辑

进程等待

进程等待必要性

进程等待的方法

wait方法

waitpid方法

获取子进程status

 阻塞与非阻塞

进程程序替换

替换原理

替换函数


进程等待

进程等待必要性

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

上篇博客中,我们讲到进程退出时有两个非常重要的信息:退出信号和退出码。当我们进程退出时退出信息会被放入进程的PCB中进行保存,等待父进程的回收。

因为进程拥有独立性,所以我们想通过参数或返回值将信息转交给父进程那是不可能的,所以我们才要进行回收资源。

进程等待的方法

wait方法

返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

下面是测试子进程变僵尸后wait是否进行回收资源:

 

我们可以看出父进程可以将僵尸进程回收!!! 

waitpid方法

返回值:
当正常返回的时候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。

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

int 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);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0); // 阻塞等待
    if(rid > 0)
    {
        printf("wait success, rid: %d, status: %d\n", rid, status);
    }

}

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

获取子进程status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位): 

当我们的子进程exit(1)时,子进程的退出状态status为256。256代表什么呢?

任何进程最终执行状态我们可以使用两个数字具体表明情况

 我们为了直观一点,可以使用位操作,将退出信号和退出码分别打印出来:

printf("wait success, rid: %d, status: %d, exit signo: %d, exit code: %d\n", rid, status, status&0x7F, (status >> 8)&0xFF);

我们也可以使用宏来获取退出信号与退出码:

int 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);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0); // 阻塞等待
    if(WIFEXITED(status))
    {
        printf("wait success, rid: %d, status: %d, exit code: %d\n", rid, status, WEXITSTATUS(status));
    }

}

 阻塞与非阻塞

在waitpid函数中,我们发现在上面的代码中,我们一直默认第三个参数为0。其就是对应的阻塞状态。而WNOHANG这个宏在第三个参数中可以进行填入,代表非阻塞等待。

阻塞等待时,父进程不能做任何事情,只能等待子进程变成僵尸进程后进行回收。而非阻塞等待可以轮转时进行行为。

非阻塞等待代码:

int 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);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, WNOHANG); 
    while(1)
    {
        if(rid > 0)
        {
            printf("wait success, 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 thing\n");
        }
        else
        {
            perror("waitpid");
            break;
        }
    }
	return 0;
}

进程程序替换

替换原理

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

当我们要进行替换时,新程序的数据和代码会将原来物理内存中的数据段和代码段进行替换。其实程序替换工作本质就是加载!!! 

替换函数

其实有六种以exec开头的函数,统称exec函数:

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

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

这些函数原型看起来很容易混,但只要掌握了规律就很好记。 

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

我们强调一下结尾带e的exec*的函数,其就是表示自己维护环境变量。我们想要子进程全部继承父进程的全部环境变量直接可以。如果单纯再父进程的环境变量中添加一些环境变量可以使用putenv函数。但是我们子进程如果想要自己拥有一个全新的环境变量,我们可以使用exec*函数中后面带e的,他会将父进程继承的环境变量覆盖掉!!! 

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

int main()
{
    char *const env[] ={
        (char*)"haha=hehe",
        (char*)"PATH=/",
        NULL
    };
    printf("I am a process, pid: %d\n", getpid());
    //putenv("MYVAL=bbbbbbbbbbbbbbbbbbbbbbbbbbbb");
    pid_t id = fork();
    if(id == 0)
    {
        //extern char**environ;
        sleep(1);
        //execle("./mytest", "mytest", NULL, environ); // 我们传递环境变量表了吗??no. 子进程默认就拿到了.他是怎么做到的?
        execle("./mytest", "mytest", NULL, env); // 我们传递环境变量表了吗??no. 子进程默认就拿到了.他是怎么做到的?
        //execl("/usr/bin/python3", "python3", "test.py", NULL);
        //execl("/usr/bin/bash", "bash", "test.sh", NULL);
        //execl("./mytest", "mytest", NULL); // 我们传递环境变量表了吗??no. 子进程默认就拿到了.他是怎么做到的?
        //char *const argv[] = {
        //    (char*)"ls",
        //    (char*)"-a",
        //    (char*)"-l"
        //};
        //sleep(3);
        //printf("exec begin...\n");
        //execvp("ls", argv);
        //execv("/usr/bin/ls", argv);
        //execl("/usr/bin/ls", "ls", "-a", "-l", NULL); //NULL 不是 "NULL"
        //execlp("ls", "ls", "-a", "-l", NULL); //NULL 不是 "NULL"
        //execl("/usr/bin/top", "/usr/bin/top", NULL); //NULL 不是 "NULL"
        printf("exec end ...\n");
        exit(1);
    }

    pid_t rid = waitpid(id, NULL, 0);
    if(rid > 0)
    {
        printf("wait success\n");
    }
    
    exit(1);
}

 

exec调用举例如下:

#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}

 事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

总结:

细节1:程序替换一旦成功,exec*后续代码不再执行,因为被替换掉了。

细节2:exec*只有失败有返回值,没有成功返回值。

细节3:替换完成,不再创建新的程序。

细节4:创建一个进程先创建PCB、地址空间、页表,再将程序加载到内存中。 

这些函数功能上没有任何区别,区别就在于传递的参数不同! 


以上就是全部内容,感谢大家观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/693355.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【教学类-40-01】20240607类似MJ的免费AI绘画工具——文心一格与通义万相

背景需求&#xff1a; 风变的AI对话大师一年到期了&#xff0c;也没有看到续费的按钮。不能使用它写代码了。 MJ早就用完了&#xff0c;最后480次&#xff0c;我担心信息课题会用到它生图&#xff0c;所以不敢用。 最近探索其他类似MJ的免费出图工具 一、文心一格&#xff08;…

C# E2Pose人体关键点检测(OpenVINO推理)

C# E2Pose人体关键点检测(OpenVINO推理) 目录 效果 模型信息 项目 代码 下载 效果 模型信息 Inputs ------------------------- name&#xff1a;inputimg tensor&#xff1a;Float[1, 3, 512, 512] --------------------------------------------------------------- Ou…

实验六、IPv4 地址的子网划分,第 2 部分《计算机网络》

你有没有发现&#xff0c;困的时候真的清醒不了。 目录 一、实验目的 二、实验内容 三、实验小结 一、实验目的 完成本练习之后&#xff0c;您应该能够确定给定 IP 地址和子网掩码的子网信息。 知道 IP 地址、网络掩码和子网掩码后&#xff0c;您应该能够确定有关该 IP 地…

二、Nginx原来是这样?(系列篇02)

二、Nginx原来是这样&#xff1f;&#xff08;系列篇02&#xff09; 大家好&#xff0c;我是秋意零。 今天分享Nginx系列篇的第二节。Nginx目录结构、运行原理、基本配置。 更多请关注&#xff0c;Nginx系列篇主页&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__b…

STM32 uc/OS-III多任务程序

目录 一、项目创建 二、代码移植 1、uC/OS-III源码处理 2、KEIL文件配置 ​编辑3、文件修改 启动文件 ​编辑app_cfg.h includes.h bsp.c和bsp.h main.c lib_ cfg.h app.c和app.h 三、总结 学习目标&#xff1a; 学习嵌入式实时操作系统&#xff08;RTOS&#xf…

找素数第二、三种方法

文章目录 第一种 &#xff1a;使用标签第二种&#xff1a;本质是方法的分装 第一种 &#xff1a;使用标签 没有使用信号量。break和continue作用范围只是最近的循环&#xff0c;无法控制外部循环。 此时使用标签 对外部循环进行操作。 package com.zhang; /* 找素数 第二种方…

小白教程--- kali(po解)WIFI密码 (图文教程)

kali学得好&#xff0c;牢饭少不了&#xff01;&#xff01;&#xff01; 原理&#xff1a; 模拟WiFi的已连接设备&#xff0c;强制让其下线重连&#xff0c;获取其握手包&#xff0c;使用密码字典&#xff08;宝丽&#xff09;婆洁。 环境&#xff08;准备工作&#xff09;&a…

计网仿真综合实验 实验十二

实验十二 综合网络实验 实验过程 IP配置说明参考连线配置OSPF使公司内部联通 路由器R1的OSPF配置路由器R2的OSPF配置路由器R3的OSPF配置R1、R2、R3的相关解释路由器R4的OSPF配置路由器R5的OSPF配置路由器R6的OSPF配置R4、R5、R6解释: 路由器R2的RIP配置路由器R7的RIP配置 总结 …

Unity DOTS技术(十五) 物理系统

要解决性能的瓶颈问题,在DOTS中我们将不再使用Unity自带的物理组件. 下面来分享一下在DOTS中当如何使用物理插件. 一.导入插件 在使用DOTS系创建的实体我们会发现,游戏物体无法受物理系统影响进行运动.于是我们需要添加物理系统插件. 1.打开Package Manager > 搜索插件Uni…

IT人的拖延——都是“分心”惹的祸?

典型表现 我们说到拖延的原因有很多&#xff0c;还有一个原因是因为“分心太多“造成的&#xff0c;分心太多的拖延大致上有以下表现&#xff1a; 无法集中注意力&#xff1a; 分心太多会导致我们无法集中注意力在当前的工作任务上&#xff0c;我们可能会经常性地走神或者在工…

你好GPT-4o——对GPT-4o发布的思考与看法

你好GPT-4o 前言 2024年5月13日&#xff0c;OpenAI官网发布了他们的新一代自然语言处理交互系统——GPT-4o。这是OpenAI继GPT4之后又一个新的旗舰模型。 GPT-4o&#xff08;“o”代表“omni”&#xff09;是迈向更自然的人机交互的一步——它接受文本、音频、图像和视频的任意…

Linux环境---在线安装MYSQL数据库

Linux环境—在线安装MYSQL数据库 一、使用步骤 1.安装环境 Mysql 驱动 8.0 需要 jdk1.8 才行。 JDK版本&#xff1a;1.8 参考文档 MYSQL版本&#xff1a;8.0.2 下载链接: https://pan.baidu.com/s/1MwXIilSL6EY3OuS7WtpySA?pwdg263 操作系统&#xff1a;CentOS 1.1 建立存…

Golang | Leetcode Golang题解之第133题克隆图

题目&#xff1a; 题解&#xff1a; func cloneGraph(node *Node) *Node {if node nil {return node}visited : map[*Node]*Node{}// 将题目给定的节点添加到队列queue : []*Node{node}// 克隆第一个节点并存储到哈希表中visited[node] &Node{node.Val, []*Node{}}// 广…

数据结构严蔚敏版精简版-栈和队列以及c语言代码实现

1栈的定义和特权 栈(stack)是限定仅在表尾进行插入或删除操作的线性表。 注&#xff1a;虽然说栈的实现就是一端插入和删除&#xff0c;但不一定是在“表尾”&#xff0c;这个“表尾”是广义的。 头插法实现链栈 尾插法实现链栈 因此&#xff0c;对栈来说&#xff0c;表尾…

从GAN到WGAN(01/2)

从GAN到WGAN 文章目录 一、说明二、Kullback-Leibler 和 Jensen-Shannon 背离三、生成对抗网络 &#xff08;GAN&#xff09;四、D 的最优值是多少&#xff1f;五、什么是全局最优&#xff1f;六、损失函数代表什么&#xff1f;七、GAN中的问题 一、说明 生成对抗网络 &#…

13_前端工程化_ES6

1.前端工程化概念 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本。 前后端分离&#xff08;前端代码工程化独立出来形成一个单独的app&#xff09; 1.开发分离 2.部署分离 3.服务器分离…

012-Linux逻辑卷管理(LVM)

前言 安装 Linux 操作系统时遇到的⼀个常见的难以决定的问题就是如何正确地评估各分区大小&#xff0c;以分配合适的硬盘空间; 基本的磁盘分区管理方式在逻辑分区划分好之后就无法改变其大小。随着 Linux的逻辑卷管理功能的出现&#xff0c;这些问题都迎刃而解&#xff0c;用户…

如何计算 GPT 的 Tokens 数量?

基本介绍 随着人工智能大模型技术的迅速发展&#xff0c;一种创新的计费模式正在逐渐普及&#xff0c;即以“令牌”&#xff08;Token&#xff09;作为衡量使用成本的单位。那么&#xff0c;究竟什么是Token呢&#xff1f; Token 是一种将自然语言文本转化为计算机可以理解的…

论文阅读:All-In-One Image Restoration for Unknown Corruption

发表时间&#xff1a;2022 cvpr 论文地址&#xff1a;https://openaccess.thecvf.com/content/CVPR2022/papers/Li_All-in-One_Image_Restoration_for_Unknown_Corruption_CVPR_2022_paper.pdf 项目地址&#xff1a;https://github.com/XLearning-SCU/2022-CVPR-AirNet 在本文…

Word Split Line

Word Split Line 分割线 https://download.csdn.net/download/spencer_tseng/89413772