目录
进程创建
fork函数
写时拷贝
进程终止
进程退出场景
进程常见退出方法
进程等待
进程等待的必要性
进程等待的方法
wait方法
waitpid方法
获取子进程status
等待行为options
进程替换
各类程序替换函数的使用
命名理解
execl函数
execv函数
execlp函数
execvp函数
execle函数
execve函数
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
返回值:创建失败返回-1;创建成功给父进程返回子进程的id,给子进程返回0
fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。
通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。
main函数的return值是进程的退出码。0:success !0:failed
代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止(程序崩溃,退出码也变得没有了意义)
打印退出码如下:
我们可以看见,不同的退出码对应不同的信息。
异常退出: ctrl + c,信号终止
正常终止(可以通过 echo $? 查看进程退出码):
1. 从main函数return,代表进程退出
2. 任何地方调用exit,都代表终止进程,参数是退出码
3. _exit终止进程,但是不会进行进程的后续收尾工作,比如刷新缓冲区。
这里我们打印“hello World!”这个字符串,没有带换行,也就是这个字符串 暂时被保存在缓冲区里边的。这里使用_exit(退出码)是不会由任何结果的,因为它直接退出程序,不会为进程处理任何后续工作。使用exit(退出码)却能打印出来,因为他会为进程处理后续的工作,比如刷新缓冲区。
1、子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
2、进程一旦变成僵尸状态,那就刀枪不入,kill -9 也无能为力,因为谁也没有办法 杀死一个已经死去的进程。
3、父进程派给子进程的任务完成的如何,我们需要知道。比如,子进程运行完成,结果对还是不对, 或者是否正常退出。
4、父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
5、可以保证时序问题,子进程先退出,父进程后退出
查看文档
返回值: 成功返回被等待进程pid,失败返回-1。
参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
执行下边代码
这里演示了父进程等待回收子进程的全过程。
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任意一个子进程。与wait等效。
Pid>0.等待指定进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options: WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
复用刚刚的代码依旧可以完成子进程回收。
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特 位):
0:默认行为,阻塞等待。WNOHANG:非阻塞等待
非阻塞等待:可能需要多次检测。
进程不变,仅仅替换当前进程的代码和数据的技术叫做进程的程序替换
程序替换的本质就是把指定的进程+数据,加载进特定进程的上下文中!
但是,我们知道父子进程的代码是共享的。这里解释一下:父子进程代码共享的前提是代码没有发生改变。如果发生了改变,是会发生写时拷贝的。
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。
比如下类代码
其实有六种以exec开头的函数,统称exec函数:
· l(list) : 表示参数采用列表
· v(vector) : 参数用数组
· p(path) : 有p自动搜索环境变量PATH
· e(env) : 表示自己维护环境变量
执行结果如下
execv函数和execl函数其实没有什么区别。就是传递参数的方式不一样。execl函数使用可变参数列表传参,execv函数使用指针数组传参。
执行代码如下:
执行结果
执行代码:
执行代码:
执行代码
执行代码: