概述
信号是一种异步的通信方式(信号的到来是异步的,可以与管道对比下)
Linux系统中有许多信号,前31号信号都有一个特殊的名字,对应于一个特殊的时间,有时会将他们成为非实时信号,这些信号都是从UNIX系统继承下来的,他们还有个名称叫做不可靠信号。后面31个信号是Linux系统新增的实时信号,也被称作可靠信号,而非实时信号没有固定的次序
非实时信号特点
- 非实时信号不排队,信号的响应会相互嵌套
- 如果目标进程没有及时响应非实时信号,那么随后到达的该信号会被丢弃
- 每一个非实时信号都对应一个系统事件,当这个事件发生时,将产生这个信号。但并不是说产生了该信号就发生了该事件,任何信号都可以通过kill来产生信号
- 如果进程的挂起信号中有实时和非实时信号,那么进程会优先响应实时信号并且会从大到小依次响应,而非实时信号没有固定的次序
- 信号SIGKILL和SIGSTOP是两个特殊的信号,他们不能被忽略、阻塞或捕捉,只能按照缺省动作来响应
非实时信号特点
- 实时信号的响应次序按接收顺序排队,不嵌套
- 即使相同的实时信号被同时发送多次,也不会被丢弃,而会依次挨个响应
- 实时信号没有特殊的系统时间与之对应
信号的处理动作
- 如果该信号被阻塞,那么将该信号挂起,不对其做任何操作,等到解除对其阻塞为止。否则进入第二步
- 如果该信号被捕捉,那么进一步判断捕捉的类型
- 如果设置了响应函数,那么执行该响应函数
- 如果设置为忽略,那么直接丢弃该信号
- 执行该信号的缺省动作
-
每一个线程都使用一个 PCB(即 task_struct)来表示,因此 pending(不是指针)
就是一个线程单独私有的,当我们使用 pthread_kill( )给一个指定的线程发送某信号时,这些信号将会被存储在这个链队列中 -
signal 是一个指向线程共享的信号挂起队列相关结构体的指针,实际上,一个线程
组(即一个进程)中的所有线程中的 signal 指针都指向同一个结构体,当我们使用诸如 kill( )来给一个进程发送某信号的时候,这些信号将会被存储在 shared_pending 这个线程共享的链队列中(内核链表串起来)。表示当前悬而未决的信号。 如果一个进程中有超过 1 条线程,那么这些共享的挂起信号将会被随机的某条线程响应,为了能确保让一个指定的线程响应来自进程之外的、发送给整个进程的某信号,一般的做法如下:除 了指 定要 响 应某 信号 的线 程 外 , 其他 线 程对 这些 信号 设 置阻 塞。 即 使 sigprocmask( )或者 pthread_sigmask( )将这些需要阻塞的信号添加到信号阻塞掩码blocked 当中。 -
sighand 也是一个指针,因此也是进程中的所有线程共享的,他指向跟信号响应函
数相关的数据结构,结构体 struct sighand_struct{}中的数组 action 有 64 个元素,一一对应 Linux 系统支持的 64 个信号(其中 0 号信号是测试用的, 32 号和 33 号信号保留),每一个元素是一个 sigaction{}结构体,其成员就是标准 C 库函数 sigaction( )中的第二个参数的成员,可见,该函数相当于是一个应用层给内核设置信号响应策略的窗口。 -
对于一个task_struct来说,其中的blocked成员表示需要阻塞的信号,我们可以发现blocked并不是一个指针,所以该成员是线程私有的
sigwait
int sigwait(const sigset_t *set, int *sig);
同步地等待一个异步事件,调用sigwait前会将set参数指定的信号集阻塞,然后等待其中某个信号的到来,如果有的话,那么*sig就是该信号的值。
推荐在多线程化的进程中使用sigwait来处理所有信号,而不是使用异步信号处理程序。
实时行为
只有保证使用实时信号并且安装信号处理程序时必须给sigaction指定SA_SIGINFO标志,才能保证实时行为,其他情况下,视实现的不同而不同
实时的含义:
- 信号是排队的
- 当有多个实时信号被挂起的时候,俺么较小信号优先于较大信号响应
- 使用实时信号可以携带更多数据,额外的数据只有在SI_ASYNCIO,SI_MESGQ,SI_QUEUE,SI_USER时有效
- 一些新函数定义成使用实时信号工作
信号的阻塞掩码,会被继承给子进程
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
void catch_sig(int sig)
{
fprintf(stderr, "%d catch SIGQUIT.n", getpid());
}
int main(int argc, char **argv)
{
signal(SIGQUIT, catch_sig);
/*
** sigmask WILL be inherit to child process
** therefore, child process will block SIGQUIT
** until proceeding unblock action.
*/
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig, SIGQUIT);
sigprocmask(SIG_BLOCK, &sig, NULL);
pid_t pid;
pid = fork();
if(pid == 0)
{
fprintf(stderr, "child: %dn", getpid());
/*
** during this period of 20s, if this process
** receive signal SIGQUIT, it will be blocked.
** until child process call sigprocmask() to
** unblock it.
**
** NOTE: child process WILL inherit sigmask from
** their parent, and then won't associate with
** each other.
*/
int count = 10;
while(count > 0)
{
printf("%dn", count--);
sleep(1);
}
sigprocmask(SIG_UNBLOCK, &sig, NULL);
pause();
}
else if(pid > 0)
{
fprintf(stderr, "parent: %dn", getpid());
sigprocmask(SIG_UNBLOCK, &sig, NULL);
pause();
}
return 0;
}
挂起的信号是不会被子进程继承的
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
void catch_sig(int sig)
{
fprintf(stderr, "%d catch SIGQUIT.n", getpid());
}
int main(int argc, char **argv)
{
signal(SIGQUIT, catch_sig);
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig, SIGQUIT);
sigprocmask(SIG_BLOCK, &sig, NULL);
/*
** during this period, we will send SIGQUIT to the
** parent process, and SIGQUIT will pending on the
** sharing sig-queue, and this pending signal will
** NOT inherit to its child process.
*/
int count = 5;
while(count > 0)
{
printf("count: %dn", count--);
sleep(1);
}
pid_t pid;
pid = fork();
if(pid == 0)
{
fprintf(stderr, "child: %dn", getpid());
/*
** unblock the signal SIGQUIT, child process
** will NOT call the function catch_sig, this
** is because the pending signals will be
** cleared when process fork() a new process.
**
** NOTE: pending signals will NOT be inherit
*/
sigprocmask(SIG_UNBLOCK, &sig, NULL);
pause();
}
else if(pid > 0)
{
fprintf(stderr, "parent: %dn", getpid());
sigprocmask(SIG_UNBLOCK, &sig, NULL);
pause();
}
return 0;
}
不同的信号会相互嵌套,但是已经在响应的相同信号不会嵌套
当我们在执行一个信号的处理函数的时候,然后发出另一个信号,那么就会嵌套,但是如果在嵌套的时候在发送第一个信号,那么就不会嵌套(这个是可以控制的),即除正在处理的信号之外,其他的信号会嵌套,但是同一个信号会顺序处理。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void sighand(int sig)
{
if(sig == SIGUSR1)
{
char str[] = "11111";
int i = 0;
while(str[i] != '