前面博客中讲解了进程控制中的一小部分:进程创建。
下面开始其他部分内容总结:
库函数(3号man
手册)
系统调用函数(2号man
手册)
exit
调用了_exit
系统函数区别:
main
函数中 return
或者 exit
终止进程,会刷新缓冲区,并释放资源。_exit
终止进程会直接释放资源。进程返回值:
0
。通过指令$?
: 查看上一程序退出码。
程序的退出码使用一个字节来保存,最大为255
。
进程等待:等待子进程状态改变(一般为终止),一般用于循环中,轮询判断。
最好是在子进程退出的时候等待,但是父进程根本就不知道子进程何时退出~
因此只能在子进程创建之后调用wait
,进行进程等待。
调用wait
就是一直在等待子进程的退出。
wait
: 系统调用接口,man
手册2
号。
wait
接口是一个阻塞函数,功能是等待子进程退出。
如果没有任何一个子进程退出,则一直等待,直到有任一子进程退出。
阻塞:为了完成某个功能发起调用,如果当前不具备完成条件,一直等待,直到完成后返回。
非阻塞:为了完成某个功能发起调用,如果当前不具备完成条件,直接报错返回。
pid_t wait(int *status);
status
:用于返回退出值pid
。 如果出错:返回-1
。用法
int status;
wait(&status);
pid_t waitpid(pid_t pid,int *status,int options);
pid
:指定的进程id
。
<= -1
:等待任一子进程
>= 0
:等待指定子进程
status
: 用于获取返回值
option
:选项,默认为0
WNOHANG
:将waitpid
设置为非阻塞。
函数返回值:
< 0
:出错
== 0
:没有子进程退出
> 0
:退出子进程的pid
用法
while(waitpid(pid,&status,0) == 0){
perror("waitpid error");
}
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
系统调用接口)
不定参函数:
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号手册) 【最终调用函数】
execl
与execv
区别:参数的赋予是以指针数组形式还是以不定参形式p
与无p
的区别:第一个参数:程序名称是否需要给定路径,未给定就从PATH
中找。e
与无e
的区别:环境变量是否由用户自己设置,自己设置环境变量就会覆盖掉原有的全局变量。用法
通过实现一个minishell
体会一下shell
原理与程序替换:
【 https://blog.csdn.net/qq_42351880/article/details/89111562 】
shell处理流程:
while(1){
1. 获取标准输入
2. 对输入字符串进行解析:获取程序名称+参数
3. 创建子进程
程序替换 – 程序名称
目的:(保证主程序的稳定性)
父进程自己并不完成功能,而是使子进程去完成,
如果发生错误,shell就崩溃了!
所以这个“危险的”操作让子进程去处理。
4. 进程等待
}
在Linux
中为一个结构体:内存描述符mm_struct
。
作用:
内存页:页号|页内偏移
一页中有4k
,一个地址的高20
位就是页号,低12
位位页内偏移。
虚拟地址通过虚拟页号通过页表得到物理页号,再复制一份页内偏移,虚拟地址与物理地址的页内偏移都是一样的。物理页号结合页内偏移就是物理地址,完成访问控制。
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)
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