进程创建-终止-等待-替换

进程创建

意义
进程运行时常会出现崩溃,为了避免父进程出现奔溃,则会创建子进程去代替父进程处理事务,即使崩溃并不会影响到父进程的正常运行,再创建一个子进程再次处理罢了。
用法:(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中使用 --------- 退出后刷新缓冲区

在这里插入图片描述
具体执行流程如下:

  1. printf打印的内容读入缓冲区
  2. 进入fun函数,退出调用该函数的的进程,并刷新缓冲区,输出------
  3. 使用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文件进行了替换实现,结果。。。。。。。。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44768163/article/details/115351312