多任务并发环境中,每个进程之间相互独立,不同的进程运行在各自不同的内存空间中,因此,进程之间的信息传递不能通过变量或者其他数据直接进行沟通。所以有了可在进程间通讯的方法:信号、管道、共享内存、信号量、消息队列、还有socket。
信号:信号其实只是一种通知机制,如:子进程给父进程发送SIGCHLD信号,告知父进程,自己结束了。
都是系统预先定义好的某些特定事件,收到该信号的进程会采取相应的行动。信号可以被产生,可以被接受。信号产生和接受的主题都是进程。
信号的使用:
①发送信号:int kill(pid_t pid,int signum);
pid:信号要发给进程的进程号 signum:信号值
kill函数把参数signum指定的信号发送给有参数pid给出的进程号指定的进程。
成功时返回0,失败返回-1。
pid > 0:向pid指定的进程发送信号
pid == 0:发送给同一进程组的所有进程
pid == -1:发送给所有进程,除系统进程
pid < -1:将信号传给进程组识别码为pid绝对值的所有进程
②信号如何接受
在进程PCB中,有记录信号的信息:
long signal; //进程接收到的信号
struct sigaction sigaction[32]; //记录信号的响应方式
signal的初始值为:00000000 00000000 00000000 00000000
当记录具体某一信号时,则将 signal |= 1<<(signum - 1) 位来记录这个信号。
struct sigaction中sa_handler来记录信号的响应方式。
③修改信号的响应方式:signal(int signum,void(*func)(int) fun);
信号的三种处理方式:
- 忽略 SIG_DFI 表示接收到此信号后的动作是系统默认动作
- 默认 SIG_IGN 向内核表示忽略此信号(SIGKILL和SIGSTOP不能忽略)
- 自定义(捕获) 自己写的函数 信号发生时调用该函数。
signal函数的实现原理:
Int sys_signal(int signum ,long handler)
{
If(signum < 1 || signum > 32 || signum == SIGKILL)
return -1;
Current->sigation[signum - 1].sa_handle = handler;
return 0;
}
信号发生后,设置PCB中signal成员变量的值,通过signal的值计算出收到的信号的值signum。然后再去调用相应的信号处理函数(Call PCB->sigaction[signum - 1].sa_handler)。
通过signal即可修改信号的响应方式:
SIGINT:当用户按中断键(一般为delete或ctrl+c)时,终端驱动程序产生此信号并发送至前台进程组中的每一个进程。进程则会被终止(默认方式)。
这个例子中,第一次收到信号,进入信号处理函数,主函数不会同时执行,主函数会等待信号处理函数执行完后才能后在接着运行(异步)。如果信号处理函数正在被执行,信号再次触发(ctrl+c)则第二次触发的信号会被放到信号的等待队列中,直到之前的信号处理函数执行完成,才会被处理。
信号的处理过程:
A进程要对某个信号做处理,则先使用signal函数对这个信号的响应方式进程修改,而它的sigaction数组中将记录这个信号的处理函数:
sigaction[signum-1].sa_handler(signum)
信号编号从1开始,所以存到sigaction[sig-1]而不是sigaction[sig]
然后就继续运行其他的代码。当另一个进程B对A通过kill发送了这个信号后,则A进程将去自己的记录信号响应方式的数组中查找这个信号对应的操作去处理这个信号。