Linux服务器--信号

一、信号概述

1.发送信号

【1】

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

该函数将信号sig发送给目标进程;目标进程由pid指定,可能取值如下:

pid参数含义
pid>0信号发送给PID为pid 的进程。
pid=0信号发送给本进程组内的其他进程
pid = -1信号发送给除init进程外的所有进程,但发送者需要拥有对目标进程发送信号的权限
pid < -1信号发送给组ID为-pid的进程组中的所有成员

【2】

linux定义的信号值均大于0,如果sig取值为0则kill函数不发送任何信号。将sig值设为0时,可用来检测目标进程或进程组是否存在,因为检查在信号发送之前执行。但是这种检测方式是不可靠的,因为一方面由于进程PID的回绕,可能导致被检测到的pid不是我们所期望的pid,另一个方面,这种检测方法不是原子操作。

【3】函数成功时返回0,失败时返回-1并设置errno:

errno含义
EINVAL无效的信号
EPERM该进程没有权限发送信号给任何一个目标进程
ESRCH目标进程或进程组不存在

2.信号处理方式

【1】目标进程收到信号时,需定义接收函数来处理

#include <signal.h>
typedef void (*__sighandler_t) (int);

信号处理函数只带一个整型参数用来只是信号类型。

【2】除了用户自定义信号处理函数之外,bits/signum.h头文件中还定义了信号的两种其他处理函数。

#include <bits/signum.h>
#define SIG_DFL ((__sighandler_t) 0)
#define SIG_IGN ((__sighandler_t) 1)

SIG_IGN 表示忽略目标信号,SIG_DFL表示使用默认处理方式。

默认处理方式有:
结束进程
忽略信号
结束进程并生成核心转储文件
暂停进程
继续进程

3.linux信号

linux可用的信号都定义在bits/signum.h头文件中。

信号默认行为含义
SIGHUPTerm终止控制终端或进程
SIGINTTerm键盘产生的中断(Ctrl-C)
SIGQUITCore键盘产生的退出
SIGILLCore非法指令
SIGTRAPCore断点陷阱,用于调试
SIGABRT/SIGIOTCore进程调用abort函数时生成该信号
SIGBUSCore总线异常/错误内存访问
SIGFPECore浮点异常
SIGKILLTerm终止一个进程,该信号不可被捕获或者忽略
SIGUSR1Term用户自定义信号
SIGSEGVCore非法内存地址引用
SIGUSR2Term用户自定义信号
SIGPIPETerm向读端被关闭的管道或者socket连接张写数据
SIGALRMTerm时钟中断(闹钟)
SIGTERMTerm进程终止
SIGSTKFLTTerm协处理器栈错误
SIGCHLDIgn子进程退出或中断
SIGCONTCont启动被暂停的进程
SIGSTOPStop暂停进程
SIGSTPStop挂起进程
SIGTTINStop后台进程试图从终端读取输入
SIGTTOUStop后台进程试图往终端输出内容
SIGURGIgnsocket接收到紧急数据
SIGXCPUCore进程的CPU使用时间超过其软限制
SIGXFSZCore文件大小超过其软限制
SIGVTALRMTerm只统计本进程用户空间代码的运行时间
SIGPROFTerm统计用户代码和内核的运行时间
SIGWINCH/SIGPOLLIgn窗口尺寸调整
SIGIOTermI/O可用
SIGPWRTerm电源异常
SIGSYSCore非法系统调用
SIGUNUSEDCore保留,通常和SIGSYS效果相同

4.中断系统调用

 程序在执行处于阻塞状态的系统调用时接收到信号,并且为该信号设置了信号处理函数,则默认情况下系统调用将被中断,并且errno被设置为EINTR。

二、信号函数

1.signal系统调用

为一个信号设置处理函数

#include<signal.h>
_sighandler_t signal (int sig, _sighandler_t _handler)
  • sig参数指出要捕获的信号类型。_handler指定信号sig的处理函数。
  • 函数成功时返回一个函数指针,该函数指针也是_sighandler_t类型。返回值是前一次调用signal函数时传入的函数指针,或是信号sig对应的默认处理函数指针SIG_DEF
  • signal系统调用出错时返回SIG_ERR,并设置errno

2.sigaction系统调用

#include<signal.h>
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact);
  • sig指出要捕获的信号类型,act指定新的信号处理方式,oact输出信号先前的处理方式。act和oact都是sigaction结构体类型的指针,其定义如下:
struct sigaction
{
    #ifdef __USE_POSIX199309
        union
        {
             __sighandler_t _sa_handler;
             void (*_sa_sigaction)(int, struct siginfo *, void *);
        }
        _sigaction_handler;
    #define sa_handler   _sigaction_handler.sa_handler
    #define sa_sigaction    _sigaction_handler.sa_sigaction
    #else 
        _sighandler_t sa_handler;
    #endif
        _sigset_t sa_mask;
        int sa_flags;
        void (*sa_restorer) (void);
};

sa_hander 成员指定信号处理函数 sa_mask 成员设置进程的信号掩码 sa_flags成员设置程序收到信号时的行为。

三、信号集

1.信号集函数

【1】Linux使用数据结构sigset_t来表示一组信号,其定义如下:

#include <bits/sigset.h>
#define _SIGSET_NWORDS(1024 / (8 * sizeof(unsigned long int)))
typedef struct
{
         unsignedlong int __val[_SIGSET_NWORDS];
}__sigset_t;

sigset_t实际上是一个长整型数组,数组的每个元素的每个位表示一个信号。这种定义方式和文件描述符集fd_set类似。

【2】Linux提供了如下一组函数来设置、修改、删除和查询信号集:

#include <signal.h>
int sigemptyset(sigset_t *set);                       //清空信号集
int sigfillset(sigset_t *set);                        //在信号集中设置所有信号
int sigaddset(sigset_t *set, int signum);             //将信号signum添加到set信号集中
int sigdelset(sigset_t *set, int signum);             //删除信号
int sigismember(const sigset_t *set, intsignum);      //测试信号是否在信号集中

2.进程信号掩码

#include<signal.h>
intsigprocmask(int _how, _const sigset_t *_set, sigset_t *_oset);

_set参数指定新的信号掩码,_oset参数则输出原来的信号掩码(如果不为NULL的话)。如果_set参数不为NULL,则_how参数指定设置进程信号掩码的方式,其可选值如下表所示:

how参数含义
SIG_BLOCK新的进程掩码是当前值和set指定信号集的并集
SIG_UNBLOCK当前值和~set信号集的交际,因此set指定的信号将不被屏蔽
SIG_SETMASK直接设置为set

如果set为NULL,则进程信号掩码不变,此时可以利用_oset参数获得进程当前的信号掩码。
sigprocmask成功返回0,失败返回-1并设置errno。

3.被挂起的信号

设置进程掩码后,被屏蔽的信号将不能被进程接收。如果给进程发送一个被屏蔽的信号,则操作系统将该信号设置为进程的一个被挂起的信号。如果取消对被挂起信号的屏蔽,则它能立即被进程接收到。下面这个函数可以获得当前被挂起的信号集:

#include<signal.h>
intsigpending(sigset_t *set);

set参数保存被挂起的信号集。进程即使多次接收到同一个被挂起的信号,sigpending函数也只能反映一次。并且,当使用sigprocmask使被挂起的信号不被屏蔽时,该信号的处理函数也只能被触发一次。

4.统一事件源

信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。信号处理函数需要尽可能地执行完毕,以确保该信号不被屏蔽(为了一些竞态条件,信号在处理期间,系统不会再触发它)太久。一种典型的解决方案是:把信号的主要逻辑放到程序的主循环中,当信号处理函数被触发时,它只是简单的通知主循环程序接收信号,并把信号值传递给主循环,主循环再根据被接收到的信号值执行目标信号的逻辑代码。信号处理函数通常用使用管道来将信号“传递“给主循环:信号处理函数往管道的写端写入信号值,主循环则从管道的独断读出该信号值,这里主循环可以用I/O复用系统监听管道的读端文件描述符上的刻度事件。如此一来,信号事件就能和其他I/O事件一样被处理,即统一事件源。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值