栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux信号

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux信号

Linux信号

信号是由用户、系统或进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常,Linux中有很多种不同的信号。可以在终端输入kill -l 来查看Linux支持的信号,如下图:

Linux信号可由如下条件产生:

1、对于前台进程,用户可以通过输入特殊的终端字符来发送,比如输入Ctrl+C通常会给正在运行的进程发送一个中断信号。

2、系统异常。比如浮点异常和非法内存段访问。

3、系统状态变化。比如alarm定时器到期时将引起SIGALRM信号。

4、在终端运行kill命令或在程序中调用kill函数,例如:如果要杀死一个进程,我们可以使用 kill -9 pid 来杀死进程,-9表示发送9号信号,也就是SIGKILL信号,pid为发送信号的目标进程的进程ID;9号信号无法被忽略以及改变默认处理方式,因此发送9号信号一定能杀死进程。

1、在程序中发送信号

Linux中,给进程发送信号的系统调用为kill,定义如下:

#include 
#include 
int kill(pid_t pid, int sig);   // 给进程ID为pid的进程发送sig信号
pid参数含义
pid > 0信号发送给进程ID为pid的进程。
pid = 0信号发送给本进程组内的其它进程。
pid = -1信号发送给除init进程外的所有进程,需要有权限
pid < -1信号发送给ID为-pid的进程组中的所有成员

kill函数在成功时返回0, 失败时返回-1,并设置errno。

2、信号的处理方式

每个信号都有默认的处理方式,有的信号的默认处理方式是终止进程,有的是忽略信号以及结束进程并生成核心转储文件、暂停进程以及继续进程等。我们也可以在程序中修改信号的处理方式,注意:无法修改9号信号SIGKILL的默认处理方式,也无法忽略该信号。

信号处理函数的原型为:

#include 
typedef void (*sighandler_t)(int);

除了用户自定义信号处理函数外,bits/signum.h头文件还定义了信号的两种其它处理方式:

#include 
#define SIG_DFL ((sighandler_t) 0)            // 使用信号的默认处理方式
#define SIG_IGN ((sighandler_t) 1)			 // 忽略目标信号

要为一个信号设置处理函数,可以使用signal系统调用:

#include 
sighandler_t signal(int signum, sighandler_t handler);

signum为要捕获的信号类型,handler用于指定新的信号处理函数,也就是程序在收到signum类型信号后执行的回调函数。返回值为旧的信号处理函数。

例如下面代码:

代码中修改了2号信号SIGINT的处理函数,SIGINT信号可以由终端中按Ctrl+C来产生,因此当我们运行该程序后按Ctrl+C就会输出hello,world,为了结束该进程我们可以按Ctrl+,这个按键组合会产生SIGQUIT信号,该信号的默认处理函数是结束进程并产生转储文件。如下图所示:

#include 
#include 
#include 

// 信号处理回调函数
void handleSignal(int signum)
{
    std::cout << "hello, world!" << std::endl;

}

int main()
{
    signal(SIGINT, handleSignal);    //修改SIGINT信号的处理方式

    while(1)
    {
        sleep(1);
    }
    return 0;
}

在下面的代码中修改9号信号SIGKILL的信号处理函数进行一个测试:

#include 
#include 
#include 
#include 
#include 

typedef void (*sighandler_t)(int);

void sig_handler(int signum)
{
    std::cout << "hello, world!" << std::endl;
}

int main()
{
    sighandler_t ret = signal(SIGKILL, sig_handler);
    if( ret == SIG_ERR)
    {
        std::cout << "ignore SIGKILL failed, reason: " << strerror(errno) << std::endl;
    }

    while(1)
    {
        sleep(1);

    }

    return 0;
}

然后编译运行该程序,如下图:

发现signal系统调用失败了,打开另一个终端查看该进程ID,并发送9号信号给此进程,发现进程还是被杀死了。说明9号信号SIGKILL的默认处理动作是无法被修改的。而且该信号也是不能被忽略的。


sigaction系统调用:

#include 

// act为新的处理方式,odlact为旧的处理方式	
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
    void     (*sa_handler)(int);                            // 信号处理函数
    void     (*sa_sigaction)(int, siginfo_t *, void *);     // 第二种形式的信号处理函数
    // 屏蔽信号集,调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
    sigset_t   sa_mask;                                     
    int        sa_flags;                                    // 通常设置为0,表使用默认属性                              
    void     (*sa_restorer)(void);                          // 过时的元素,弃用
};

使用sigaction函数时可以指定在信号处理函数被调用的过程中要屏蔽的信号。

3、信号的机制

进程或用户A给一个进程B发送信号,B在收到信号之前执行自己的代码,当B进程收到信号后,不管程序执行到什么位置,都要暂停运行,去处理信号,也就是调用信号处理函数,处理完再继续执行。与硬件中断类似——异步模式。但信号是软件层面实现的中断,早期常被成为“软中断”。

信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。

​ 每个进程收到的所有信号,都是由内核负责发送的,内核处理。

内核实现信号捕捉过程:

4、信号集

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

#define _SIGSET_NWORDS (1024 / (8 * sizeof(unsigned long int)))

typedef struct
{
    unsigned long int _val[_SIGSET_NWORDS];
} sigset_t;

// sigset_t 实际上是一个长整型数组,数组中每个元素的每个位表示一个信号,Linux提供了如下一组函数来设置、修改、删除和查询信号集:
int sigemptyset(sigset_t *set);			            //将信号集清0		 成功:0;失败:-1
int sigfillset(sigset_t *set);				       //将信号集置1		  	成功:0;失败:-1
int sigaddset(sigset_t *set, int signum);		    //将信号加入信号集  	成功:0;失败:-1
int sigdelset(sigset_t *set, int signum);		    //将信号清出信号集   	成功:0;失败:-1
int sigismember(const sigset_t *set, int signum);   //判断某个信号是否在信号集中	返回值:在集合:1;不在:0;出错:-1  

进程信号掩码

​ 我们可以利用sigprocmask来设置进程的信号掩码。该函数可以用来设置进程要屏蔽的信号或者解除屏蔽的信号,其本质,读取或修改进程的信号掩码(也叫信号屏蔽集)(PCB中)。进程的PCB中保存了该进程的信号屏蔽集,该屏蔽集用来指示哪些信号在产生时会被屏蔽。

#include 
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);


	struct timeval it_value;    
};

 struct timeval {
	time_t      tv_sec;         
	suseconds_t tv_usec;        
};

8.4、SIGCHLD

子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/296461.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号