Linux:进程控制和进程替换

一、进程控制

进程控制我们要以三个方面讲:进程的创建、进程的终止、进程的等待

进程的创建

在前面的文章中我已经讲到了进程如何创建,在这里做个总结
1.先调用fork()函数
• 分配新的内存块和内核数据结构给⼦进程
• 将⽗进程部分数据结构内容拷⻉⾄⼦进程
• 添加⼦进程到系统进程列表当中
•fork 返回,开始调度器调度

返回值类型为pid_t(int类型的ypedef),返回值为0,是创建的子进程,返回值为>0的数,是父进程,返回值<0,创建失败
2.当共享的数据发生改变
我们根据学到的基本虚拟地址空间的知识,一旦发生改变,就会发生 写时拷贝,将共享的数据复制重新复制一份到物理内存中

还有就是为什么操作系统能知道代码发生了变化?
因为本来虚拟地址的数据区域映射到页表的标志位的权限默认是可读可写("rw")权限,一旦创建了子进程的话就会先把父进程页表中的数据段权限改为只读("r"),也会拷贝到的子进程,现在父子进程的代码区域权限都是只读,一旦父子进程任意一个尝试修改共享的数据的话,就会被操作系统检查到没有写入的权限,就会判断是要把共享的数据写时拷贝一份在物理内存中,并且恢复父子进程默认的代码权限,以后在不同分区内改代码只会影响自己,这样就能保证进程的独立性
调用fork()的原因
⼀个⽗进程希望复制⾃⼰,使⽗⼦进程同时执⾏不同的代码段。例如,⽗进程等待客⼾端请求,⽣成⼦进程来处理请求。
调用fork()失败原因
• 系统中有太多的进程
• 实际⽤⼾的进程数超过了限制

进程终止

进程终⽌的本质是释放系统资源,就是释放进程申请的相关内核数据结构和对应的数据和代码。
所以我们执行一个进程的退出的条件是什么? 我们最接近的就是我们常写的int main()函数里的 return 0
退出码
它叫做退出码 ,是我们进程终止的情况之一
退出码(退出状态)可以告诉我们最后⼀次执⾏的命令的状态。在命令结束以后,我们可以知道命令是成功完成的还是以错误结束的。其基本思想是,程序返回退出代码0时表⽰执⾏成功,没有问题。代码1或0以外的任何代码都被视为不成功。
退出码的含义
可以通过strerror传整数然后返回默认的返回数字之下的错误信息

常见的退出方法
从main返回 (return 返回)
调⽤exit

_exit
这两个退出区别不大exit退出会刷新缓冲区, _exit不会,但是exit底层是调用_exit,所以建议用exit
退出信号
它是进程终止的情况之一,这里的退出信号异常也会导致进程终止,比如ctrl+c结束进程,一般都是空指针的访问等情况就会导致进程直接被终止,我们其实也经历过,我们运行代码弹出来的白色的噔噔响的窗口就是信号异常退出的结果,这里我们都是粗略讲等学到这个具体知识再详谈, 可以通过 kill -l查看进程异常的信号的选项 与退出码的区别:退出信号异常代码不会全部运行完,提前退出,而退出码异常是正常写的逻辑跑完后退出
额外说明:我们task_struct中有 int exit_signal(退出信号),int exit_code(退出码),将来进程退出后的退出信号和退出码传给task_struct的这两个变量
进程退出场景
1.代码正确,结果正确
也就是退出码为0,退出信号为0
这里额外说明:为什么我们好像平常写代码运行感受不到,是因为我们是靠printf或cout等输入只能看到程序主动打印出来的内容,但进程真实的退出码、终止信号,并不会自动打印到屏幕上。这部分信息保存在内核 PCB 中,必须由父进程调用wait() / waitpid()系统调用,读取 status 参数,才能拿到。我们直接在终端运行程序,看不到内核保存的这一组退出状态。这是下面进程等待的知识,这里引出来
2.代码正确,结果不正确
退出码不为0,退出信号为0
代码没跑完,程序异常了
退出码无意义,退出信号为非0

进程等待

我们之前讲过,当一个子进程结束时,它的状态会先变成僵尸状态,它不会立马被操作系统回收task_struct结构,这时则需要父进程获取子进程的退出状态后操作系统才会回收掉task_struct结构
为什么要有进程等待呢?
如上面所说,第一,能避免内存泄漏,因为将来会回收掉内核数据结构.第二,你创建的子进程的目的不是为了完成父进程的任务吗(本身的进程也是bash的子进程),那完成的结果肯定要让父进程知道完成的怎么样,所以将来的退出码和退出信号组成的退出状态会给父进程
进程等待的方法

调用的函数 使用方法
pid_t wait(int * status) 子进程退出后,父进程会阻塞式的等待处于僵尸状态的子进程,然后接收退出状态
pid_t waitpid(pid_t id,int* status,int options) 1.id是等待特定一个处于僵尸状态的子进程,如果传入-1,就是等待任意一个僵尸状态的子进程 2.status是子进程中退出码和退出信号一起组成的一个整数, 3.option是选择阻塞状态(传参 -> 传0)还是非阻塞状态(传参->传 WNOHANG这个宏)

pid的返回值和参数:

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

waitpid的返回值:

返回值:1.当正常返回的时候waitpid返回收集到的⼦进程的进程iD;
2.如果设置了选项WNOHANG,⽽调⽤中waitpid发现没有已退出的⼦进程可收集,则返回0;
3.如果调⽤中出错,则返回-1,这时errno会被设置成相应的值以指⽰错误所在;

子进程的status

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


将来给父进程完成任务的结果就是查看status,可以通过宏操作看到退出码和退出信号, WIFEXITED(status)查看退出信号,WEXITSTATUS(status)查看退出码

阻塞状态和非阻塞状态

阻塞状态:就是父进程阻塞在waitpid这里一直等待进程退出的子进程,自己下面的工作没等到子进程是不会执行的
非阻塞状态:父进程每过一段时间去等待子进程退出,没等到就继续执行自己的工作
区别就是:非阻塞状态会轮询等待,它的等待时间可以做其他的事情,而阻塞状态只能等到子进程才能做其他事情
下面是一段基本感受进程等待的代码

三、进程替换

我们知道fork()之后是父子进程是执行同一个程序中的代码,即使你用pid做分流让子进程做与父进程不同的任务,其本质还是在同一个数据段和代码段执行的,我子进程能不能执行一个全新的程序呢? 可以!进程的程序替换可以来完成这一工作

用一个简单例子见一下效果

可以看到子进程做了ls这个系统命令的工作,父进程照常等待子进程并实行它的逻辑

替换原理

我们fork()后,在子进程里通过exec系列的函数能让我们当前的进程去一个全新的程序里执行它的逻辑代码这是我们所看到的现象,原理:当进程调用exec系列函数后,我们会把exec函数中要替换的程序的代码区和数据区拷贝到当前这个进程的物理内存的相应位置进行覆盖

可以发现该进程的id并没有发生改变只是虚拟空间地址某些区域的大小、页表、物理内存可能会有所改变
不仅是可以让进程执行系统命令,也可以执行C/C++/java/python等语言写的程序,因为将来它们编译都是转化为二进制程序,本质上没太大区别,系统命令也是用C语言编译好的二进制程序

一些替换函数

• l(list) :表⽰参数采⽤列表
• v(vector) 参数⽤数组
• p(path) 有p⾃动搜索环境变量
• e(env) :表⽰⾃⼰维护环境变量PATH



列表的意思是:你的选项是分开写的 比如调系统程序"ls" "-a" "-l" 这样分开写的
数组就是把这些选项写到一个指针数组里面,只要穿指针数组的地址就行

是否带路径就是如果是带了p的exec函数,如果你替换的程序是环境变量PATH能找到的就可以直接写程序名就能版本你找到,不需要写绝对路径和相对路径
execvpe,你替换的程序的路径是用当前没有改变的环境变量找,所以是你虽然改变了环境变量,要替换后才会覆盖掉旧的环境变量

相关推荐
默辨17 分钟前
我用Java后端的视角读了一遍JoyAgent-JDGenie
java·开发语言
weixin1997010801620 分钟前
《alibaba.idle.isv.order.ship 接入实录:闲鱼订单发货回传的5个隐式约束》(附Python源码)
服务器·前端·python
SQL-First布道者22 分钟前
⚡ Spring JDBC 完整体系 · 第 5 讲 · Spring Boot Starter JDBC
java·spring boot·spring·mybatis·spring jdbc
爱和冰阔落25 分钟前
【Linux】多线程打印为什么会乱?pthread 创建、等待、退出、取消与分离全实战
linux·运维·c++·redis
DYWorker00125 分钟前
基于RK3568的Linux驱动开发:实战——WDT
linux·驱动开发
宋哥转AI26 分钟前
深入理解 AI Agent · 多 Agent 编排 #03:Supervisor 与 Orchestrator——Dream-SaaS 双层编排架构拆解
java·人工智能·ai
初願致夕霞27 分钟前
五种IO模型(详解非阻塞IO与多路转接)
linux·前端·网络·数据库·tcp/ip
深圳多奥智能一卡(码、脸)通系统29 分钟前
智能梯控SDK可直接用于第三方开发对接的接口文档说明书
服务器·前端·算法
维核科技29 分钟前
三个业务共用一台服务器,显存不够用了
大数据·服务器·人工智能