学习目标:
僵尸进程和信号:
1、僵尸进程的概念
2、如何处理僵尸进程
3、信号
4、如何发送信号
5、如何利用信号异步处理僵尸进程
学习内容:
1、僵尸进程的概念
僵尸进程:父进程未结束,子进程已经结束,并且父进程未处理子进程的退出状态。
一个进程是由进程实体和进程控制块组成,进程结束时,实现释放进程实体的内容,再释放进程控制块。
僵尸进程:进程已经结束,进程实体已经释放,但是系统并没有释放对应的PCB
int main()
{
pid_t pid=fork();
assert(pid!=-1);
if(pid==0)
{
printf("child start\n");
sleep(3);
printf("end\n");
}
else
{
sleep(3);
}
exit(0);
}
2.4的内核,一个PCB大概1.7K。而父进程一般是服务器进程,一般是不会结束的。
如果没有处理僵尸进程,他就会一直存在。bash:每执行一个命令(大部分命令),bash都会fork一个子进程。
如果出现大量的僵尸进程,机器的内存就会被大量消耗。
2、如何处理僵尸进程
操作系统提供了一组系统调用:
pid_t wait(int *result); //返回值是处理的子进程的pid,参数是进程的退出状态
pid_t waitpid(pid_t pid,int *result,int option);
wait方法由父进程调用,来获取任意(不是随机,而是第一个退出的)一个子进程的退出状态;父进程调用wait的个数与子进程的个数一致。
wait方法的使用:
int main()
{
pid_t pid=fork();
assert(pid!=-1);
if(pid==0)
{
printf("child start\n");
sleep(3);
printf("end\n");
}
else
{
pid_t n=wait(NULL); //wait会阻塞当前的进程
if(n==-1)
printf("no\n");
else
{
printf("%d deal with\n",n);
}
sleep(5);
printf("father over\n");
}
exit(0);
}
结论:虽然wait方法的调用最终可以处理子进程的僵尸状态,但是wait是阻塞的,导致父子进程是串行进行的,效率较低。
一次wait只能处理一个僵尸进程,而父进程有多少个子进程是不能提前(写代码)知道的。
问题:在单核机器上,多进程或多线程有意义?
3、信号
-
概念:
信号是系统预先定义好的某些特殊事件。信号可以被产生,可以被接收,产生和接收的实体都是进程。
信号就是进程间传递某些发生的事件。
linux平台上信号的定义:
信号就是宏,每个信号都有系统指定的当一个进程接收到该信号的时候的处理方式。
-
信号的响应方式
信号的响应方式分类:默认 忽略 捕获(用户自定义)
SIG_ERR
SIG_DFL
SIG_IGN
__sighandler_t 就是一个函数指针
修改信号响应方式的系统调用方法:
-
信号的应用
void func(int sigh) { printf("sigh=%d\n",sigh); //sigh的值就是信号的类型值 printf("hello world\n"); signal(SIGINT,SIG_DFL); } int main() { signal(SIGINT,func); while(1) { sleep(1); printf("main...\n"); } }
-
信号的应用的深入
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #include<assert.h> #include <signal.h> void my_fun(int sign) { printf("my_fun start\n"); sleep(5); printf("my_fun end\n"); } int main() { signal(SIGINT, my_fun); while(1) { sleep(1); printf("main running\n"); } }
在信号的处理过程中,该信号如果多次触发,系统只能记录一次,信号处理函数,该信号会被屏蔽。
信号处理函数要尽可能快的执行,防止该信号被屏蔽。
4、如何发送信号
系统调用:
int kill(pid_t ,int signum);
pid :>0 指定的是给那个进程发送信号
==0
==-1
<-1
signum :发送的信号类型
返回值:成功返回0,失败返回-1
e.g
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <signal.h>
int main(int argc, char *argv[])
{
if(argc < 2) exit(0);
// 确定要发送的信号
int signum = SIGTERM;
for(int i = 1; i < argc; ++i)
{
if(strncmp(argv[i], "-", 1) == 0)
{
sscanf(argv[i]+1, "%d", &signum);
break;
}
}
if(signum <= 0 || signum > 31)
{
printf("signum %d is error\n", signum);
exit(0);
}
for(int i = 1; i < argc; ++i)
{
if(strncmp(argv[i], "-", 1) == 0) continue;
int pid = 0;
sscanf(argv[i], "%d", &pid);
if(-1 == kill(pid, signum))
{
printf("%d kill error\n", pid);
}
}
exit(0);
}
5、利用信号异步处理僵尸进程
/*
1、父进程要处理僵尸进程,必须调用wait方法
2、能不能让父进程在子进程结束后调用wait
3、父进程怎么知道子进程什么时候结束
4、子进程结束时,给父进程发送一个信号。父进程可以在信号处理函数中调用wait
不需要自己实现子进程给父进程发送信号,因为子进程结束时,会默认给父进程发送一个
信号(SIGCHLD)
*/
项目有待更新…