信号
信号是软件中断,提供了一种处理异步事件的方法,如在终端按下Ctrl+C
会产生SIGINT
中断信号。
信号产生
- 用户键入:必须在终端按下
Ctrl+C
和Ctrl+X
等会产生对应信号 - 硬件异常产生信号:除数为0、无效的内存引用等,如对执行一个无效内存引用的进程产生SIGSEGV信号
- 进程调用kill函数将信号发送给另一个进程或进程组,接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者是超级用户
- 用户可用kill命令将信号发送给其他进程
- 当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。如管道的读进程已终止,此时向管道写会产生SIGPIPE信号
信号处理
- 忽略。大多数信号都可使用该方式,担忧两种信号不能被忽略(SIGKILL和SIGSTOP)。
- 捕捉信号。需提前通知内核在某种信号发生时调用一个用户函数,在该函数中用户处理这个信号事件。
- 执行系统默认动作。大多数信号的系统默认动作是终止进程。
信号列举(以Linux2.4.22为例)
名字 | 说明 | 默认动作 | 名字 | 说明 | 默认动作 |
---|---|---|---|---|---|
SIGABRT | 异常终止(abort) | 终止+core | SIGALRM | 超时(alarm) | 终止 |
SIGBUS | 硬件故障 | 终止+core | SIGCHLD | 子进程状态改变 | 忽略 |
SIGCONT | 使暂停进程继续 | 继续/忽略 | SIGEMT | 硬件故障 | 终止+core |
SIGFPE | 算术异常 | 终止+core | SIGHUP | 连接断开 | 终止 |
SIGILL | 非法硬件指令 | 终止+core | SIGINT | 终端中断符 | 终止 |
SIGIO | 异步IO | 终止/忽略 | SIGIOT | 硬件故障 | 终止+core |
SIGKILL | 终止 | 终止 | SIGPIPE | 写至无读进程的管道 | 终止 |
SIGPOLL | 可轮询事件(poll) | 终止 | SIGPROF | 梗概事件超时 | 终止 |
SIGPWR | 电源失效/重启动作 | 终止/忽略 | SIGQUIT | 终端退出符 | 终止+core |
SIGSEGV | 无效内存引用 | 终止+core | SIGSTKFLT | 协处理器栈故障 | 终止 |
SIGSTOP | 停止 | 暂停进程 | SIGSYS | 无效系统调用 | 终止+core |
SIGTERM | 终止 | 终止 | SIGTRAP | 硬件故障 | 终止+core |
SIGTSTP | 终端停止符 | 暂停进程 | SIGTTIN | 后台读控制tty | 暂停进程 |
SIGTTOU | 后台写至控制tty | 暂停进程 | SIGURG | 紧急情况(套接字) | 忽略 |
SIGUSR1 | 用户定义的信号 | 终止 | SIGUSR2 | 用户定义的信号 | 终止 |
SIGVTALRM | 虚拟时间闹钟 | 终止 | SIGWINCH | 终端窗口大小改变 | 忽略 |
SIGXCPU | 超过CPU限制 | 终止+core | SIGXFSZ | 超过文件长度限制 | 终止+core |
信号相关函数
#include <signal.h>
// 成功返回信号以前的处理配置,出错返回SIG_ERR
// signo是上表的信号名
// func是常量SIG_IGN、SIG_DFL或当接收此信号后要调用的函数地址
void (*signal(int signo, void (*func)(int)))(int);
signal函数原型说明此函数需要两个参数,返回一个函数指针。
static void sig_usr(int signo) {
printf("received SIGUSR1\n");
}
int main()
{
if(signal(SIGUSR1, sig_usr) == SIG_ERR)
printf("can't catch SIGUSR1");
}
当执行一个程序时,所有信号的状态都是系统默认或忽略,当一个进程fork时,其子进程继承父进程的信号处理方式。
信号集
一个能表示多个信号的数据类型。
// 初始化set指向的信号集,清除其中所有信号
int sigemptyset(sigset_t *set);
// 初始化set,使其包括所有信号
int sigfillset(sigset_t *set);
// 添加
int sigaddset(sigset_t *set, int signo);
// 删除
int sigdelset(sigset_t *set, int signo);
// 用于判断是否包括指定信号
int sigismember(const sigset_t *set, int signo);
调用sigprocmask可以检测或更改其信号屏蔽字,用于阻塞指定信号传递到当前进程。
// how指示怎样用set修改当前信号屏蔽字
// SIG_BLOCK:当前信号屏蔽字并上set
// SIG_UNBLOCK:解除存在set中的当前信号屏蔽字
// SIG_SETMASK:用set替代当前信号屏蔽字
// oset非空则当前信号屏蔽字通过oset返回
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
// 返回当前信号集
int sigpending(sigset_t *set);
sigaction
函数用于检查或修改与指定信号相关联的处理动作,用以取代signal
函数
// signo是要检测或修改的信号编号;act指向要修改的动作,oact用于接收信号之前的动作
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
/* 其中sigaction结构:
struct sigaction {
void (*sa_handler)(int); /*信号处理函数地址*/
sigset_t sa_mask; /*阻塞的信号*/
int sa_flags;
/*可替代的信号处理函数地址*/
void (*sa_sigaction)(int, siginfo_t *, void *);
};
// 其中sa_mask会在嗲用信号捕捉函数前加到进程的信号屏蔽字中,当从信号捕捉函数返回时会将信号屏蔽字恢复
*/
可以阻塞选择的信号以保护不希望被信号中断的代码临界区,之后再解除阻塞,然后等待之前被阻塞的信号发生,但我们无法控制信号发生的时间窗口,可能在解除阻塞调用返回前就触发了信号,这是不可靠的,我们需要在一个原子操作中先恢复信号屏蔽字然后使进程休眠,可由sigsuspend
函数提供该功能。
// 将进程屏蔽字设置为sigmask指向的值,在捕捉到一个信号或发生了一个会终止该进程的信号前,该进程被挂起
// 如果捕捉到信号且从信号处理程序返回,则sigsuspend返回,并将进程信号屏蔽字恢复
int sigsuspend(const sigset_t *sigmask);
标签:core,set,int,终止,信号,进程,相关
From: https://www.cnblogs.com/songlh424/p/18454570