Linux --进程控制
本文从以下五个方面来初步认识进程控制:
目录
进程创建
进程终止
进程等待
进程替换
模拟实现一个微型shell
进程创建
在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。
当进程调用fork以后就会进行分流,内核会分配新的内存块和内核数据结构给子进程,将父进程的部分数据结构内容拷贝到子进程,添加子进程到系统的进程列表之中。
通常,⽗⼦代码共享,⽗⼦在不写⼊时,数据也是共享的,当任意⼀⽅试图写⼊,便以写时拷⻉的⽅式各⾃⼀份副本。因为有写时拷⻉技术的存在,所以⽗⼦进程得以彻底分离离!完成了进程独⽴性的技术保证!写时拷⻉,是⼀种延时申请技术,可以提⾼整机内存的使⽤率,具体⻅下图
fork之前⽗进程独⽴执⾏,fork之后,⽗⼦两个执⾏流分别执⾏。注意,fork之后,谁先执⾏完 全由调度器决定。fork的返回值在父子进程之中有不同的体现,父进程中会返回子进程的pid,子进程返回0,如果fork失败返回-1,这里我们用一段代码解释上面提到的一些概念。
这里我们定义了一个全局变量gav,在没有fork以前我们会看到父进程的pid和gav等于100,fork以后我们通过不同的返回值能够达到不同分支完成不同任务的效果,这里能看到子进程已经将gav修改成了110,而父进程的gav没有改变,两个进程的gav地址一样是因为在各自的虚拟内存的地址,实际它们的物理地址已经发生了写时拷贝被分配了新的物理地址。
需要注意的一点是,子进程会拷贝父进程的的代码,所以在结束if()分支后两个进程都会执行剩下的代码,而不要误解成为剩下的代码会由父进程执行。
fork的常规用法:
⼀个⽗进程希望复制⾃⼰,使⽗⼦进程同时执⾏不同的代码段。例如,⽗进程等待客⼾端请求,⽣成⼦进程来处理请求。
⼀个进程要执⾏⼀个不同的程序。例如⼦进程从fork返回后,调⽤exec函数,这个在进程替换的时候会讲解。
进程终止
进程终⽌的本质是释放系统资源,就是释放进程申请的相关内核数据结构和对应的数据和代码。
进程退出有三个场景:1.代码运行完毕,结果正确。2.代码运行完毕,结果不正确。3.代码异常终止,返回异常信号。
进程终止有三个方式:1.main()函数return 2.调用exit(),可以在代码任何地方,表示进程结束。 3._exit(),这是系统调用接口,exit()的就是底层调用这个接口。这三种方式结束进程都会有返回值,异常终止的进程只有异常信号,这些都会存在内核数据中,父进程可以等待获取。在linux命令行中我们可以使用echo $?来查看进程的退出码。注意这个退出码是程序员自己约定好的,一般来说进程成功运行返回0,其他非0返回则代表进程执行出现错误,异常信号是由系统中断进程再返回的。
Linux Shell 中的主要退出码:



进程等待
为什么要进行进程等待必要性?之前讲过,⼦进程退出,⽗进程如果不管不顾,就可能造成‘僵⼫进程’的问题,进⽽造成内存 泄漏。 另外,进程⼀旦变成僵⼫状态,那就⼑枪不⼊,“杀⼈不眨眼”的kill -9 也⽆能为⼒,因为谁也没有办法杀死⼀个已经死去的进程。最后,⽗进程派给⼦进程的任务完成的如何,我们需要知道。如⼦进程运⾏完成,结果对还是不对,或者是否正常退出。 ⽗进程通过进程等待的⽅式,回收⼦进程资源,获取⼦进程退出信息。、
进程等待通常有两种方式,一个是pid_t wait(int* status),等待成功会返回进程Pid,失败则返回-1,status是一个输出型参数,如果不关心子进程退出状态可以将参数设为NULL。
另外一个是pid_ t waitpid(pid_t pid, int *status, int options)了
返回值: 当正常返回的时候waitpid返回收集到的⼦进程的进程ID; 如果设置了选项WNOHANG,⽽调⽤中waitpid发现没有已退出的⼦进程可收集0,则返回; 如果调⽤中出错,则返回-1,这时errno会被设置成相应的值以指⽰错误所在;
这里我们用一个子进程提前结束但是没有及时被父进程等待回收的例子来说明wait
pid_t id = fork();if(id == 0){int cnt = 5;while(cnt--){cout<<"子进程 pid:"<<getpid()<<endl;sleep(1);}exit(0);}else if(id>0){sleep(10);pid_t rid;rid = wait(nullptr);if(rid>0){cout<<"等待子进程成功 rid : "<<rid<<endl;}sleep(10);}
当子进程结束以后父进程还需要等待5秒才会回收,此时子进程是僵尸状态,五秒以后等待成功,子进程被释放只有父进程
wait和waitpid,都有⼀个status参数,该参数是⼀个输出型参数,由操作系统填充。





vector<int> data;
void backup_data(vector<int>& data)
{string name =to_string( time(nullptr));//name += ".backup";FILE* fp = fopen(name.c_str(),"w");//以时间戳命名备份文件if(fp == nullptr)exit(1);string numdata;for(auto d : data){numdata+=to_string(d);//将数据转换为string类型写入文件numdata+=' ';}fputs(numdata.c_str(),fp);fclose(fp);exit(0);
}
int main()
{int num = 0;while(++num){data.push_back(num);//插入数据if(num %10 == 0)//每增加十次数据则进行一次备份{int status = 0;pid_t id = fork();if(id == 0){backup_data(data);//子进程进行备份}pid_t rid = wait(&status);if(rid > 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0)//等待子进程成功并且正常退出(退出码为0){cout<<"备份成功!"<<endl;}else {cout<<"备份失败请注意!"<<endl;}}sleep(1);}
}
运行结果:

进程替换
fork() 之后,⽗⼦各⾃执⾏⽗进程代码的⼀部分如果⼦进程就想执⾏⼀个全新的程序就需要进程的程序替换来完成这个功能。程序替换是通过特定的接⼝,加载磁盘上的⼀个全新的程序(代码和数据),加载到调⽤进程的地址空间中。
替换原理:⽤fork创建⼦进程后执⾏的是和⽗进程相同的程序(但有可能执⾏不同的代码分⽀),⼦进程往往要调⽤⼀种exec函数以执⾏另⼀个程序。当进程调⽤⼀种exec函数时,该进程的⽤⼾空间代码和数据完全被新程序替换,从新程序的启动例程开始执⾏。调⽤exec并不创建新进程,所以调⽤exec前后该进程的id并未改变。这里用一个简单代码可以体现
其实有六种以exec开头的函数,统称exec函数:

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);

模拟实现一个微型shell
所以我们利用上诉的几个理论知识就可以手搓一个简易版的shell,我们首先要知道shell的工作原理其实就是:
1.获取命令行 2.解析命令行 3.执行命令
在此之前我们需要先实现shell循环打印我们的信息框 比如:[toutie40@VM-8-16-centos repos]$,这个信息框是由 [+用户名+@+主机名+当前地工作目录]
此时就可以打印处理命令行信息了
接下来我们需要实现地是获取命令行
此时我们就可以获取用户输入地命令了
然后我们需要将获取到地命令进行解析,将comman_buffer的字符分割到gargv的每一个元素中
接着就可以正确解析命令了
下一步就是将我们解析的命令使用子进程调用需要的进程即可
然后我们就可以实现一个简易版的shell了!
但是它只能够正常调用一些系统,cd命令并没有调用成功,或者说调用了但是没有起效,这是因为调用的cd其实是更改了子进程的工作目录,并不能影响我们父进程shell的工作目录,所以这部分需要shell本身调用自身函数来实现功能的命令我们成为自建命令,那么我们可以使用chdir进行更改当前的工作目录,记得要将环境变量中的pwd一起修改,因为Linux中的cd命令也是这样实现的。
通过检查解析后的命令是否与内建命令相同,如果相同则shell本身调用函数实现,命令行的pwd也要记得修改
env,export等命令也是内建命令,我们在系统中export会添加加属于shell维护的env表里面,我们自己写的shell目前的环境变量表是直接继承shell进程的,我们应该也要维护自己的env表,那么这里用系统shell进程环境变量来创建我们自己的env表
使用了自定义的环境变量表以后记得修改命令行获取pwd的使用自定义的环境变量表,替换程序也可以使用我们自定义的环境变量表
至此我们就完成了一个"手搓的"shell,这个shell遵循了系统中shell的基本原理,有自己的环境变量表,能够实现一般命令和内建命令。通过这个案例我们能够知道为什么在export环境变量是一个内存级的修改,因为在每次重新启动shell以后就会重新加载环境变量,而环境变量是以参数的方式传给我们的替换程序从而实现全局变量。
总结

相关文章:

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...