Linux系统编程:进程控制
1.进程创建
1.1 fork函数
fork()通过复制调用进程来创建一个新进程。新进程称为子进程,是调用进程的精确副本
进程,但以下几点除外:
- 子进程有自己的PID,此PID与任何现有进程组的ID不匹配
- 子进程的父进程ID与父进程的进程ID相同。
- 子进程没有继承父进程的内存锁
- 进程资源利用率(getrusage(2))和CPU时间计数器(times(2))在子进程中重置为零
- 子进程的挂起信号集最初为空
- 子进程不能继承父进程的信号调整
- 子进程不从父进程继承记录锁
- 子进程不从父进程继承计时器
- 子进程不继承父进程未完成的异步I/O操作,也不继承任何异步操作从它的父进程中获取同步I/O上下文
#include <unistd.h>
pid_t fork();
返回值:fork成功则子进程PID被返回给父进程,0被返回给子进程。失败,-1被返回给父进程,没有子进程创建。-- 给父进程返回子进程的pid,子进程返回0,是因为一个父进程可以有多个子进程,儿子进程只能有一个父进程。

当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可执行自己的代码,看如下程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>int g_val = 0;
int main()
{pid_t id = fork();if(id < 0){perror("fork");return 0;}else if(id == 0){ //childg_val = 100;printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);}else{ //parentprintf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}
执行结果如下:

由结果可以得出,fork之后,父子进程各自执行自己的代码块
1.2 写时拷贝
2.进程终止
进程退出场景:
- 代码运行结束,结果正确
- 代码运行结束,结果错误
- 代码异常终止
进程退出方式:
- 从main返回 return n; 执行 return n 等同于执行 exit(n), 因为调用 main 的运行时函数会将 main 的返回值当做 exit 的参数。
- 在任意地方调用exit(errno) -- 库函数,终止进程,主动刷新缓冲区
- _exit() -- 系统调用,终止进程不会刷新缓冲区
- ctrl + c -- 信号终止
echo $? :看记录最后一个进程在命令行执行完毕时对应的退出码
#include <unistd.h>
void _exit(int status);参数:status定义了进程的终止状态,父进程通过wait来获取该值 。虽然status是int,但是仅有低8位可以被父进程所用
#include <unistd.h>void exit(int status);exit最后也会调用exit, 但在调用exit之前,还做了其他工作:
1. 执行用户通过 atexit 或 on_exit 定义的清理函数。2. 关闭所有打开的流,所有的缓存数据均被写入3. 调用 _exit

3.进程等待
检测子进程推出信息,将子进程的退出信息通过status拿回来。
3.1 进程等待必要性
- 之前讲过,子进程退出,父进程如果不管不顾,就可能造成 ‘ 僵尸进程 ’ ,进而造成内存泄漏。
- 另外,进程一旦变成僵尸状态,那就刀枪不入, kill -9 也无能为力
- 最后,父进程派给子进程的任务完成的如何,我们需要知道。
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.2 进程等待的方法
wait方法
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int* status);
参数:获取子进程的退出状态返回值:成功,返回终止子进程的pid,失败返回-1
waitpid方法
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(pid_t pid, int *status, int options);
参数:pid:pid=-1,等待任意子进程,与wait等效;pid>0,等待其进程id与pid相等的子进程status:输出型参数,拿到子进程的退出结果。-- 有自己的为图结构,只关心低16
个比特位()0-15,
次低八位(8-15):进程退出状态(结果是否正确) -- 设为:(status>>8) &0XFF
低七位(0-7):进程终止信号(是否正常结束) -- 设为:status&0X7F
options:先设为0
返回值:成功,返回收集到的子进程的pid若无可以收集的一推出的子进程,return 0;
若调用出错,返回-1,这时,errno会被设置为相应的值,以指示错误所在。

WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)

下面看一段示例代码:
#include<stdlib.h>
#include<stdio.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("我是子进程pid:%d, 父进程:%d, cnt = %d\n", getpid(), getppid(), cnt--);sleep(1);}// int d = 10/0;exit(10);}sleep(7);int status = 0;pid_t ret = waitpid(id, &status, 0);if(id > 0){printf("wait success: %d, sig_number: %d, child_exit_code: %d\n", ret, (status & 0x7F), (status>>8)&0xFF);}sleep(5);return 0;
}
结果下图所示:

第一个图为我执行kill -9 22287得到的结果,进程收到9号信号,进程终止
第二个图为程序正常运行结束的结果。
阻塞等待和非阻塞等待:
阻塞等待:子进程未推出时,父进程不调用一直等待直到子进程结束。
非阻塞等待:在与逆行期间一直询问子进程是否结束 waitpid()函数第三个参数设置为WNOHANG。
非阻塞等待的好处:
- 不会占用父进程的所有精力,可以在轮询期间做别的事情
下面为非阻塞式等待的代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{pid_t pid;pid = fork();if(pid < 0){printf("%s fork error\n",__FUNCTION__);return 1;}else if( pid == 0 ){ //childprintf("child is run, pid is : %d\n",getpid());sleep(5);exit(1);} else{int status = 0;pid_t ret = 0;do{ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待if( ret == 0 ){printf("child is running\n");}sleep(1);}while(ret == 0);if( WIFEXITED(status) && ret == pid ){printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));}else{printf("wait child failed, return.\n");return 1;}}return 0;
}

可以看到,父进程在等待子进程的同时还会执行打印语句。若为非阻塞等待,会一直卡在等待的环节。
总结:
进程等待:
- 是什么? -- 通过系统调用,让父进程等待子进程的方式
- 为什么? -- 释放子进程的僵尸状态,获取子进程状态
- 怎么等? -- wait/waitpid 阻塞等待/非阻塞等待
4.进程程序替换
相当于用自己的程序把别人的程序跑起来,支持不同语言(任何后端语言)的替换
4.1 创建子进程的目的
- 想让子进程执行父进程代码的一部分 -- 执行父进程对应的磁盘代码中的一部分
- 想让子进程执行一个全新的程序 -- 让紫禁城想办法,家在磁盘上指定的程序,执行新程序的代码和数据

4.2 替换原理
就是将指定程序的代码和数据加载带指定的位置,进程替换时并没有创建新的进程。在子进程中调用execl函数并不会影响父进程的执行(进程具有独立性)。OS感觉到替换后,则进行写时拷贝,重新分配内存--子进程通过页表重新映射到新的内存。
4.3 替换函数
#include <unistd.h>`int execl(const char *path, const char *arg, ...);参数:path:路径arg:在命令行怎么执行就怎么传参...:可变参数列表返回值:执行失败返回-1,并继续执行源代码,成功不会返回值。int execlp(const char *file, const char *arg, ...);p:代表的就是如何找程序的功能,带p字符的函数,不用告诉我替换程序的路径,只需要知道时谁,会自动在环境变量PATH中进行可执行程序的查找。int execle(const char *path, const char *arg, ...,char *const envp[]);int execv(const char *path, char *const argv[]);
v:vector,可以将所有的可执行参数,放入到数组(必须以空作为结束)中,不用可变参数传参int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]); -- 允许自定义环境变量
所有的execl* 系列的接口都必须以NULL结尾
下面为各个接口的演示:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<sys/wait.h>
#include<sys/types.h>int main()
{// 用我们的程序将别人的程序执行起来// .c -> exe -> load -> process -> 运行 -> 执行我们现在所写的代码printf("process is running...\n");// 只要是个函数,掉用就有可能失败 就是没有替换成功 继续执行下面的原代码// 只有错误时会返回,返回值为-1execl("/usr/bin/ls"/*要执行哪个程序*/, "ls","-a", "-l", "--color=auto", NULL/*你想怎么执行*/); // 所有的execl* 系列的接口都必须以NULL结尾printf("execl\n");// 为何下面的printf没有执行呢? printf是在execl之后的,execl执行完毕后,代码已经完全被替换 开始新的程序的代码了printf("process running done...\n");return 0;
}
这个代码使用execl接口将我们的程序替换为了命令行命令,ls -a -l,结果如下:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<sys/wait.h>
#include<sys/types.h>int main(int argc, char *argv[])
{printf("process is running\n!");pid_t id = fork();assert(id!=-1);if(id == 0){sleep(1);execlp("ls", "ls","-a", "-l", NULL);printf("原子进程!\n");exit(10);}// 下面为父进程的代码,子进程的替换不会影响父进程的代码int status = 0;int ret = waitpid(id, &status, 0);if(ret > 0) printf("wait success: exit code:%d, sig:%d\n", (status>>8) & 0xFF, status & 0x7F);return 0;
}
有这段代码也可以看出,子进程在进行程序替换时,是不会影响父进程的
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<sys/wait.h>
#include<sys/types.h>int main(int argc, char *argv[])
{printf("process is running\n!");pid_t id = fork();assert(id!=-1);if(id == 0){sleep(1);char *arv_[] = { "ls","-a", "-l", NULL };execv("/usr/bin/ls", arv_);exit(10);}// 下面为父进程的代码,子进程的替换不会影响父进程的代码int status = 0;int ret = waitpid(id, &status, 0);if(ret > 0) printf("wait success: exit code:%d, sig:%d\n", (status>>8) & 0xFF, status & 0x7F);return 0;
}

其他几个与上面的类似,这里就不一一列举了。除了可以替换系统命令,还可以将程序替换为自己的程序,下面用一个接口execvp来演示。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<sys/wait.h>
#include<sys/types.h>int main(int argc, char *argv[])
{printf("process is running\n!");pid_t id = fork();assert(id!=-1);if(id == 0){sleep(1);char *arv_[] = { "mycpp", NULL };execvp("./mybin", arv_);exit(10);}// 下面为父进程的代码,子进程的替换不会影响父进程的代码int status = 0;int ret = waitpid(id, &status, 0);if(ret > 0) printf("wait success: exit code:%d, sig:%d\n", (status>>8) & 0xFF, status & 0x7F);return 0;
}
相关文章:
Linux系统编程:进程控制
1.进程创建 1.1 fork函数 fork()通过复制调用进程来创建一个新进程。新进程称为子进程,是调用进程的精确副本 进程,但以下几点除外: 子进程有自己的PID,此PID与任何现有进程组的ID不匹配子进程的父进程ID…...
Android 异常开机半屏重启代码分析
Android 的稳定性是 Android 性能的一个重要指标,它也是 App 质量构建体系中最基本和最关键的一环;如果应用经常崩溃,或者关键功能不可用,那显然会对我们的留存产生重大影响所以为了保障应用的稳定性,我们首先应该树立…...
Kafka从0到消费者开发
安装ZK Index of /zookeeper/zookeeper-3.9.2 下载安装包 一定要下载-bin的,不带bin的是源码,没有编译的,无法执行。-bin的才可以执行。 解压 tar -zxvf apache-zookeeper-3.9.2-bin.tar.gz 备份配置 cp zoo_sample.cfg zoo_sample.cfg-b…...
01-项目功能,架构设计介绍
稻草快速开发平台 开发背景就是通过此项目介绍使用SpringBoot Vue3两大技术栈开发一个拥有动态权限、路由的前后端分离项目,此项目可以继续完善,成为一个模板为将来快速开发做铺垫。 实现功能 开发流程 通过命令构建前端项目在VSCode中开发ÿ…...
bvh 好用强大的播放器源码
目录 效果图: 显示旋转角度: 显示骨骼名称 下载链接: 可以显示骨骼名称,旋转角度,自适应大小,支持3维npz数据可视化 python实现,提供源代码,修改和完善很方便。 根据3维npz生成…...
安阳在线知识付费系统,培训机构如何进行课程体系的设置?
校外培训不管是从招生还是课程体系都是截然不同的,在课程体系设置上,不同的层次设计也就不同。课程体系设计在功能诉求上可以分为入门课、核心课、高利润课、种子课四个类别。下面为大家介绍一下。 1、入门课 “入门课”就是最易、最省、最少障碍的满足家…...
网络编程:服务器模型-并发服务器-多进程
并发服务器概念: 并发服务器同一时刻可以处理多个客户机的请求 设计思路: 并发服务器是在循环服务器基础上优化过来的 (1)每连接一个客户机,服务器立马创建子进程或者子线程来跟新的客户机通信 (accept之后…...
React 基础案例
React的特点: 1、声明式编程 2、组件化开发 3、多平台适配yuan 原生实现: <h2 class"title"></h2><button class"btn">改变文本</button><script>let msg "Hello World";const titleEl d…...
【Python探索之旅】选择结构(条件语句)
文章目录 条件结构: 1.1 if单分支结构 1.2 if-else 多分支结构 1.3 if-elif 多重结构: 完结撒花 前言 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。 Python提供了顺序、选择、循环三…...
Recommender ~ Collaborative filtering
Using per-item features User j 预测 movie i: Cost Function: 仅求和用户投票过的电影。 常规规范化(usual normalization):1/2m 正则化项:阻止过拟合 在知晓X的前提下,如何学习w,b参数…...
我觉得POC应该贴近实际
今天我看到一位老师给我一份测试数据。 这是三个国产数据库。算是分布式的。其中有两个和我比较熟悉,但是这个数据看上去并不好。看上去第一个黄色的数据库数据是这里最好的了。但是即使如此,我相信大部分做数据库的人都知道。MySQL和PostgreSQL平时拿出…...
AI 情感聊天机器人工作之旅 —— 与复读机问题的相遇与别离
前言:先前在杭州的一家大模型公司从事海外闲聊机器人产品,目前已经离职,文章主要讨论在闲聊场景下遇到的“复读机”问题以及一些我个人的思考和解决方案。文章内部已经对相关公司和人员信息做了去敏,如仍涉及到机密等情况…...
如何使用ArcGIS Pro进行选房分析
无论是研究城市规划布局还是寻找理想的住房,都需要综合考虑购物、医疗、教育和休闲等多方面因素,此时我们的GIS软件就可以派上用场了,这里为大家介绍一下如何使用 ArcGIS Pro 进行选房分析,希望能对你有所帮助。 数据来源 教程所…...
android图标底色问题,debug与release不一致
背景 在android 8(sdk 26)之前的版本,直接使用图片文件作为图标,开发时比较容易控制图标,但是不同的安卓定制版本就不容易统一图标风格了。 在android 8及之后的版本,图标对应的是ic_launcher.xml&#x…...
如何提高自己的全局视野?
以下是一些可以帮助提高全局视野的方法: 1. 广泛学习不同领域知识:包括但不限于技术相关的各个领域、业务知识、行业动态等,拓宽知识面。 2. 参与大型项目:积极投身到复杂的、规模较大的项目中,在实践中感受和理解系…...
element ui的确认提示框文字样式修改
修改确认提示框文字样式修改,使用message属性修改: 例: js代码: this.$msgbox({title: 确定要删除吗?,message: this.$createElement(p, null, [this.$createElement(span, { style: color: red }, 该素材一旦删除,…...
Typescript 哲学 - ts模块使用最佳实践
ts的作用域 默认是全局(global),这也是为什么在 两个ts文件声明同一个变量报错变量名冲突,解决方法是使某个文件以模块的形式存在(文件顶层使用 export 、import ) In TypeScript, just as in ECMAScript 2…...
自动驾驶决策规划——坐标转换
以下内容来自b站up主忠厚老实的老王,视频链接:自动驾驶决策规划算法序章 总纲与大致目录_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1hP4y1p7es/?spm_id_from333.999.0.0&vd_sourced36e625f376908cfa88ef5ecf2fb0ed8侵删。 决策规划算法…...
信创应用软件之邮箱
信创应用软件之邮箱 文章目录 信创应用软件之邮箱采用信创邮箱的必要性信创邮箱采购需求国产邮箱业务形态国产邮箱代表性品牌CoremailRichmail安宁eyouUMail拓波 邮件安全的发展阶段 采用信创邮箱的必要性 邮箱是天然的数据存储空间,党政和央国企客户在使用过程中存…...
TriCore: Architecture
说明 本文是 英飞凌 架构文档 TriCore TC162P core archiecture Volume 1 of 2 (infineon.com) 的笔记,稍作整理方便查阅,错误之处,还请指正,谢谢 :) 1. Architecture 2. General Purpose & System Register 名词列表&#…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
