Linux开发(十):多进程通信与同步---信号

信号是进程间通信机制中唯一的异步通信机制,在实现上是一种软中断,信号可以导致一个正在运行的进程被中断,进而处理一个突发事件。

目录

一、信号表

 二、信号术语

三、信号使用

1、信号安装

(1)signal()

 (2)sigaction()

2、信号发送

(1)kill函数

(2)raise函数

(3)alarm函数

 (4)abort函数

 (5)pause函数

 (6)sigqueue函数

3、信号集操作函数

4、信号阻塞函数

四、注意事项


一、信号表

Linux在/usr/include/asm-generic/signal.h中详细定义了信号的信号值,其内容如下:

取值 名称 解释 默认动作
1 SIGHUP 挂起
2 SIGINT 中断
3 SIGQUIT 退出
4 SIGILL 非法指令
5 SIGTRAP 断点或陷阱指令
6 SIGABRT abort发出的信号
7 SIGBUS 非法内存访问
8 SIGFPE 浮点异常
9 SIGKILL kill信号 不能被忽略、处理和阻塞
10 SIGUSR1 用户信号1
11 SIGSEGV 无效内存访问
12 SIGUSR2 用户信号2
13 SIGPIPE 管道破损,没有读端的管道写数据
14 SIGALRM alarm发出的信号
15 SIGTERM 终止信号
16 SIGSTKFLT 栈溢出
17 SIGCHLD 子进程退出 默认忽略
18 SIGCONT 进程继续
19 SIGSTOP 进程停止 不能被忽略、处理和阻塞
20 SIGTSTP 进程停止
21 SIGTTIN 进程停止,后台进程从终端读数据时
22 SIGTTOU 进程停止,后台进程想终端写数据时
23 SIGURG I/O有紧急数据到达当前进程 默认忽略
24 SIGXCPU 进程的CPU时间片到期
25 SIGXFSZ 文件大小的超出上限
26 SIGVTALRM 虚拟时钟超时
27 SIGPROF profile时钟超时
28 SIGWINCH 窗口大小改变 默认忽略
29 SIGIO I/O相关
30 SIGPWR 关机 默认忽略
31 SIGSYS 系统调用异常

也可以使用kill -l来查看信号的名字以及序号:

Linux开发(十):多进程通信与同步---信号_第1张图片

 二、信号术语

  •     发送信号:产生信号,并将信号发送出去。
  •     安装中断:设置信号到来时,不是执行默认操作,而是执行自己自定义的代码。
  •     递送信号:一个信号被操作系统发送到目标进程。
  •     捕获信号:被递送的信号在目标进程中引起某段代码的执行。
  •     屏蔽信号:进程告诉操作系统暂时不接收某些信号。
  •     忽略信号:进程被递送到目标进程,但目标进程不做任何操作,直接丢弃
  •    未决信号:信号已经产生,但目标进程暂时屏蔽了该信号,从而导致不能被目标进程捕获的信号。
  •     可靠信号:信号值大于32的为信号
  •     不可靠信号:信号值小于32的信号

三、信号使用

1、信号安装

进程处理某个信号前,需要先在进程中安装此信号。安装过程主要是建立信号值和进程对相应信息值的动作。信号安装函数有两个:

  • signal():不支持信号传递信息,主要用于非实时信号安装;
  • sigaction():支持信号传递信息,可用于所有信号安装;

(1)signal()

#include 
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

函数参数:

signum:指定信号的值

handler:指定针对该信号值的处理,有以下选项可供选择:

  • SIG_IGN:忽略该信号
  • SIG_DFL:系统默认方式处理信号
  • 函数指针:自定义处理

返回值:

成功则返回handler值,失败则返回SIG_ERR

 (2)sigaction()

#include 
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

函数参数:

signum:指定信号的值

act:设置对signum信号的新处理方式,,为NULL,则表示使用默认方式进行处理。struct sigaction结构体内容为:

struct sigaction {
    void (*sa_handler)(int); 
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}
  • _sa_handler:同signal()的参数handler,代表新的信号处理函数
  • _sa_sigaction:新的信号安装机制,处理函数被调用的时候,不但可以得到信号编号,而且可以获悉被调用的原因以及产生问题的上下文的相关信息。。第一个参数为信号值,第三个参数没有使用,第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值。
  • sa_mask:用来设置在处理该信号时,暂时将sa_mask 指定的信号集搁置
  • sa_flags:用来设置信号处理的其他相关操作,下列的数值可用:
    • SA_RESTART:使被信号打断的系统调用自动重新发起。
    • SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
    • SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
    • SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
    • SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
    • SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。

oldact:用以存储原来对信号的处理方式,不关心则设置为NULL

返回值:

成功返回0,失败返回-1

2、信号发送

(1)kill函数

向指定进程发送指定信号

#include 
#include 
int kill(pid_t pid, int sig);

函数参数:

pid:要发送的目标进程ID:

  • pid > 0:将发送个该 pid 的进程
  • pid == 0:将会把信号发送给与发送进程属于同一进程组的所有进程,并且发送进程具有权限想这些进程发送信号。
  • pid < 0:将信号发送给进程组ID 为 pid 的绝对值得,并且发送进程具有权限向其发送信号的所有进程
  • pid == -1:将该信号发送给发送进程的有权限向他发送信号的所有进程。(不包括系统进程集中的进程)

sig:发送的信号编号值

返回值:

成功则返回0,失败则返回SIG_ERR

(2)raise函数

向自身发送指定信号。

#include 
#include 
 int raise(int signo);

函数参数:

sigo:发送的信号编号值

返回值:

成功则返回0,失败则返回SIG_ERR

(3)alarm函数

指定内核为调用进程在指定的seconds秒后发出一个SIGALRM的信号

#include 
#include 
unsigned int alarm(unsigned int seconds);
__useconds_t ualarm(__useconds_t value,__useconds_t interval);

函数参数:

seconds:指定的seconds秒后发出一个SIGALRM的信号:

  • seconds=0:取消之前的设置
  • seconds>0:seconds秒后发出一个SIGALRM的信号

value:指定函数将在value 微妙内产生SIGALARM信号

interval:指定在之后每interval微妙时,再次产生SIGALARM信号

返回值:

函数第一次调用时,执行成功返回0,失败返回-1。
若之前已有调用,再次调用函数则表示重置时间间隔,执行成功则返回距上次信号发送,剩余间隔时间。

 (4)abort函数

向自身发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。

#include 
void abort(void);

 (5)pause函数

使进程挂起,直到捕捉到一个信号为止

#include
int pause(void);

 (6)sigqueue函数

主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用

#include 
#include 
int sigqueue(pid_t pid, int sig, const union sigval val);

函数参数:

pid:指定接收信号的进程id

sig:发送的信号值

val:信号的附加参数,联合体union sigval为:

typedef union sigval
{ 
    int sival_int; 
    void *sival_ptr; 
}sigval_t; 

返回值:

成功则返回0,失败则返回-1

3、信号集操作函数

/*清空set代表的信号集*/
int sigemptyset(sigset_t* set)

/*添加信号sig到信号集set中*/
int sigaddset(sigset_t* set,int sig);

/*从信号集set中删除信号sig*/
int sigdelset(sigset_t* set,int sig);

/*检测信号sig是否在信号集set中*/
int sigismember(sigset_t* set,int sig);

/*检测信号集set是否为空信号集*/
int sigisemptyset(sigset_t* set);

/*将信号集left与信号集right按照逻辑与的方式合并到信号集set中*/
int sigandset(sigset_t* set,sigset_t* left,sigset_t* right);

/*将信号集left与信号集right按照逻辑或的方式合并到信号集set中*/
int sigandset(sigset_t* set,sigset_t* left,sigset_t* right);

4、信号阻塞函数

// 不同how参数,实现不同功能:
//     SIG_BLOCK:将set指向信号集中的信号,添加到进程阻塞信号集;
//     SIG_UNBLOCK:将set指向信号集中的信号,从进程阻塞信号集删除;
//     SIG_SETMASK:将set指向信号集中的信号,设置成进程阻塞信号集;
sigprocmask(int how, const sigset_t *set, sigset_t *oldset));

// 获取已发送到进程,却被阻塞的所有信号
sigpending(sigset_t *set));

// 用mask代替进程的原有掩码,并暂停进程执行,直到收到信号再恢复原有掩码并继续执行进程。
sigsuspend(const sigset_t *mask));

四、注意事项

  1. 一个进程在执行exec()函数时,因为原先的信号捕捉函数的地址在新程序文件内已无意义,所以,新程序会将原先程序的信号全部改为默认处理方式
  2. fork()时,子进程会继承父进程的信号处理方式
  3. 通常将信号编号为0的信号称为空信号,用与kill函数配合用来检测某进程是否存在(非原子操作)
  4. 在进入信号处理程序的时候,我们总是先屏蔽当前的信号,以避免在执行这个信号处理程序的时候又收到了一模一样的信号。
  5. 使用排队信号必须满足以下3点:
    1. 使用sigaction函数安装信号处理函数时指定SA_SIGINFO标志,如果没有这个标志,信号会延迟
    2. 在sigaction结构的sa_sigaction成员中,而不是通常的sa_handler字段,提供信号处理函数
    3. 使用sigqueue()函数发送信号
  6. SIGCHLD在子进程退出时,将发送该信号给父进程,父进程可根据该信号完成对子进程PCB资源的回收
  7. SIGSTOP、SIGKILL不能被屏蔽、安装
  8. SIGSTOP和SIGCOUNT是配对的,一个进程收到SIGSTOP后会暂停执行,并屏蔽除SIGKILL外所有信号,在收到SIGCOUNT后,才会继续执行
  9. 信号可以唤醒处于sleep()的进程
  10. 信号处理函数必须保证其可重入性

你可能感兴趣的:(Linux,信号,linux,通信)