Linux下的进程与线程(二)—— 信号

时间:2022-12-27 18:46:32

Linux进程之间的通信:

本文主要讨论信号问题。

在Linux下的进程与线程(一)中提到,调度器可以用中断的方式调度进程。

然而,进程是怎么知道自己需要被调度了呢?是内核通过向进程发送信号,进程才得以知道的。

Linux系统的进程之间是通过信号来通信的。

程序员在Shell上显式地发送信号使用的是kill命令,原型如下:

kill -sigid [-]pid

其中, sigid指示的是信号的id,pid前若有-,则pid代表的为进程组id,否则pid代表的为进程id

kill函数也有相同的作用,原型如下:

int kill(pid_t pid, int sig);

一个使用kill命令的典型例子如下:

我们在Linux的Shell上运行一个前台作业的时候,Shell会fork一个新的进程A,然后在这个新的进程中运行我们的作业。该前台作业在运行时也可能会fork子进程a,子进程b。

当我们按下Ctrl+C,Shell会收到一个SIGINT信号,然后Shell进程会转发这个信号到A,然后A再将这个信号转发到子进程a,子进程b。

通过这种转发的方式,实现信号传递。

下图摘自CS:APP(图8-27),在本书的举例中,前台作业运行的是 ls | sort,图示一目了然:

Linux下的进程与线程(二)—— 信号

值得一提的是,当父进程创建子进程之后,父子进程的进程组ID(pgid)是相同的。

信号的等待与阻塞

如果一个进程正在处理类型为k的信号,那么此时当另一个k信号到的时候,进程的pending位向量(待处理信号集)的第k位会被设置。但这个刚到的k信号不会被立即处理,直到handler程序返回。

如果此时又来了一个k信号,那么由于此前pending位向量已被设置,所以这个信号会被丢弃。

一旦进程接收了信号k,那么内核就会清除pending的第k位。

blocked位向量(被阻塞信号集)维护着进程阻塞的信号,在这里设置的信号不会被进程接收处理。

所以我们知道,pending & ~blocked 的结果指示了进程将要去接收处理的信号集。

信号中断慢速系统调用

诸如read,wait,accept之类的慢速系统调用会阻塞进程,在特定的Solaris系统上,当被阻塞的进程被信号中断之后,将不会再返回。而是给用户一个错误条件,并对errno进行设置。

在Linux系统上,会自动重启被中断的系统调用.

为了使得编写的代码在Linux和Solaris系统上都能运行,需编写可移植性代码.

接收信号

进程接收每一个信号之后都有默认的行为,比如接收SIGKILL信号后,进程被终止。

可以设置signal函数,来修改部分信号的默认行为:

sighandler_t signal(int signum, sighandler_t handler)

其中,signum为要捕获的信号,handler为捕获信号后要执行的函数名称。