进程创建
再次理解fork
fork创建进程,我们已经了解了。在程序中,如果我们使用fork后,父进程会创建一个子进程,子进程可以通过if(x==0)进行分流,分流结束后,exit(0)退出。子进程也会进行父进程后面的代码。并且发生了神奇的现象,父子进程的同一个变量,有不同的数值。
进程终止
进程终止的三种情况:
- 正常退出。结果正确
- 正常退出。结果异常
- 程序异常终止。
进程退出码
退出0 则表示退出成功了。当然还有很多退出码,接下来我们用函数一一打印出来
实际上,我们平时的指令也是进程。那么他们退出都会有退出码。
不同情况,会返回不同的错误。
exit函数
使用exit函数退出进程也是我们常用的方法,exit函数可以在代码中的任何地方退出进程,并且exit函数在退出进程前会做一系列工作:
执行用户通过atexit或on_exit定义的清理函数。
关闭所有打开的流,所有的缓存数据均被写入。
调用_exit函数终止进程。
接下来 我们对比 exit 和_exit
执行程序后:
我们可以得出结论。
- _exit不刷新缓存区,exit刷新缓冲区
- _exit更贴近系统低层,exit包装了_exit
进程等待
进程等待是父进程为了查看子进程完成情况,以及子进程的退出情况。
进程等待的必要性:
- 回收僵尸进程,僵尸进程的危害我们不用多说了 用一个成语形容 ,那就是尸位素餐。
- 父进程要知道 我交给子进程办的事是不是办好了。也就是查看他的退出码。
- kill 无法杀死僵尸进程
进程状态标识:status
计算方法:
exitCode = (status >> 8) & 0xFF; //退出码
exitSignal = status & 0x7F; //退出信号
对于此,系统当中提供了两个宏来获取退出码和退出信号。
-
WIFEXITED(status):用于查看进程是否是正常退出,本质是检查是否收到信号。
-
WEXITSTATUS(status):用于获取进程的退出码。
我们可以发现被kill杀死的进程退出码则为0。
尽管我们在进程退出时设置为1.但退出时任然为0.
两种等待方法:wait方法和waitpid方法
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。
接下来我们使用这两个函数接口
- wait
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
int main()
{
pid_t id = fork();
if(id == 0){
//child
int count = 10;
while(count--){
printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid());
sleep(1);
}
exit(1);
}
//father
int status = 0;
pid_t ret = wait(&status); //等待子进程 并且一直等
cout<<((status >> 8)&0xFF)<<endl;
if(ret > 0){
//wait success
printf("wait child success...\n");
if(WIFEXITED(status)){
//exit normal
printf("exit code:%d\n", WEXITSTATUS(status));
}
}
sleep(3);
return 0;
}
注意:wait的等待是父进程的等待,并且一直等待直到子进程退出。
-
waitpid()
如果不带选项,那么和wait一模一样 接下来,我们带入选项。
WNOHANG: 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。 默认的行为是挂起调用进程,直到有子进程终止 。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。
WUNTRACED: 挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止 。返回的PID 为导致返回的已终止或被停止子进程的 PID。默认的行为是只返回已终止的子进程。当你想要检査已终止和被停止的子进程时,这个选项会有用。可以用或运算把这些选项组合起来 。例如:WNOHANG | WUNTRACED: 立即返回,如果等待集合中的子进程都没有被停止或终止,则返回值为0: 如果有一个停止或终止,则返回值为该子进程的 PID。
没用等待到 waitpid直接返回0。通过 这个方法,我们可以一边等待一边做事情。
父进程可以边做,边问。
进程替换
替换的基本概念:
用fork创建子进程后,子进程执行的是和父进程相同的程序(但有可能执行不同的代码分支),若想让子进程执行另一个程序,往往需要调用一种exec函数。
注意只是代码区更换,并没有产生新进程。
如图所示,他们并没有产生新的pcb,只是替换了进程代码和进程数据。