文章目录
一、孤儿进程
父进程结束,子进程还未结束,然后子进程就交给init进程照顾
二、僵死进程(僵尸进程)
父进程未结束时,子进程已经结束,并且父进程未处理子进程的退出状态。
一个进程是由进程实体和进程控制块组成,进程结束时,实现释放进程的内容,再释放进程控制块。
**僵尸进程的形成过程:**进程已经结束,进程实体已经被释放,但是系统并没有释放对应的进程控制块(PCB);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{
pid_t pid = fork();
assert(pid != -1);
if(pid == 0)
{
printf("child start\n");
sleep(3);
printf("child end\n");
}
else
{
sleep(10);
}
return 0;
}
(1)产生僵死进程
注意:
2.4G的内核,一个PCB大约占用1.7k的内存,而父进程一般都是服务器进程,这种进程一般是不会结束的。如果没有处理僵尸进程,他就会一直存在;bash:每执行一个命令,bash都会fork一个子进程,如果出现大量的僵尸进程,内存就会被大量消耗,拖慢速度。
(2)处理僵尸进程
操作系统提供了一组系统调用:
pid_t wait(int *result);//返回值时处理子进程的pid,参数是进程退出的状态码
pid_t waitpid(pid_t pid, int *result, int option);
pid的值可以是如下图所示:
wait()方法是由父进程来调用,来获取其任意的一个子进程的退出状态(第一个退出的子进程),父进程wait()的次数与子进程的个数相等。
(3)解决上面产生的僵死进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
assert(pid != -1);
if(pid == 0)
{
printf("child start\n");
sleep(3);
printf("child end\n");
}
else
{
wait(NULL);
sleep(10);
}
return 0;
}
(4)结论
- 虽然父进程直接调用wait方法最终可以处理子进程的僵死状态,但是因为wait的阻塞,造成了父子进程时串行执行的,效率较低;
- 一次wait只能处理一个僵死进程,一个父进程能产生多少的僵死进程是无法预知的;
- 所以就引出信号这一概念
三、信号
(1)信号定义
- 信号是系统预先定义好某些特殊事件。信号可以被产生,也可以被接受,产生和接收的实体都是进程;
- 信号就是在进程间传递某些发生的事件;
- 信号就是宏,每个信号都有系统指定的一个进程收到该信号是的处理方式;
(2)修改信号的响应方式
1.默认 SIG_DFL
2.忽略 SIG_IGN
3.捕获(用户自定义处理方式)
__sighandler_t:就是函数指针的类型
修改信号响应方式的系统调用:
signum:信号类型,就是信号对应的宏值
handler:函数指针,可以是用户自定义的函数,也可以是SIG_DFL(默认),SIG_IGN(忽略);
返回值:返回的是信号原来的响应方式;
(3)信号的应用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
void func(int sign)
{
printf("sign is %d\n", sign);
printf("hello,world\n");
}
int main()
{
signal(SIGINT, func);
while(1)
{
sleep(1);
printf("main running\n");
}
return 0;
}
运行结果:
结论:
在信号函数的执行过程中,该信号如果多次触发,系统只会记录一次(多余的信号会被屏蔽),也就是说系统没有为该程序维护一个消息队列;
四、发送信号
(1)系统调用kill
int kill(pid_t pid, int signum)
pid 就是要给指定的进程发信号的进程号(大于0)
signum:发送的信号类型
返回值:发送成功返回 0 ;发送失败返回 -1;
(2)仿写一个kill命令
使用kill pid就可以杀死pid对应的进程
#include <stdio.h>
#include <signal.h>
#include <string.h>
//得到目标进程的进程号pid
pid_t getDesPid(int argc, char* argv[])
{
if(argc < 1)
return -1;
int pid = -1;
sscanf(argv[1], "%d", &pid);
return pid;
}
//发送SIGTERM信号
void sendSignal(pid_t pid)
{
if(pid <= 0)
{
printf("pid不合法\n");
return;
}
kill(pid, SIGTERM);
}
int main(int argc, char* argv[])
{
pid_t pid = getDesPid(argc, argv);
if( pid == -1)
{
printf("sig or pid err\n");
return 0;
}
sendSignal(pid);
return 0;
}
- 结果:
(3)使用信号异步处理僵死进程
- 父进程要处理僵尸进程,必须调用wait方法
- 子进程结束时,自动给父进程发送一个信号(SIGCHLD)。
- 父进程可以在信号处理函数中调用wait