这里总结下Linux进程控制总结❗
fork()创建子进程成功,父进程返回子进程的pid是大于0的为真,子进程返回的是0为假;根据if语句的与和或逻辑进行判断即可。
程序替换成功后,当前进程运行的程序将成为pwd程序,因此当前程序的最后一句printf将不会被执行,因为当前进程在运行完pwd程序后将退出。
因此程序并不会打印最后a的值,而是执行pwd打印当前工作路径之后退出。
Fork后子进程保留了父进程的环境变量和当前工作目录。fork函数功能是通过复制父进程,创建一个新的子进程。
- 环境变量默认会继承于父进程,与父进程相同
- 信号相关信息各自独立,并不会复制
- 工作路径也相同
- 每个进程都有自己独立的标识符
进程等待:
进程等待功能:
- 等待子进程退出
- 获取子进程返回值(退出码)
- 释放子进程资源,避免出现僵尸进程。释放僵尸子进程资源(僵尸进程的代码和数据已经被释放了,进程控制块PCB在父进程等待之后被OS释放)属于进程等待的功能。
- 退出指定子进程❌是return 和 _exit和exit的事情
关于waitpid函数:
- pid_t waitpid(pid_t pid,int *status,int options);
- waitpid默认阻塞等待任意一个或指定子进程退出,当options被设置为WNOHANG则函数非阻塞,且当没有子进程退出时,waitpid返回0
- 若pid大于0,则表示等待指定的子进程退出
- 若pid等于-1,则表示等待任意一个子进程退出
- status参数用于获取退出子进程的退出码
- 若options选项参数被设置为WNOHANG则waitpid为非阻塞
关于waitpid函数WNOHANG参数的描述:
- 若选项参数被设置为WNOHANG则waitpid为非阻塞
若waitpid设置WNOHANG后,没有子进程退出则返回值为0
waitpid默认阻塞等待任意一个或指定子进程退出,当options被设置为WNOHANG则函数非阻塞,且当没有子进程退出时,waitpid返回0,并不会阻塞。
进程退出:
如何使一个进程退出:
在程序的任意位置调用return❌函数使用return可能就不会进程退出只是使其函数返回
在main函数中调用return
在程序的任意位置调用exit接口
在程序的任意位置调用_exit接口
退出进程的方式在课堂中咱们讲到了三种:
- 在main函数中return
- 主任意位置调用库函数 exit
- 在任意位置调用系统调用 _exit
- 库函数 exit 可以在任意位置调用,用于退出进程, 并且退出前会刷新文件缓冲区中的数据到文件中。
- 系统调用 _exit 可以在任意位置调用,用于退出进程,但是退出时直接释放所有资源,并不会刷新缓冲区。
进程退出返回值:
- 进程的退出返回值也不能随意设置,因为进程的退出返回值实际上只用了一个字节进行存储,因此随意设置可能会导致实际保存的数据与设置的数据不同的情况,因为过大会导致数据截断存储。
- waitpid(int pid, int *status, int options); 函数中 status参数 用于父进程获取退出子进程的返回值。
- ❌程序异常退出时,进程返回值为-1。
- 程序异常退出时,意味着程序并没有运行到return/exit去设置返回值,则返回值不做评判标准,因为返回值的存储位置的数据是一个未知随机值。
- 并不能由任意进程获取子进程退出返回值,只能是父进程。
通过fork和exec系统调用可以产生新进程:
- fork生成的进程是当前进程的一个相同副本。fork调用通过复制父进程创建子进程,子进程与父进程运行的代码和数据完全一样。
- fork系统调用与clone系统调用的工作原理基本相同。fork创建子进程就是在内核中通过调用clone实现。
- exec是程序替换函数,本身并不创建进程。
- clone函数的功能是创建一个pcb,fork创建进程以及后边的创建线程本质内部调用的clone函数实现,而exec函数中本身并不创建进程,而是程序替换,因此工作机理并不相同。
- clone函数的功能是创建一个pcb,fork创建进程以及后边的创建线程本质内部调用。
- clone函数实现,而exec函数中本身并不创建进程,而是程序替换,因此工作机理并不相同。
clone
是一个相当底层的系统调用,通常用于实现更复杂的进程间通信(IPC)机制,如线程(尽管 POSIX 线程库pthread
在更高层次上提供了对线程的抽象)。如果你只是想要简单地创建一个新的进程,那么使用fork
和exec
系列函数通常会更简单和直观。然而,如果你需要更细粒度的控制或想要实现更复杂的 IPC 机制,那么clone
可能会是一个有用的工具。
进程等待:
1.考察进程等待的意义, 同时理解wait/waitpid函数的使用
2.要求:
- 代码创建子进程, 父进程调用wait/waitpid函数进行等待, 子进程打印“i am child process”之后,等待5s钟结束子进程;
- 父进程在等待到子进程之后, 分析子进程的退出信号, coredump标志位以及退出码, 并打印到标准输出
查看以下代码☞出现以下情况:
❓❓出现这种情况是为什么呢
答:因为子进程没有写入,还是和父进程一起共享了后面的代码,又不存在exit结束子进程,子进程是在最后整个代码执行完的时候才结束。所以出现了wait fail的结果。wait success是父进程正常等待。
子进程通常会在执行完其任务后自动退出,或者是否需要明确使用 exit 或 return 来退出子进程,取决于具体的情况和需求❗❗
一般来说,子进程会在以下情况下自动退出:
- 执行到程序的末尾。
- 调用 return 语句。
- 使用 exit 函数可以在子进程中明确地终止进程,并可以指定退出状态码呢。
exec系列函数:
exec函数族:execl,execlp,execle,execv,execvp,execve
#include <unistd.h> extern char **environ; 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[]);
- 其中 l 和 v 的区别在于程序运行参数的赋予方式不同,l是通过函数参数逐个给与,最终以NULL结尾,而v是通过字符指针数组一次性给与。
- 其中有没有 p 的区别在于程序是否需要带路径,也就是是否会默认到path环境变量指定的路径下寻找程序,没有p的需要指定路径,有p的会默认到path环境变量指定路径下寻找。
- 其中有没有 e 的区别在于程序是否需要自定义环境变量,没有e则默认使用父进程环境变量,有e则自定义环境变量。
- execl函数可以直接指定可执行程序文件的名称而不需要路径❌
- execle函数可以直接指定可执行程序文件的名称而不需要路径❌
- execl函数和execlp函数的区别是是否自定义设置环境变量❌
- execl函数和execle函数的区别是是否自定义设置环境变量
- exec系列的函数均需要路径。
进程程序替换:
- 进程程序替换成功后,运行完新程序,则程序直接退出
- 进程程序替换成功后,原进程没有退出,使用原进程运行新程序
- 程序替换是在当前进程pcb并不退出的情况下,替换当前进程正在运行的程序为新的程序(加载另一个程序在内存中,更新页表信息,初始化虚拟地址空间)
- 并且当前进程在运行完替换后的程序后就会退出,并不会继续运行原先的程序,这是尤其需要注意的。
- 进程替换并没有创建新的进程
- 1.考察进程程序替换函数簇的函数,利用任一一个进程程序替换函数完成代码。
- ❗❗理解进程程序替换的本质, 在深层次的理解程序员在 命令行当中启动一个程序, 本质上是bash程序启动了一个子进程, 子进程程序替换成为程序员启动的程序。
- 2.要求: 将启动程序替换成为“ls”程序, 并且要给“ls”程序指定命令行参数, “-l”和“-a”
🙂感谢大家的阅读,若有错误和不足,欢迎指正。