您的位置:首页 > 运维架构 > Linux

Linux C 信号处理

2015-12-04 23:33 387 查看
信号是传递给进程的一种事件通知,也称作软中断。比如在终端按下ctrl+c可以结束进程,实际上就是给进程发送了一个SIGINT信号;程序发生错误的时候,比如除数为0,非法访问内存的时候,内核也会向发生异常的那个进程传递错误信号。

有关信号的术语:

当发生了一个需要引起进程注意的时间而导致信号出现,我们称对此进程发送信号

当被发送的那个进程识别了信号并且采取了适当动作时,我们称信号交付给了进程,或者成进程接受了信号。

信号交付给进程后,进程可以通过一个函数捕获该信号并进行下一步操作。这个捕获函数就称作信号句柄

当一个信号已经生成,但还未交付,我们称这个信号是悬挂的。

每个进程都有一个信号屏蔽字标识当前被阻塞 交付的信号集合。如果某个信号对应的位被设置,则该型号是被阻塞

如何发送信号?

raise函数:

#include <signal.h>
int raise(int sig);


给本进程发送名为sig的信号。如果安装了sig句柄函数,则raise()只会在句柄函数返回时才会返回

kill函数:

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


给pid号进程发送名为sig的信号,如果进程不存在,则返回-1。因此kill函数经常用来检测进程是否存在

如何接受信号?

signal函数

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t handler);


sig指明是哪一种信号;handler可以是函数指针,也可以是如下的宏:

SIG_DFL:采取默认动作

SIG_IGN:忽略该信号

sigaction函数

signal函数缺乏可靠性,且功能局限。一般采用sigaction函数。

#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);


act和oact都是指向sigaction结构体的指针。act规定发送信号的动作;oact可以为NULL,如果不为NULL,则可用用来接收之前的动作对象。

sigaction是描述信号动作的一个结构,定义为:

struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t*, void*);
sigset_t sa_mask;
int sa_flags;
};


sa_handler与signal()的第二个参数相同,指定一个信号句柄,其值也可以是SIG_DFL、SIG_IGN

sa_sigaction也是一个信号句柄,是一个更高级的信号句柄,只用当sa_flags 设为SA_SIGINFO时起作用。

示例代码

//信号的中断测试

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Handler(int sig, siginfo_t *info, void *context)
{
if (sig == SIGALRM)
{
for (int i = 0; i < 10; i++)
{
printf("CallBack is running...\n");
sleep(1);
}
}
}

int main()
{
struct sigaction sa;
sa.sa_sigaction = Handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);

if (sigaction(SIGALRM, &sa, NULL) == -1)   //设定闹钟信号
{
printf("error\n");
return -1;
}

alarm(2);     //两秒后将发送闹钟信号

for (int i = 0; i < 10; i++)
{
printf("main is running...\n");
sleep(1);
}

return 0;
}


运行结果:



可以看出,进程接收信号后会立即中断当前操作,转而执行信号句柄。

设置信号屏蔽

每一个进程都有一个信号屏蔽,进程在创建是继承父进程的信号屏蔽。

sigset_t类型及其操作

sigset_t称为信号集类型,是多个型号的集合。它只能用一下几个函数操纵:

int sigemptyset(sigset_t *set);                     //置为空集
int sigfillset(sigset_t *set);                      //使之包含所有信号
int sigaddset(sigset_t *set, int signum);               //添加信号
int sigdelset(sigset_t *set, int signum);               //删除信号
int sigismember(const sigset_t *set, int signum);       //测试信号是否在信号集中


信号屏蔽函数sigprocmask()

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);


how的值指明如何改变信号屏蔽,它必须是下列的值之一:

SIG_BLOCK:阻塞set集中的信号

SIG_UNBLOCK:放开set集中的信号

SIG_SETMASK:用set所指的信号集进程的新信号集屏蔽

oset作为输出参数,可以为NULL,输出之前该进程屏蔽的信号集。

示例代码

//信号屏蔽测试

#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void SignalHandler(int sig)
{
if (sig == SIGUSR1)
{
printf("signal1 is clicked\n");
}
else if (sig == SIGUSR2)
{
printf("signal2 is clicked\n");
}
}

int main()
{
struct sigaction sa;
sa.sa_handler = SignalHandler;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);

if (sigaction(SIGUSR1, &sa, NULL) == -1){
printf("error\n");
return -1;
}
if (sigaction(SIGUSR2, &sa, NULL) == -1){
printf("error\n");
return -1;
}
/*  这几行代码回导致进程屏蔽SIGINT和SIGQUIT从而无法退出
sigset_t mask_quit;
sigemptyset(&mask_quit);
sigaddset(&mask_quit, SIGINT);
sigaddset(&mask_quit, SIGQUIT);
if (sigprocmask(SIG_BLOCK, &mask_quit, NULL) < 0)
{
printf("mask quit error\n");
return -1;
}
*/

sigset_t st;
sigemptyset(&st);
sigaddset(&st, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &st, NULL) < 0)
{
printf("mask sig2 error\n");
return -1;
}

printf("raise SIGUSR1\n");
raise(SIGUSR1);
printf("raise SIGUSR2\n");
raise(SIGUSR2);

while (1);
return 0;
}


运行结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 linux 信号