Linux进程控制(一)

一、fork函数

在linux中,父进程通过fork函数创建子进程,子进程返回0,父进程返回子进程的pid,出现错误返回-1。

当运行fork函数时,OS会为子进程创建task_struct、mm_struct(进程地址空间)、页表,子进程继承父进程的代码,数据通过写时拷贝的方式进行共享和独立。因此父子进程互相不影响,做到相互独立。

而在fork之前父进程独自运行,fork之后父子进程分别执行,因此父子进程都会有一个返回值,当父进程先return后,子进程再return,此时就发生写时拷贝(注意:父子进程的先后执行顺序取决于调度器)。

如上图,二号进程被执行两次,就是因为fork函数中,父进程会执行第二次printf代码,而子进程也会执行第二次printf代码。

那么子进程如何继承父进程的代码呢???或者说子进程是全部继承父进程的代码吗???

一般来说,父子进程共享fork之后的代码,子进程只能执行fork之后的代码。在CPU中有程序计数器eip,其作用是用来保存正在执行指令的下一条指令,当进行fork时,eip会拷贝给子进程,子进程就会从eip指向的位置开始执行,eip还可以改成main函数入口,此时子进程就会从main函数开始执行。

写时拷贝 

在不写入数据时,父子进程代码、数据共享;当任意一个进程写入数据时,就以写时拷贝的方式进行。

如上图,在未修改数据之前,父子进程共享数据块、代码块,当父子进程任意一方要修改数据时,就会触发写时拷贝,OS为要修改数据的一方开辟一块空间并将原数据拷贝到这块空间中,并为他生成新的页表。

为什么不在fork时把父进程数据全部给子进程呢
  1. 父进程的数据子进程并不是全部需要的,可能只是只读操作,这会产生空间浪费;
  2. 值拷贝需要修改的数据,其他数据共享,这种层面技术角度难以实现;
  3. fork函数直接全部拷贝给子进程,会增减fork的成本,消耗空间时间。

写时拷贝优点在于:只拷贝修改的数据,也就是用最小的成本实现,这是一种延迟拷贝策略,只有当真正需要的时候(进程需要立刻使用,如果进程不是立刻使用,OS会分配给需要的进程)才会开辟空间,拷贝数据,这也就变相的提高了内存使用效率。

 二、进程终止

在c/cpp代码中,main函数是入口,而代码中的return 0是什么意思???

  1. return 0是return给谁???
  2. return 其他数值可以吗???

在代码执行完毕后,结果正确则返回0,不正确返回非0,而各个非0的数字有代表退出码,不同的退出码代表不同的进程退出信息,方便快速定位程序的问题。

  1. 在main函数中,return代表进程退出;在其他函数中代表返回值,该函数调用结束;
  2. 在代码中的任意位置使用exit表示进程退出。

如上图,使用echo $? 打印最近一次的退出码,第一次打印111,代表主函数的返回信息,而第二次打印0,代表echo进程成功执行,返回0。

内核在进程终止时的作用

进程是由内核结构和进程代码、数据构成,内核结构也就是task_struct和mm_struct,而在进程终止时OS可能并不会将内核的数据结构释放。创建一个进程主要分为开辟空间,初始化进程数据两步。而linux会维护一张废弃的数据结构链表,当一个进程被释放(结构被释放,空间没有被释放)时,其不需要的数据就会被维护在这个数据结构链表中,当这个进程再次被创建的时候,OS就会从数据结构链表中找到该进程对应的task_struct和mm_struct,只需要改进成的数据进行初始化即可

三、进程等待 

  1. 如果子进程退出时,父进程不做处理就可能造成僵尸进程,最终造成内存泄漏问题;
  2. 父进程无法拿到子进程的返回值,且僵尸进程无法被杀死。

因此父进程通过进程等待的方式回收子进程的资源并获取子进程的退出信息。

通过wait证明进程等待

用wait的方式回收子进程,让子进程从Z状态进入X状态;

  1. pid_t  wait ( int *status ) ; pid_t>0表示等待成功,否则失败;它可以等待任意一个退出的子进程;
  2. pid_t  waitpid ( pid_t  pid , int  *status , int options) ; 参数pid代表要等待的进程pid,-1表示等待任意进程;options是0表示阻塞等待;
  3. status是一个输出型参数,通过调用该函数从函数内部拿到特定的数据;若不关心子进程退出状态可设为NULL :例如当子进程代码执行完毕返回退出信息到子进程的task_struct时,该函数从子进程的task_struct内部拿到子进程的退出码。

详解status

waitpid(pid_t pid , int *status , int options)函数方法中,options默认是0,叫做阻塞等待,

如上图,在代码中子进程经过count 5次循环后break,exit的退出码是99,在父进程代码中,可以直接通过status获得子进程的退出码,而status的次八位表示子进程的退出状态,status的低七位表示异常退出,因为这个进程收到某个特定的信号。

如上图,status的低七位表示子进程收到的某个特定信息。当一个进程出现异常时,只关心退出信号,退出码毫无意义。

  • WIFEXITED(status): 查看进程是否正常返回,子进程正常结束返回时,则为真。
  • WEXITSTATUS(status):查看进程的退出码,若WIFEXITED非零,直接拿到子进程退出码。

四、进程程序替换

子进程执行的是父进程的代码块,如何让创建出来的子进程执行完全不一样的、全新的程序呢???此时就需要用到进程程序替换,由OS完后该工作。

一般在服务器设计时需要子进程干两件事情:

  1. 让子进程执行父进程的代码块;
  2. 让子进程执行磁盘中全新的程序(shell,让客户端执行对应的程序)。

程序替换原理就是:将磁盘中的程序加载到内存中并重新建立要执行程序的页表映射,此时子进程就不再共享父进程的数据及代码而是执行该程序对应的数据及代码。

如何实现进程程序替换?

执行一个全新的程序,需要解决什么问题???

  1. 先确定程序的位置;
  2. 程序可携带选项执行(明确程序如何执行即要不要带选项);

int execl(const char *path , const char *arg, ...); 为例

第一个参数表示上述问题一;

第二个参数表示可携带的选项(就是命令行的写法(ls -a -l))最后必须是NULL,表示【如何执行程序】的参数传递完毕。

执行上述代码后发现,最后一个printf并没有执行,这是因为一旦execl将程序替换成功后,就会将后面的代码全部替换(execl之后的原先代码就不再存在),因此不执行。

那么该程序替换函数是否需要返回值呢???

当execl替换成功后,即使由返回值int ret = execl(......),此时也没有机会再执行后续代码,因为后面的代码已经被替换了。但是如果程序替换失败,就不会执行execl(......)代码,而是通过返回值来判断失败原因。


进程创建与进程替换

如上图,在子进程内部进行程序替换,如果替换失败则exit(1),此时代码执行结果显示:子进程程序替换成功,父进程等待成功,说明父子进程各自执行且没有受到影响。子进程进程程序替换,不会影响父进程(进程之间的独立性)。

那么是如何做到的呢???

在数据层面是写时拷贝,但是当子进程要进城execl时,OS会识别到紫禁城的execl操作并且为了不让父进程代码受到影响就会将代码也进行写时拷贝,因此就可以理解为数据和代码都发生了写时拷贝。

函数名称    
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[]);

c程序调用cpp程序

如上图,在ALL依赖关系与方法中,ALL依赖mycmd与myexec两个方法,因此make ALL会依次生成mycmd与myexec两个程序。

在myexec.c文件中的execl函数会在对应的路径中搜索"mycmd",找到后并执行。

子进程环境变量传递

在函数execle中涉及到参数环境变量:

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

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

相关文章

C语言实现高精度计时和高精度延时微秒级别

C语言实现高精度计时和高精度延时微秒级别 目的说明环境说明一、高精度延时(微秒级别)二、测试例程三、测试结果 目的说明 在Windows下C语言实现高精度计时功能和高精度延时微秒级别环境说明 Dev-C V5.11一、高精度延时(微秒级别) void vDelayUS(u32 usDelay) {LARGE_INTEGER…

C语言例:表达式10<<3+1的值

10的二进制 00001010 10<<3 01010000 十制左移m位&#xff0c;乘以。 0101 0000 十进制80 10<<31 81

Day75:WEB攻防-验证码安全篇接口滥用识别插件复用绕过宏命令填入滑块类

目录 图片验证码-识别插件-登录爆破&接口枚举 登录爆破 接口枚举 图片验证码-重复使用-某APP短信接口滥用 滑块验证码-宏命令-某Token&Sign&滑块案例 知识点&#xff1a; 1、验证码简单机制-验证码过于简单可爆破 2、验证码重复使用-验证码验证机制可绕过 3、…

运维篇SHELL脚本实战案例

统计出每个IP的访问量有多少&#xff1f; 检查是否提供了日志文件的路径作为参数。使用awk从日志文件的每行中提取第一个字段&#xff08;假设这是IP地址&#xff09;。使用sort对提取的IP地址进行排序。使用uniq -c统计每个唯一IP地址的出现次数。最后&#xff0c;使用sort -…

复习斐波那契(用C++写)

或者这样写&#xff1a; 斐波那契数列 题目描述 斐波那契数列是指这样的数列&#xff1a;数列的第一个和第二个数都为 1 1 1&#xff0c;接下来每个数都等于前面 2 2 2 个数之和。 给出一个正整数 a a a&#xff0c;要求斐波那契数列中第 a a a 个数是多少。 输入格式…

Java基础---IO流

1. File类 1.1 File的介绍 File是java.io.包下的类&#xff0c; File类的对象&#xff0c;用于代表当前操作系统的文件&#xff08;可以是文件、或文件夹&#xff09;。 注意&#xff1a;File类只能对文件本身进行操作&#xff0c;不能读写文件里面存储的数据。 1.2 File类…

Python模块-基础知识

Python模块-基础知识 1.模块分类&#xff1a; &#xff08;1&#xff09;自定义模块&#xff1a; 如果你自己写一个py文件&#xff0c;在文件内写入一堆函数&#xff0c;则它被称为自定义模块&#xff0c;即使用python编写的.py文件 &#xff08;2&#xff09;第三方模块&…

javaSE练习题(一)

1、BMI是根据体重测量健康的方式。通过以千克为单位的体重除以以米为单位的身高的平方计算出BMI。下面是16 岁以上人群的BMI图表: 编写一个java程序&#xff0c;提示用户输人以磅为单位的体重和以英寸为单位的身高&#xff0c;然后显示BMI值。注意: 1磅是0.453592 37千克而1英寸…

9大变频电源模块的测试参数及其重要性

变频电源是将交流电经过交流-直流-交流变换&#xff0c;从而得到输出为正弦波的交流电&#xff0c;广泛应用于家电、电机、电脑设备、测试单位、航空等领域。变频电源测试是确保系统稳定运行的重要步骤。 变频电源测试的重要参数 1. 输出电压和电流 可用万用表、电流表或者示波…

企业电脑如何管控(高效管控企业电脑的小技巧)

员工企业管理一直以来都是一个难题&#xff0c;难在人员多管理费劲。 因此高效管理一直都是企业最头疼的问题。 而使用一款软件辅助管理是很多企业发现的最有效的方法&#xff0c;如域智盾软件。 域智盾软件是一款专业的文件加密和数据安全软件&#xff0c;适用于各种企业和个…

4.1 用源文件写汇编代码

汇编语言 1. 源程序 1.1 伪指令 汇编指令是有对应的机器码的指令&#xff0c;可以被编译为机器指令&#xff0c;最终为CPU所执行伪指令没有对应的机器指令&#xff0c;最终不被CPU所执行伪指令是由编译器来执行的指令&#xff0c;编译器根据伪指令来进行相关的编译工作 1.2…

【LeetCode每日一题】2312. 卖木头块(DFS记忆化搜索+动态规划)

文章目录 [2312. 卖木头块](https://leetcode.cn/problems/selling-pieces-of-wood/)思路1:用DFS进行记忆化搜索代码&#xff1a;思路2:动态规划代码&#xff1a; 2312. 卖木头块 思路1:用DFS进行记忆化搜索 1.要用DFS深度优先遍历每一种情况。在递归的同时&#xff0c;不断更…

【什么是Internet?网络边缘,网络核心,分组交换 vs 电路交换,接入网络和物理媒体】

文章目录 一、什么是Internet&#xff1f;1.从具体构成角度来看2.从服务角度来看 二、网络结构1.网络边缘1.网络边缘&#xff1a;采用网络设施的面向连接服务1.1.目标&#xff1a;在端系统之间传输数据1.2.TCP服务 2.网络边缘&#xff1a;采用网络设施的无连接服务2.1目标&…

unicloud快速上手,unicloud项目创建以及项目创建注意事项

uniCloud快速上手 本项目地址https://gitee.com/qayrup/unicloud-demo 创建unicloud项目 新建一个uni项目,并选择启用unicloud,选择阿里云或腾讯云 阿里云和支付宝云都支持一个月免费的云,如果只想体验啥的,可以选择这两个, 但是需要注意,支付宝云需要配置跨域,否则很多云函…

XCode升级错误:Command CompileC failed with a nonzero exit code 解决办法

升级完XCode之后&#xff0c;bulid失败&#xff0c;出现如下错误&#xff1a; 问题1&#xff1a; xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrunCommand Compi…

网络编程面试题

一、什么是IP地址 1.IP地址是主机在网路中的唯一标识&#xff0c;&#xff0c;当主机从一个网络切换到另一个网络时&#xff0c;会更改IP地址&#xff0c;同样的IP地址也是路由器进行路由选择的标识 2.IP地址的分类 IPV4&#xff1a;采用4字节无符号整数存储 IPV6&#xff…

【JVM】垃圾收集算法,垃圾收集器,调优,如何判断类是无用的类?对象一定分配在堆中吗?了解逃逸分析技术吗?

目录 垃圾收集算法 标记-清除 标记-复制 标记-整理 分代收集算法 垃圾收集器 CMS收集器 G1 收集器 Serial收集器 ParNew收集器 Parallel Scavenge 收集器 Serial Old 收集器 Parallel Old 收集器 引用类型总结 调优 调优命令 调优工具 调优参数 如何判断类是…

PPT好看配色

放几个链接&#xff01;画图时候可以参考&#xff01;转自知乎 Color Hunt ColorDrop 中国色 Flat UI Colors Coolors

Stable Diffusion之核心网络结构解析

Stable Diffusion核心网络结构解析 1. SD模型整体架构初识 1. SD模型整体架构初识 Stable Diffusion模型整体上是一个End-to-End模型&#xff0c;主要由以下三个核心组件构成。 VAE&#xff08;变分自编码器&#xff0c;Variational Auto-Encoder&#xff09;&#xff0c;U-N…

Redis设计原理简介

键值存储模型&#xff1a; Redis是一个基于内存的键值对存储系统&#xff0c;它支持五种基本数据结构&#xff08;字符串String、哈希Hash、列表List、集合Set、有序集合Sorted Set&#xff09;以及几种高级数据结构如Bitmaps、HyperLogLogs等。 单线程架构&#xff1a; Redis采…