本文来自个人博客:https://dunkwan.cn
函数sigaction
sigaction
函数的功能是检查或修改(或检查并修改)与指定信号向关联的处理动作。此函数取代了UNIX早期版本使用的signal
函数。
#include <signal.h>
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
返回值:若成功,返回0;若出错,返回-1。
参数
signo
是要检测或修改其具体动作的信号编号。若act
指针非空,则要修改其动作。如果oact
指针非空,则系统经由oact
指针返回该信号的上一个动作。
此函数中的sigaction
结构如下:
struct sigaction{
void (*sa_handler)(int); /* addr of signal handler */
/* or SIG_IGN, or SIG_DFL */
sigset_t sa_mask; /* additional signals to block */
int sa_flags; /* signal options */
/* alternate handler */
void (*sa_sigaction)(int, siginfo_t *, void *);
};
以下是用sigaction
函数实现signal
。
#include "../../include/apue.h"
/* Reliable version of signal(), using POSIX sigaction(). */
Sigfunc * signal(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(signo == SIGALRM){
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
}else{
act.sa_flags |= SA_RESTART;
}
if(sigaction(signo, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
以下是支持不可重启动的signal
函数版本。
#include "../../include/apue.h"
Sigfunc *signal_intr(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
if(sigaction(signo, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
函数sigsetjmp
和siglongjmp
在信号处理程序中进程非局部转移时应当使用这两个函数。
#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
返回值:若直接调用,返回0;若从siglongjmp调用返回,则返回非0。
void siglongjmp(sigjmp_buf env, int val);
这两个函数和
setjmp
、longjmp
之间的唯一区别是sigsetjmp
增加了一个参数。如果savemask
非0,则sigsetjmp
在env
中保存进程当前信号屏蔽字。调用siglongjmp
时,如果带非0savemask
的sigsetjmp
调用已经保存了env
,则siglongjmp
从其中恢复保存的信号屏蔽字。
测试示例:
#include "../../include/apue.h"
#include <setjmp.h>
#include <time.h>
#include <errno.h>
static void sig_usr1(int);
static void sig_alrm(int);
static sigjmp_buf jmpbuf;
void pr_mask(const char *);
static volatile sig_atomic_t canjump;
int main(void)
{
if(signal(SIGUSR1, sig_usr1) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if(signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal(SIGALRM) error");
pr_mask("starting main: ");
if(sigsetjmp(jmpbuf, 1)){
pr_mask("ending main: ");
exit(0);
}
canjump = 1;
for(;;)
pause();
}
static void sig_usr1(int signo)
{
time_t starttime;
if(canjump == 0)
return;
pr_mask("starting sig_usr1: ");
alarm(3);
starttime = time(NULL);
for(;;)
if(time(NULL) > starttime + 5)
break;
pr_mask("finishing sig_usr: ");
canjump = 0;
siglongjmp(jmpbuf, 1);
}
static void sig_alrm(int signo)
{
pr_mask("in sig_alrm: ");
}
void pr_mask(const char *str)
{
sigset_t sigset;
int errno_save;
errno_save = errno;
if(sigprocmask(0, NULL, &sigset) < 0){
err_ret("sigprocmask error");
}else {
printf("%s", str);
if(sigismember(&sigset, SIGINT))
printf(" SIGINT");
if(sigismember(&sigset, SIGQUIT))
printf(" SIGQUIT");
if(sigismember(&sigset, SIGUSR1))
printf(" SIGUSR1");
if(sigismember(&sigset, SIGALRM))
printf(" SIGALRM");
printf("\n");
}
errno = errno_save;
}
结果如下:
函数sigsuspend
如果希望对一个信号解除阻塞,然后pause
以等到以前被阻塞的信号发生,假定该信号是SIGINT
,实现这一点的并不正确的方法是:
sigset_t newmask, oldmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
/* critical region of code */
/* restore signal mask, which unblocks SIGINT */
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
/* window is open */
pause();
/* continue processing */
如果在信号被阻塞时, 产生了信号,那么该信号的传递就被推迟直到对它解除了阻塞。对应用程序而言,该信号好像发生在解除对SIGINT
的阻塞和pause
之间。如果发生了这种情况,或者如果在解除阻塞时刻和pause
之间确实发生了信号,那么就会产生问题。因为可能不会再见到该信号,所以从某种意义上讲,在此时间窗口中发生的信号丢失了。这样使得pause
永远阻塞。这是早期的不可靠信号机制的另一个问题。
sigsuspend
函数用于需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
返回值:-1,并将errno设置为EINTR。
进程的信号屏蔽字设置为由
sigmask
指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend
返回,并且该进程的信号屏蔽字设置为调用sigsuspend
之前的值。注意,此函数没有成功返回。如果它返回到调用者,则总是返回-1,并将errno设置为
EINTR
(表示一个被中断的系统调用)。
测试示例1:
保护代码临界区,使其不被特定信号中断的方法。
#include "../../include/apue.h"
static void sig_int(int);
void pr_mask(const char *);
int main(void)
{
sigset_t newmask, oldmask, waitmask;
pr_mask("program start: ");
if(signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
pr_mask("in critical region: ");
if(sigsuspend(&waitmask) != -1)
err_sys("sigsuspend error");
pr_mask("after return from sigsuspend: ");
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
pr_mask("program exit: ");
exit(0);
}
static void sig_int(int signo)
{
pr_mask("\nin sig_int: ");
}
void pr_mask(const char *str)
{
sigset_t sigset;
int errno_save;
errno_save = errno;
if(sigprocmask(0, NULL, &sigset) < 0){
err_ret("sigprocmask error");
}else {
printf("%s", str);
if(sigismember(&sigset, SIGINT))
printf(" SIGINT");
if(sigismember(&sigset, SIGQUIT))
printf(" SIGQUIT");
if(sigismember(&sigset, SIGUSR1))
printf(" SIGUSR1");
if(sigismember(&sigset, SIGALRM))
printf(" SIGALRM");
printf("\n");
}
errno = errno_save;
}
结果如下:
测试示例2:
等待一个信号处理程序设置一个全局变量。
#include "../../include/apue.h"
volatile sig_atomic_t quitflag;
static void sig_int(int signo)
{
signal(SIGINT, sig_int); /* additional code */
signal(SIGQUIT, sig_int);/* additional code */
if(signo == SIGINT)
printf("\ninterrupt\n");
else if(signo == SIGQUIT)
quitflag = 1;
}
int main(void)
{
sigset_t newmask, oldmask, zeromask;
if(signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
if(signal(SIGQUIT, sig_int) == SIG_ERR)
err_sys("signal(SIGQUIT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
while(quitflag == 0)
sigsuspend(&zeromask);
quitflag = 0;
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
return 0;
}
结果如下:
注意: 测试示例2中additional code
处为另加的代码,由于书中代码的运行版本与测试机版本可能存在很多差异,如若按源代码运行无法得到预期结果,所以特别添加此处代码,这才得到原书中所要达到的结果。
用信号实现父子进程之间的同步。
#include "apue.h"
static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;
static void
sig_usr(int signo) /* one signal handler for SIGUSR1 and SIGUSR2 */
{
sigflag = 1;
}
void
TELL_WAIT(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR2) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
/* Block SIGUSR1 and SIGUSR2, and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
}
void
TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2); /* tell parent we're done */
}
void
WAIT_PARENT(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for parent */
sigflag = 0;
/* Reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
void
TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1); /* tell child we're done */
}
void
WAIT_CHILD(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for child */
sigflag = 0;
/* Reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
函数abort
abort
函数用于使程序异常终止。
#include <stdlib.h>
void abort(void);
此函数不返回值。
以下是按POSIX.1说明实现的abort
函数。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void
abort(void) /* POSIX-style abort() function */
{
sigset_t mask;
struct sigaction action;
/* Caller can't ignore SIGABRT, if so reset to default */
sigaction(SIGABRT, NULL, &action);
if (action.sa_handler == SIG_IGN) {
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
}
if (action.sa_handler == SIG_DFL)
fflush(NULL); /* flush all open stdio streams */
/* Caller can't block SIGABRT; make sure it's unblocked */
sigfillset(&mask);
sigdelset(&mask, SIGABRT); /* mask has only SIGABRT turned off */
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT); /* send the signal */
/* If we're here, process caught SIGABRT and returned */
fflush(NULL); /* flush all open stdio streams */
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL); /* reset to default */
sigprocmask(SIG_SETMASK, &mask, NULL); /* just in case ... */
kill(getpid(), SIGABRT); /* and one more time */
exit(1); /* this should never be executed ... */
}