一、僵死进程
1、了解进程的结构
(1)进程分为两部分:
- PCB(进程控制块):控制一个进程,相当于一个struct。
记录一个进程的相关执行过程和内存位置。 - 进程实体:指定进程的操作。
(2)创建进程:
先申请一个PCB结点,再加载程序生成进程实体。
(3)进程结束:
先结束进程实体,再释放PCB。
2、什么是僵死进程?
(1)从结构上来说:进程实体已经被释放,但PCB结点并没有被释放。
因为在PCB中有一个退出码,用来查看进程退出原因,如果进程实体被释放,用户就要查看进程退出原因,所以如果要看退出码,PCB就不能被释放。
(2)从理论上来说: 一个进程执行结束,但是进程的PCB没有被系统释放。也就是父进程未结束,子进程结束,并且父进程没有获取子进程的退出码。
(3)代码实现僵死进程
在子进程和父进程10s的时间差内,子进程为僵死进程;当父进程执行结束后,子进程就变成了孤儿进程,被init所接管并处理。
孤儿进程:父进程结束,子进程未结束
int main()
{
pid_t pid = fork() ;
assert(pid != -1);
if (pid == 0)
{
printf("child staring\n");
sleep(10);
printf("child over\n");
}
else
{
printf("father staring\n");
sleep(20);
printf("father over\n");
}
exit(0);
}
运行结果:
3、如何处理僵死进程?
(1)让父进程获取子进程的退出状态:pid_t wait(int *result);
- result获取的是进程的退出码。
- 返回值处理的是进程的pid
(2)代码
int main()
{
pid_t pid = fork() ;
assert(pid != -1);
if (pid == 0)
{
printf("child staring: %d\n",getpid());
sleep(10);
printf("child over\n");
}
else
{
pid_t id = wait(NULL);
printf("id = %d\n",id);
printf("father staring\n");
sleep(20);
printf("father over\n");
}
exit(0);
}
运行结果:
(3)但是使用wait有一个问题就是:
wait本身会阻塞,也就是等待事件发生,本身会挂起,直到有一个任意子进程退出。(然后才会执行父进程)
二、进程替换
1、为什么要用进程替换?
创建子进程后,子进程执行的还是父进程的指令,想让子进程执行另外的指令。
2、进程替换
(1)一个进程能够执行另一份程序。
即,在fork之后,让子进程进行进程替换,父进程继续执行其指令,子进程执行另一份指令。
(2)注意:执行进程替换仅仅是替换进程所执行的指令集,并没有创建新的进程。
//test.c
int main(int argc,int argv[])
{
printf("test:my pid = %d\n", getpid());
int i = 0;
for (; i < argc; ++i)
{
printf("argv[%d] = %s\n",i,argv[i]);
}
exit(0);
}
//main.c
int main()
{
pid_t pid = fork();
assert(pid != -1);
if (pid == 0)
{
printf("child:my pid = %d\n", getpid());
execl("./test", "./test", "nine", "percent", (char*)0);
int i = 0;
for (; i < 5; ++i)
{
printf("child\n");
sleep(1);
}
}
else
{
int i = 0;
for (; i < 10; ++i)
{
printf("father\n");
sleep(1);
}
}
exit(0);
}
运行结果:
3、进程替换的方法
在子进程中调用exec函数来执行另一个程序,调用后,新程序完全替换子进程进行程序执行,exec并不创建新进程,所以进程的pid不会发生变化。
(1)库函数
int execl(const char *path, const char *argv, ,...(char* )0);
int execv(const char * path,char * const argv[]);
int execle(const char * path,const char * argv,char * const envp[]);
int execlp(const char * file,const char * argv,…(char* )0);
int execvp(const char * file,char * const argv[]);
int execve(const char * path,char * const argv[],char * const envp[]);
(2)系统调用int execve(const char * path,char * const argv[],char * const envp[]);
其中,
path:要执行的程序路径。
file:要执行的程序名称。
argv:命令行参数的矢量数组。
envp:带有该参数的exec函数可以在调用时指定一个环境变量数组。
argv:程序的第0个参数,即程序名自身。
…:命令行参数列表。