【Linux】进程篇Ⅱ:进程开始、进程终止、进程等待、程序替换

news2025/1/21 6:32:34

文章目录

  • 五、fork 函数,创建进程
      • 写时拷贝
  • 六、进程终止
      • 1. 退出码
      • 2. 如何终止程序
  • 七、进程等待
      • 1. 概念
      • 2. wait 函数
        • waitpid 函数 🔺
      • 3. 阻塞等待
  • 八、程序替换
      • 1. execl
      • 2. execv
      • 3. execlp
      • 4. execvp
      • 5. execle 🔺
      • 6. execvpe 、execve
  • 一个简易的 shell


五、fork 函数,创建进程


#include <unistd.h>

pid_t fork(void);

返回值:

  • 子进程 返回 0
  • 父进程 返回 子进程 id
  • 出错 返回 -1

进程调用 fork,当控制转移到内核中的 fork 代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

fork 之前,父进程独立执行,
fork 之后,父子两个执行流分别执行 fork 后面的代码。

注意,fork之后,谁先执行完全由调度器决定。

写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副
本。具体见下图:

在这里插入图片描述

fork常规用法

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

fork调用失败的原因

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制

六、进程终止


如何理解进程退出?

OS内少了一个进程,OS就要释放进程对应的内核数据结构 + 代码和数据(如果有独立的)

进程结束情况分类:

a. 执行完了,正常退出

  • 结果正确
  • 结果不正确

b. 崩溃了,进程异常 (通过 信号 的方式,后面更新)

  • 崩溃的本质: 进程因为某些原因,导致进程收到了来自操作系统的信号 kill -9

1. 退出码

我们说,进程是有退出码的。

比如 main 函数中的 return x; 正常执行完了

  • 结果正确,退出码为 0,
  • 结果不正确, 1、2、3、4 则表示不同的原因(供用户进行进程退出健康状态的判定)

echo $? 可以 查看 最近执行的进程的 退出码

echo $?

2. 如何终止程序

正常终止有三种方式:

① main 函数 return。

  • 其他函数return,仅仅代表该函数返回
  • 进程执行,本质是main执行流执行!

void exit(int status) 函数退出

  • 头文件 #include <stdlib.h>
  • status 代表就是进程的退出码
  • 等价于 main return XXX
  • 会刷新缓冲区、关闭流等
  • 其实 exit 函数 也是封装调用的 _exit 函数

void _exit(int status) 函数退出

  • 头文件 #include <stdlib.h>
  • status 代表就是进程的退出码
  • 直接退出程序,不处理缓冲区等

在这里插入图片描述

由此我们可以推测出的就是:
	缓冲区,一定不在 OS 内,不然肯定都能刷新了,事实上并没有
这是因为用户层和 OS 之间,还有一个 C库。(后面更新)

异常退出

  • ctrl + C 信号终止(信号!!后面更新)

七、进程等待


1. 概念

进程等待 就是 通过系统调用,获取 子进程 退出码 或者 退出信号 的方式,顺便释放内存问题。

进程等待的作用:

  • 避免内存泄漏

  • 获取子进程执行的结果(如果必要)

    • 对于进程退出的结果查验,我们通过的还是上述提过的 信号 + 退出码 的方案。

等待,实际上就是 父进程 对 子进程 的等待

2. wait 函数

pid_t wait(int *status)

头文件: #include <sys/wait.h>
返回值:等待成功则返回 子进程 的 PID

waitpid 函数 🔺

pid_t waitpid(pid_t pid, int *status, int options);

头文件 #include <sys/wait.h>

返回值:

  • > 0,success(返回的是对应子进程的 pid)
  • ==-1,failed(一般不会失败,pid 传错了会导致失败)
    == 0,在选项设置了 WNOHANG 的情况下,发现没有已退出的子进程可收集

参数 pid

  • >0,表示等待指定的子进程
  • ==-1,表示等待任一个子进程,与 wait 等效

参数 status:是一个输出型参数

  • 我们会想得到,子进程的退出状态,即 信号 + 返回码
    • 如何用一个整数 返回两个整数…?实际上这里的 status 是类似位图作用的。信号和返回码都不超过一定值,用一个整数位存储足够啦!(见下文)
  • WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status):若 WIFEXITED 非零,提取子进程退出码。(查看进程的退出码)

参数 options

  • 0,默认父进程进行 阻塞等待
  • WNOHANG,父进程进行 非阻塞轮询

🌰使用举例:

int status = 0;
pid_t ret_id = waitpid(id, &status, WNOHANG);
if(ret_id < 0)
{
    printf("waitpid error!\n");
    exit(1);
}
else if(ret_id == 0)	// 子进程还没结束
{
    RunTask();
    sleep(1);
    continue;
}
else					// 子进程结束了
{
    if(WIFEXITED(status))	// 正常退出
    {
        printf("wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    else					// 异常退出
    {
        printf("wait success, child exit signal: %d\n", status & 0x7F);
    }
    break;
}

status 记录 信号 和 退出码 的方式:
00000000 00000000 00000000 00000000

  • 高 16 位不需要
  • 低 16 位中,次低的 8 位(黑体比特位),储存 退出状态
  • 最低 7 位(斜体比特位),储存 终止信号
  • 第8位,是 core dump 标志(后面更新,信号篇)

想要自己访问 信号 和 退出码,我们用如下方式:

// 退出码
(status>>8) & 0xFF;
// 终止信号
 status & 0x7F;

所以,

父进程 如何拿到 子进程 的退出码和终止信号的呢?

实际上,进程的 PCB 中,就有这两个成员值。

struct task_struct
{
	// ...
	int exit_code;
	int exit_signal;
	
	task_struct* parent;
};
  • 当子进程执行完毕时 main 函数的退出码,写入 PCB 的 exit_code 里
  • 如果出异常,OS 会把遇到异常时的信号编号写到 PCB 的 exit_signal 里
  • 进程退出之后,OS 会将进程 PCB 维护起来,通过系统调用接口 waitpid / wait,就可以让用户拿到这些数据。

3. 阻塞等待

父进程在 wait 的时候,如果子进程没有退出,父进程只能一直调用 waitpid 进行等待,这就叫 阻塞等待

  • 可以理解为父进程从 R 状态,开始等待,进入 S 状态。
  • 这个等待不在运行队列,可以理解为在一个只有它一个节点的等待队列里等待。
  • 等待的同时,子进程的 CPB 中找到并挂接到父进程,直到子进程结束,父进程再回到运行队列(R 状态)

如果我们不想 阻塞等待,即 不想在 waitpid 处卡住呢?

  • 非阻塞轮询(WNOHANG,waitpid 的最后一个参数)
  • 故,我们在日常这样情况的时候,说 这个程序 夯住了

八、程序替换


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

exec 这一系统接口,也被叫做 加载器

在这里插入图片描述

简单来说:

  • 程序替换 不产生新的进程

  • 程序替换 是 整体替换,不能局部替换

  • 程序替换 只会影响 调用进程,因为 进程具有独立性(写时拷贝)

    • 加载拥有新代码的新进程哦!这说明了,在代码区,也可以发生写时拷贝!!!

接下来是 exec 系列函数,
头文件是#include <unistd.h>

1. execl

记忆:l 代表 list,把参数一个一个列表一样的传给函数。

int execl(const char *path, const char *arg, ...)

返回值:

  • 成功时,发生整体替换,也不会有返回值
  • 失败时,会继续调用后面的代码,此时才有返回值,返回也有意义

参数 path:

  • 带路径的可执行程序

参数包:

  • 在命令行怎么执行这个命令,就以空格为分割,一个一个的传给 execl 就可以了。最后以 NULL 结尾。

ps:对于该函数不用进行返回值判断,只要继续向后运行了,就一定是失败的

🌰使用举例

execl("/bin/ls", "ls", "-a", "-l", NULL);

2. execv

记忆:v 代表 vector,数组作为参数。

int execv(const char *path, char *const argv[])

返回值:

  • 成功时,发生整体替换,也不会有返回值
  • 失败时,会继续调用后面的代码,此时才有返回值,返回也有意义

参数 path:

  • 带路径的可执行程序

参数 argv:

  • 设置一个 char* const argv[] 的指针数组, 在命令行怎么执行这个命令,就以空格为分割,放入数组。最后以 NULL 结尾。

🌰使用举例

char *const myargv[] = { "ls", "-a", "-l", NULL };

execv("/bin/ls", myargv);

3. execlp

记忆:不带 p 的需要指定程序路径,而带 p 的只需要指定程序名即可,系统会自动在环境变量 PATH 中查找。

int execlp(const char *file, const char *arg, ...)

返回值:

  • 成功时,发生整体替换,也不会有返回值
  • 失败时,会继续调用后面的代码,此时才有返回值,返回也有意义

参数 file:

  • 可执行程序名,该程序必须要在环境变量 PATH 中可以被调用

参数包:

  • 在命令行怎么执行这个命令,就以空格为分割,一个一个的传给 execl 就可以了。最后以 NULL 结尾。

🌰使用举例

execlp("ls", "ls", "-a", "-l", NULL);

这里两个一样的参数,不要省略!!!即使有时候省略了也能跑,不要这样写!!

4. execvp

记忆: p,直接给数组名; v,以数组方式传参。

int execvp(const char *file, char *const argv[])

返回值:

  • 成功时,发生整体替换,也不会有返回值
  • 失败时,会继续调用后面的代码,此时才有返回值,返回也有意义

参数 file:

  • 可执行程序名,该程序必须要在环境变量 PATH 中可以被调用

参数 argv:

  • 设置一个 char* const argv[] 的指针数组, 在命令行怎么执行这个命令,就以空格为分割,放入数组。最后以 NULL 结尾。

🌰使用举例

char *const myargv[] = { "ls", "-a", "-l", NULL };

execvp("ls", myargv);

5. execle 🔺

记忆: p,直接给数组名; v,以数组方式传参。

int execvp(const char *path, const char *arg, ..., char *const envp[])

返回值:

  • 成功时,发生整体替换,也不会有返回值
  • 失败时,会继续调用后面的代码,此时才有返回值,返回也有意义

参数 path:

  • 带路径的可执行程序

参数包:

  • 在命令行怎么执行这个命令,就以空格为分割,一个一个的传给 execl 就可以了。最后以 NULL 结尾。

参数 envp:

  • 自定义环境变量,并且是 覆盖式传入

引申另一个函数

int putenv(char *string)

头文件:#include <stdlib.h>

作用:把 参数位置 的字符串作为 环境变量,加入 PATH

🌰使用举例

// 【系统环境变量】
extern char** environ;
/* 自定义环境变量
char *cosnt myenv[] = {
	"MYENV=ThisIsValue",
	NULL
};*/
// 【把自定义变量加入系统环境变量】
putenv("MYENV=ThisIsValue");	// 此时就被添加进 environ 里了

// 要求既传 系统的 又传 自定义的
execle("./otherproc", "otherproc", NULL, environ);

🎯环境变量具有全局性,可以被子进程继承下去。 这句话如何深刻理解?

  • bash 是最父的父进程!
  • 他调用子进程的时候,只需要通过 exec 函数,
  • 传入相应的环境列表,子进程就共享了 bash 的环境变量!!!

实际上,也正是 OS 调用 exec,调用的我们写的 main 函数,main 函数的参数 envp 也就是 OS 传给我们的。

6. execvpe 、execve

不多说喽

int execvpe (const char* file, char *const argv[], char *const envp[])

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

这些 exec 函数,实际上,只有 execve 是系统调用,其余的 6 个都是这个调用的封装


一个简易的 shell


👉🔗链接在此

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vmware的window中安装GNS3

1.向vmware中的windows虚拟机传送文件 点击虚拟机-安装VMwaretools 安装在虚拟机上面 此图标代表已经成功&#xff0c;将文件复制到虚拟机上里面 2.安装 安装gns3&#xff0c;需要先安装winpcap&#xff08;检查网卡&#xff09;和wireshark&#xff08;对winpcap上数据进行抓…

16- C++多态-4 (C++)

第五章 多态 5.1 多态的引入 思考&#xff1a;在之前实现的英雄模型中&#xff0c;假如实现某个接口可以传入一个英雄&#xff0c;在该接口中可以对英雄的力量、敏捷和智力进行加强&#xff0c;请问该接口的参数该如何设计&#xff1f; 以上解决办法利用了C中的多态&#xf…

51单片机定时器/计数器

目录 1、定时器/计数器0/1介绍 1.1 定时器介绍 1.2 单片机定时/计数器原理 2、定时器/计数器0和1的相关寄存器 2.1 定时器/计数器控制寄存器TCON 2.2 定时器/计数器工作模式寄存器TMOD 2.3 定时器/计数器工作模式 2.3.1 模式0(13位定时器/计数器) 2.3.2 模式1(16位定…

CSDN如何输入公式

方法分三步&#xff1a; 1&#xff09;预先设置MathType的复制剪切选项 2&#xff09;将MathType已经编写好的公式复制到CSDN 3&#xff09;把复制的公式文本&#xff0c;首尾的“\[”和“\]”符号替换成“$$”和“$$” 1&#xff09;预先设置MathType的复制剪切选项 2&#x…

海量数据存储组件Hbase

hdfs hbase NoSQL数据库 支持海量数据的增删改查 基于Rowkey查询效率特别高 kudu 介于hdfs和hbase之间 hbase依赖hadoopzookeeper&#xff0c;同时整合框架phoenix(擅长读写),hive&#xff08;分析数据&#xff09; k&#xff0c;v 储存结构 稀疏的&#xff08;为空的不存…

Qt实现思维导图锦集

序号简述文章导航1思维导图树形结构、不重叠且均匀分布、支持折叠和展开核心树2菜单按钮风格、菜单提示风格、侧滑菜单、侧滑功能窗口UI设计3支持JPEG、PNG、XML、JSON、PDF、SVG格式文件数据导入导出4支持撤销回撤功能、显示节点操作流程、点击可跳转历史撤销回撤5思维导图横向…

哈工大计算机网络课程网络安全基本原理详解之:消息完整性与数字签名

哈工大计算机网络课程网络安全基本原理详解之&#xff1a;消息完整性与数字签名 这一小节&#xff0c;我们继续介绍网络完全中的另一个重要内容&#xff0c;就是消息完整性&#xff0c;也为后面的数字签名打下基础。 报文完整性 首先来看一下什么是报文完整性。 报文完整性…

C++模拟实现反向迭代器

1.代码实现 1.有了解正向迭代器的应该知道&#xff0c;比如list的正向迭代器其实本质是一个类&#xff0c;而有些人想模拟实现反向迭代器&#xff0c;依旧想再创建一个类&#xff0c;但是库里面想要的是&#xff0c;你给我一个迭代器&#xff0c;我就能给你反馈一个反向迭代器…

信号槽中的函数重载

信号槽中的函数重载 QT4的方式QT5的方式函数指针重载函数QT5信号函数重载解决方案 总结 QT4的方式 Qt4中声明槽函数必须要使用 slots 关键字, 不能省略。 信号函数&#xff1a; 槽函数&#xff1a; mainwondow: cpp文件&#xff1a; #include "mainwindow.h"…

C/C++多线程操作

文章目录 多线程C创建线程join 和detachthis_thread线程操作锁lock_guardunique_lock 条件变量 condition_variablewaitwaitfor C语言线程创建线程同步 参考 多线程 传统的C&#xff08;C11标准之前&#xff09;中并没有引入线程这个概念&#xff0c;在C11出来之前&#xff0c…

C语言:反转一个单链表

Lei宝啊&#xff1a;个人主页 题目&#xff1a; 描述&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 接口&#xff1a; struct ListNode* reverseList(struct ListNode* head){} 示例&#xff1a; 输入&#xff1a; head [1…

一起学算法(冒泡排序篇)

1.概念 冒泡排序&#xff08;Bubble Sort&#xff09;又称泡式排序&#xff0c;是一种简单的排序算法 核心思想&#xff1a;它重复地走访过要排列的次数&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来&#xff0c;走访数列的工作是重复地进行交…

【Datawhale夏令营】任务三学习笔记

任务一笔记回顾 任务二笔记回顾 目录 一&#xff1a;竞赛上分流程 1.1问题建模1.2数据分析 1.3数据清洗1.4特征工程 1.5模型训练与验证 二&#xff1a;任务总结与心得 一&#xff1a;竞赛上分流程 问题建模——>数据分析 ——>数据清洗——>特征工程——>模型…

ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000

leetcode上的一道题&#xff0c;当 s “a” 这样的单字符的时候&#xff0c;使用memset会出错&#xff0c;ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000。 有些奇怪&#xff0c; 记录一下&#xff0c;双字符以及以上的时候不会报错&#xff0c;

Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 Spring注解开发 一、注解开发定义Bean二、纯注解开发Bean三…

MacOS Sonoma 14.0 (23A5301g) Beta4 带 OC 0.9.3 and FirPE 双分区原版黑苹果镜像

7月26日苹果发布macOS Sonoma Beta 4预览版更新&#xff0c;但部分用户在升级后遇到了各种问题。这一测试版目前还处于开发阶段&#xff0c;因此出现各种问题并不意外。 一、镜像下载&#xff1a; 1.微信公众号&#xff1a;MacOS Sonoma 14.0 (23A5301g) Beta4 带 OC 引导双分…

视频爬虫:解析m3u8文件 python m3u8库,m3u8文件中.ts视频流的解密下载

一、引用的库 这里需要引用的库是&#xff1a;from Crypto.Cipher import AES 有坑哈&#xff0c;python3.0之后直接安装crypto你会发现不管怎么着都会报错。 经过查找资料找到了原因&#xff0c;原来是20年之后crypto已经被pycryptohome替换掉啦&#xff0c; 如果之前安装过…

暴力破解(DVWA和pikachu)

目录 前言暴力破解模式一.pikachu靶场1.基于表单的暴力破解2. 验证码绕过2. token防爆破 二. DVWA1.low,Medium2.High3. Impossible 前言 渗透测试中暴力破解方法解释&#xff1a;通过尝试所有可能的字符组合来猜测密码的方法。 这种攻击方法需要大量计算资源和时间&#xff0…

人类文明进入下个纪元奇点:UFO听证会-恒温超导发现-GPT大模型

今年以来&#xff0c;科技领域出圈的事件频繁发生&#xff0c;每一个事件都意味着一个领域的重大突破的可能。这些事件是UFO听证会、恒温超导LK99的论文、GPT类大模型的广泛应用&#xff0c;我常将这些事件串在一起思考&#xff0c;细思极恐&#xff0c;一种”火鸡与农场主“的…

Vue(待续)

概念 一套用于构建用户界面的渐进式JavaScript框架 Vue可以自底向上逐层的应用&#xff1a; 简单应用:只需一个轻量小巧的核心库。 复杂应用:可以引入各式各样的Vue插件。 1.采用组件化模式&#xff0c;提高代码复用率、且让代码更好维护。 2.声明式编码&#xff0c;让编码人员…