进程创建
意义:
进程运行时常会出现崩溃,为了避免父进程出现奔溃,则会创建子进程去代替父进程处理事务,即使崩溃并不会影响到父进程的正常运行,再创建一个子进程再次处理罢了。
用法:(man命令查相关命令及函数的具体用法)
fork() | 创建子进程,父子进程虚拟地址空间独立 |
---|---|
vfork() | 创建子进程,并阻塞父进程,父子进程同用一块虚拟地址空间 |
返回值:子进程返回0;父进程返回子进程pid;出错返回-1
阻塞父进程:因为子进程和父进程分配到CPU的可能性是一样的,所以子进程与父进程谁先运行难以确定。但经过阻塞父进程后,子进程便先于父进程获取到CPU。
为什么要阻塞父进程:因为使用vfork()函数创建子进程时,父子进程共用同一块虚拟地址空间,因此共用同块栈去,如果不进行阻塞,会出现栈混乱的现象。
上一篇文章分析了进程创建的原理,链接如下:
https://blog.csdn.net/qq_44768163/article/details/115014661
进程终止
方法 | 接口 | 头文件 | 区别 |
---|---|---|---|
exit | 库函数调用接口 | <stdlib.h> | 退出后刷新缓冲区 |
_exit | 系统调用接口 | <unistd.h> | 退出后并不刷新缓冲区 |
return | 只能在main中使用 | --------- | 退出后刷新缓冲区 |
具体执行流程如下:
- printf打印的内容读入缓冲区
- 进入fun函数,退出调用该函数的的进程,并刷新缓冲区,输出------
- 使用echo $? 获取返回值99
注:缓冲区的存在避免了频繁操作输出设备,使得输入内容存满后,再刷新缓冲区。。
进程等待
概念:
父进程等待子进程退出,获取退出子进程的退出返回值,释放子进程资源,避免产生僵尸进程。
方法:
wait方法:(等待任意一个子进程退出)
返回值:成功- ->返回被等待进程pid;失败- ->返回-1;
参数:获取子进程退出状态- ->int *status;不关心子进程退出状态- ->置为NULL
waitpid方法:(等待任意一个子进程退出或等待一个指定的子进程退出)
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(用来查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零(正常终止),提取子进程退出码。(用来查看进程的退出码)
options:
0:表示默认阻塞等待
WNOHANG: 设置为非阻塞等待
阻塞等待:为了完成一个功能,发起调用,若当前不具备完成条件,则一直等待。
非阻塞等待:为了完成一个功能,发起调用,若当前不具备完成条件,则报错返回。eg:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
实验:
wait方法
正常退出运行结果:
异常退出运行结果:(使用kill强杀依然属于正常退出,这里通过不创建子进程再等待即可模拟出等待失败返回-1的场景)
waitpid方法:
正常退出:
异常退出:
获取子进程的status
进程替换
其实有六种以exec开头的函数,统称exec函数:
#include <unistd.h>`
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[]);//库函数
int execve(const char *path, char *const argv[], char *const envp[]);//系统调用函数
参数说明:
第一个参数:新的程序文件路径名
第二个参数:程序的运行参数
第三个参数:程序的环境变量
实验:
总结:
有没有p的区别:
在于程序文件是否需要带路径,有p时可以不带路径,但前提是该程序文件必须在PATH环境变量指定路径下。
有没有e的区别:
在于程序是否自己设定环境变量,有e时自己设定环境变量(覆盖式设定,将之前已有的环境变量覆盖),没有e时,使用默认已有的环境变量。
l和v的区别:
在于程序运行参数的赋予方式不同,l是一一列举传入,v是将运行参数列入数组中,整体传入
上图是进程替换的几种实现方式,分别对已有的ls程序文件和自定义实现的arg.c文件进行了替换实现,结果。。。。。。。。