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中详细定义了信号的信号值,其内容如下:

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

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

 二、信号术语

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

三、信号使用

1、信号安装

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

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

(1)signal()

#include <signal.h>
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 <signal.h>
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 <sys/types.h>
#include <signal.h>
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 <sys/types.h>
#include <signal.h>
 int raise(int signo);

函数参数:

sigo:发送的信号编号值

返回值:

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

(3)alarm函数

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

#include <unistd.h>
#include <signal.h>
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 <stdlib.h>
void abort(void);

 (5)pause函数

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

#include<unist.h>
int pause(void);

 (6)sigqueue函数

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

#include <sys/types.h>
#include <signal.h>
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. 信号处理函数必须保证其可重入性

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiang木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值