[Linux] 进程控制


前面博客中讲解了进程控制中的一小部分:进程创建

下面开始其他部分内容总结:


进程终止(进程退出)

exit()

库函数(3号man手册)

_exit()

系统调用函数(2号man手册)

  • 所以实际是exit调用了_exit系统函数

区别

  1. main函数中 return 或者 exit 终止进程,会刷新缓冲区,并释放资源。
  2. _exit终止进程会直接释放资源

进程返回值:

  1. 程序正常退出,结果符合预期,返回0
  2. 程序正常退出,结果不符合预期,返回错误码
  3. 程序异常退出:[ 崩溃 ]

通过指令$? : 查看上一程序退出码。
程序的退出码使用一个字节来保存,最大为255


进程等待

进程等待:等待子进程状态改变(一般为终止),一般用于循环中,轮询判断

  • 为什么要等待子进程退出?
    避免产生僵尸进程。

最好是在子进程退出的时候等待,但是父进程根本就不知道子进程何时退出~
因此只能在子进程创建之后调用wait,进行进程等待。
调用wait就是一直在等待子进程的退出。

wait: 系统调用接口,man手册2号。

wait接口是一个阻塞函数,功能是等待子进程退出。
如果没有任何一个子进程退出,则一直等待,直到有任一子进程退出。

阻塞:为了完成某个功能发起调用,如果当前不具备完成条件,一直等待,直到完成后返回。
非阻塞:为了完成某个功能发起调用,如果当前不具备完成条件,直接报错返回。

wait()

pid_t wait(int *status);
  • 功能:等待任意一个子进程退出
  • status:用于返回退出值
  • 返回值:如果返回退出子进程的pid。 如果出错:返回-1

用法

int status;
wait(&status);

waitpid()

pid_t waitpid(pid_t pid,int *status,int options);	
  1. pid指定的进程id
    <= -1:等待任一子进程
    >= 0 :等待指定子进程

  2. status: 用于获取返回值

  3. option:选项,默认为0
    WNOHANG:将waitpid设置为非阻塞

  4. 函数返回值:
    < 0 :出错
    == 0 :没有子进程退出
    > 0:退出子进程的pid

用法

while(waitpid(pid,&status,0) == 0){
	perror("waitpid error");
}

在这里插入图片描述
waitpid函数的参数status值中:

  • 16位没有使用~
  • 16位存放:
    • 8位:返回码,例如255
    • 8位:
      • 1位:coredump标志(核心转储) :保存异常退出时,程序的运行信息。
      • 7位:异常退出原因,异常事件的信号值。

获取子进程返回值
根据上图,我们获取返回值其实只是通过按位右移就可获取:

(status >> 8) & 0xff
  • 正常终止:
    0xff:一个字节,相当于char*。一个字节二进制位上全为1,按位与&上一个数字,则剩下的就是低八位,其中存放的是程序的返回值。

  • 异常终止
    7位中保存异常信号值,出错一定大于0,否则正常退出低7位为0
    通过低7位是否为0,判断程序是否异常退出。

status & 0x7f 

7位如果是0表示正常退出。

已经封装好的宏接口

  • WIFEXITED(status);:如果为真,正常退出
  • WEXITSTATUS(status);

上面获取返回值的操作相当于:

if(WIFEXITED(status)){
	printf("exit code :%d\n",WEXITSTATUS(status));
}

程序替换

程序替换:替换进程所运行的程序,重新初始化虚拟地址空间,更新页表信息

替换后重新从替换进程的main函数起始处开始运行。就是将进程的运行虚拟地址空间所映射在物理内存的区域进行改变,改变成另一个程序在内存中的位置,更改内存指针,更新页表信息,重新初始化虚拟空间。

  • 为何替换?
    常用于替换子进程程序,让子进程完成其他功能。

  • 如何替换?
    exec函数族 (调用的都是execve系统调用接口)

exec函数族

不定参函数:

execl	(const char *path,const char *arg,...);
execlp	(const char *file,const char *arg,...);//如果没有给定路径,默认去PATH环境变量中查找,找不着则报错
execle	(const char *path,const char *arg,...,char *const envp[]);
		
execv	(const char *path,char *const argv[]);
execvp	(const char *file,char *const argv[]);
execve	//(man 2号手册)	【最终调用函数】
  1. execlexecv区别:参数的赋予是以指针数组形式还是以不定参形式
  2. p与无p的区别:第一个参数:程序名称是否需要给定路径,未给定就从PATH中找。
  3. e与无e的区别:环境变量是否由用户自己设置,自己设置环境变量就会覆盖掉原有的全局变量。

用法
通过实现一个minishell体会一下shell原理与程序替换:
https://blog.csdn.net/qq_42351880/article/details/89111562

shell处理流程:
while(1){
1. 获取标准输入
2. 对输入字符串进行解析:获取程序名称+参数
3. 创建子进程
程序替换 – 程序名称
目的:(保证主程序的稳定性)
父进程自己并不完成功能,而是使子进程去完成,
如果发生错误,shell就崩溃了!
所以这个“危险的”操作让子进程去处理。
4. 进程等待
}


虚拟地址空间

Linux中为一个结构体:内存描述符mm_struct

作用

  1. 保持进程独立性。
  2. 通过页表映射物理地址,充分利用物理内存。
  3. 增加内存访问控制。

内存页:页号|页内偏移
一页中有4k,一个地址的高20位就是页号,低12位位页内偏移。

虚拟地址通过虚拟页号通过页表得到物理页号,再复制一份页内偏移,虚拟地址与物理地址的页内偏移都是一样的。物理页号结合页内偏移就是物理地址,完成访问控制。


例题两则

  1. 下面程序会创建多少个进程?
fork();
if(fork() && fork() || fork())
	fork();

答案:16个。

0号)
fork();1号)
if(fork()2号) && fork()3号)|| fork()4号))
	fork()5号);

0号与1号进程经历完全相同。
创建过程
零号:0-1-2(4-5)-3(4-5)-5
①:2(4-5)-3(4-5)-5
②:4-5
③:4-5
4号自己返回0,失败,无法进入if语句
14+2=16个

图解

	0
	|-1
	  |-2
		|-4
		|-5
	  |-3
		|-4
		|-5
	  |-5
	|-2
	  |-4
	  |-5
	|-3
	  |-4
	  |-5
	|-5
(16)
  1. 共打印多少个横杠?
for(int i = 0;i < 2;i++){
	fork();
	printf("-");
}

答案:共打印8个。
因为没有刷新缓冲区,复制了父进程缓冲区中未打印的横杠。

那如果变成了以下形式,多了\n换行符,会打印多少个?

for(int i = 0;i < 2;i++){
	fork();
	printf("-\n");
}

答案:共打印6个。
因为\n刷新缓冲区,没有复制父进程缓冲区中的横杠。


【注】监控while [true] ;do clear;ps aux |grep wait; sleep 0.5;done

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

giturtle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值