Linux C 信号处理
2015-12-04 23:33
387 查看
信号是传递给进程的一种事件通知,也称作软中断。比如在终端按下ctrl+c可以结束进程,实际上就是给进程发送了一个SIGINT信号;程序发生错误的时候,比如除数为0,非法访问内存的时候,内核也会向发生异常的那个进程传递错误信号。
当被发送的那个进程识别了信号并且采取了适当动作时,我们称信号交付给了进程,或者成进程接受了信号。
信号交付给进程后,进程可以通过一个函数捕获该信号并进行下一步操作。这个捕获函数就称作信号句柄
当一个信号已经生成,但还未交付,我们称这个信号是悬挂的。
每个进程都有一个信号屏蔽字标识当前被阻塞 交付的信号集合。如果某个信号对应的位被设置,则该型号是被阻塞的
给本进程发送名为sig的信号。如果安装了sig句柄函数,则raise()只会在句柄函数返回时才会返回
给pid号进程发送名为sig的信号,如果进程不存在,则返回-1。因此kill函数经常用来检测进程是否存在
sig指明是哪一种信号;handler可以是函数指针,也可以是如下的宏:
SIG_DFL:采取默认动作
SIG_IGN:忽略该信号
act和oact都是指向sigaction结构体的指针。act规定发送信号的动作;oact可以为NULL,如果不为NULL,则可用用来接收之前的动作对象。
sigaction是描述信号动作的一个结构,定义为:
sa_handler与signal()的第二个参数相同,指定一个信号句柄,其值也可以是SIG_DFL、SIG_IGN
sa_sigaction也是一个信号句柄,是一个更高级的信号句柄,只用当sa_flags 设为SA_SIGINFO时起作用。
可以看出,进程接收信号后会立即中断当前操作,转而执行信号句柄。
how的值指明如何改变信号屏蔽,它必须是下列的值之一:
SIG_BLOCK:阻塞set集中的信号
SIG_UNBLOCK:放开set集中的信号
SIG_SETMASK:用set所指的信号集进程的新信号集屏蔽
oset作为输出参数,可以为NULL,输出之前该进程屏蔽的信号集。
有关信号的术语:
当发生了一个需要引起进程注意的时间而导致信号出现,我们称对此进程发送信号当被发送的那个进程识别了信号并且采取了适当动作时,我们称信号交付给了进程,或者成进程接受了信号。
信号交付给进程后,进程可以通过一个函数捕获该信号并进行下一步操作。这个捕获函数就称作信号句柄
当一个信号已经生成,但还未交付,我们称这个信号是悬挂的。
每个进程都有一个信号屏蔽字标识当前被阻塞 交付的信号集合。如果某个信号对应的位被设置,则该型号是被阻塞的
如何发送信号?
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; }
运行结果
相关文章推荐
- Linux socket 初步
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- 如何组织构建多文件 C 语言程序(二)
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝