硬中断是外部设备对CPU的中断,软中断通常是硬中断服务程序对内核的中断,信号则是由内核(或其他进程)对某个进程的中断。
1信号的概念
信号是软件中断,全称为软中断信号,信号提供了一种处理异步事件的方法。信号用来通知进程发生了异步事件。进程间可互相通过kill函数发送信号,内核也可以因为内部事件而给进程发送信号。信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
信号都以SIG开头,在头文件<signal.h>中,这些信号都被定义为正整数(信号编号)。其中,不存在编号为0的信号。(kill函数对信号编号为0有特殊的应用)。
-
产生信号的条件
1)当用户按某些终端键时,引发终端产生的信号。如按Ctrl+C产生中断信号。
2)硬件异常产生信号:除数为0,无效的内存引用等等。这些条件通常由硬件检测到,并将其通知内核,内核为该条件发生时正在运行的进程产生适当的信号。
3)当检测到某种软件条件已经发生,并应将其通知有关进程时也产生信号。如,进程所设置的闹钟时钟超时就会产生SIGALRM信号,网络连接上传来带外数据时就会产生SIGURG信号。
4)进程调用kill函数可将信号发送给另一个进程或进程组。限制:接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者必须是超级用户。
2.信号的处理
信号是异步事件,故而进程不知道信号何时出现,只能通过内核在某信号出现时,作出处理。
内核按照三种方式之一进行处理:
1)忽略信号。大多数信号可以这种方式处理,但有两种信号不能忽略:SIGKILL和SIGSTOP。
2)捕捉信号。通过signal函数通知内核在某种信号发生时调用一个用户函数,以执行用户希望对这种时间进行的处理。但有两种信号不能捕捉:SIGKILL和SIGSTOP。
3)执行系统默认动作。
在进程表的表项中,有一个信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。
2 signal函数
头文件 | #include <signal.h> |
函数原型 | void (*signal(int signo, void (*func)(int)))(int); |
参数 | signo:信号名 func:可以为常量SIG_ERR、SIG_IGN或SIG_DFL,也可以是接到此信号后要调用的函数的地址。 常量定义: #define SIG_ERR (void (*)())-1 #define SIG_DFL (void (*)())0 #define SIG_IGN (void (*)())1 |
返回 | 成功返回信号以前的处理配置,出错返回SIG_ERR。 |
功能 | 设置信号处理程序 |
函数等价 | typedef void sigfunc(int); sigfunc *signal(int, sigfunc *); |
【1】程序启动
当执行一个程序时,所有信号的状态都是系统默认或忽略。通常所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号。
【2】进程创建
当一个进程调用fork时,其子进程继承父进程的信号处理方式。
#include "apue.h"
static void sig_usr(int signo)
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
}
int main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
{
err_sys("can't catch SIGUSR1");
}
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
{
err_sys("can't catch SIGUSR2");
}
for (;;)
pause();
}
[root]# ./a.out &
[2] 2824
[root]# kill -USR1 2824
[root]# received SIGUSR1
[root]# kill -USR2 2824
[root]# received SIGUSR2
[root]# kill 2824
[2]- 已终止 ./a.out
[root]#
3信号术语
产生信号时,内核通常在进程表中设置一个某种形式的标志,当对信号采取了这种动作时,称向进程递送一个信号。
在信号产生(generation)和递送(delivery)之间的时间间隔内,称信号是未决(pending)的。
进程可以选择阻塞(block)某个信号,如果对该信号的动作是系统默认动作或捕捉该信号,则此信号将保持在未决状态,直到进程对此信号解除阻塞或此信号的动作更改为忽略。
所在头文件/linclude/linux/sched.h struct task_struct { …… /* signal handlers */ struct signal_struct *signal; /*指向进程的信号描述符的指针*/ struct sighand_struct *sighand; /*指向进程的信号处理程序描述符的指针*/
sigset_t blocked, real_blocked; /*信号屏蔽字,当前要阻塞的信号,每个信号对于一位*/ struct sigpending pending;/*未决的信号,即待处理的私有挂起信号*/
unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; …. } |
上面结构体中的字段blocked和pending表示信号的阻塞与未决状态。
字段pending:信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递送才清除该标志。
字段blocked:信号屏蔽字,规定了当前要阻塞递送到该进程的信号集。对于每种可能的信号,该屏蔽字都有一位与之对应。对于某种信号,若其对应位已设置,则它当前是被阻塞的。
4 kill和raise函数
头文件 | #include <signal.h> |
函数原型 | int kill(pid_t pid, int signo); |
参数 | signo:信号名 如果signo为0,则kill仍执行政策的错误检查,但不发送信号。这常被用来确定一个特定进程是否仍旧存在。如果向一个并不存在的进程发送空信号,则kill返回-1,并将errno设置为ESRCH pid: 1) pid>0,将信号发送给进程ID为pid的进程 2) pid==0,将该信号发送给与发送进程属于同一进程组的所有进程。 3) pid<0,将该信号发送给其进程组ID等于pid的绝对值。 4) pid==-1,将该信号发送给发送进程有权限向它们发送信号的系统上的所有进程。 |
返回 | 成功返回0,出错返回-1。 |
功能 | 将信号发送给进程或进程组 |
函数等价 | 调用int raise(int signo)等价于调用kill(getpid(), signo),即进程向自身发送信号。 |
5 alarm和pause函数
头文件 | #include <unistd.h> |
函数原型 | unsigned int alarm(unsigned int seconds); |
参数 | seconds:秒数,经过了指定的seconds秒数后会产生信号SIGALRM。 |
返回 | 返回值:0或以前设置的闹钟时间的余留秒数 |
功能 | 设置一个计时器,在将来某个指定的时间该计时器会超时。当计时器超时时,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止调用该alarm函数的进程。 |
说明 | 每个进程只能有一个闹钟时钟。 1) 调用alarm之前,已经调用过,且未超时,则本次alarm返回值为上次的余留值,闹钟时间为新的时间值。 2) 调用alarm之前,已经调用过,且未超时,若本次alarm的参数为0,则取消以前的闹钟时间,其余留值为本次alarm的返回值。即alarm(0)可用来取消之前设置的闹钟。 |
头文件 | #include <unistd.h> |
函数原型 | int pause(void); |
参数 | void |
返回 | 返回值:-1,并将errno设置为EINTR |
功能 | 使调用进程挂起直至捕捉到一个信号。 |
说明 | 只有执行了一个信号处理程序并从其返回时,pause才返回。在这种情况下,pause返回-1,并将errno设置为EINTR |
/*sleep函数简单而不完整的实现 V1*/
#include <signal.h>
#include <unistd.h>
static void sig_alrm(int signo)
{
/*nothing to do, just return to wake up the pause*/
}
unsigned int sleep1(unsigned int nsecs)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)/*问题2:修改了对SIGALRM信号处理的配置,后面需要恢复其原先的配置*/
return nsecs;
/*问题1:进程只能有一个闹钟时间,在该alarm(nsecs)调用时,需要对之前设置的闹钟进行处理*/
alarm(nsecs);
pause();/*问题3:alarm和pause之间有一个竞争条件。系统繁忙情况下,可能alarm在调用pause之前超时并调用处理程序。使得pause捕捉不到信号一直挂起*/
return alarm(0);/*取消alarm(nsecs)闹钟,并将其剩余时间返回*/
}
/*具有超时限制的read的调用*/
#include "apue.h"
static void sig_alrm(int);
int main(void)
{
int n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
err_sys("signal error");
alarm(10);
if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
err_sys("my read error");
alarm(0);/*在10s内完成读的话,则取消上次的闹钟时钟*/
write(STDOUT_FILENO, line, n);
exit(0);
}
static void sig_alrm(int signo)
{
/*nothing to do, just return to wake up the pause*/
}
/*sleep的简单而不完整的实现 V2:解决V1中的问题3*/
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
static jmp_buf env_alrm;
static void sig_alrm(int signo)
{
longjmp(env_alrm, 1);
}
unsigned int sleep(unsigned int nsecs)
{
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
return nsecs;
if (setjmp(env_alrm) == 0)
{
alarm(nsecs);
pause();
}
return alarm(0);
}
6信号集
多个信号即是信号集,因为信号种类数目可能超过一个整数量所包含的位数,所以一般而言,不能用整型量中的一位表示一种信号,也就是不能用一个整型量表示信号集。故而信号集有自己的数据类型,如下:
typedef struct { unsigned long sig[2]; } sigset_t; |
信号集的处理函数有5个,如下:
#include <signal.h>
int sigemptyset(sigset_t *set);/*功能:初始化set指向的信号集,清除其中的所有信号*/ int sigaddset(sigset_t *set, int signo);/*功能:将信号集中对应的bit置1,即添加一个信号*/ int sigdelset(sigset_t *set, int signo);/*功能:将信号集中对应的bit置0,即删掉一个信号*/ 四个函数的返回值:成功返回0,失败返回-1 int sigismember(const sigset_t *set, int signo);/*判断指定的信号是否存在*/ 返回值:若真则返回1,若假则返回0,若出错则返回-1 |
7 sigprocmask函数
头文件 | #include <signal.h> |
函数原型 | int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset); |
参数 | how:指示如何修改当前信号屏蔽字 宏名 宏值 作用 SIG_BLOCK 0 “或”操作,oset与set的并集 SIG_UNBLOCK 1 “与”操作,oset与set的交集 SIG_SETMASK 2 赋值操作,用set代替oset set:新的信号屏蔽字,若set为空,则不改变该进程的信号屏蔽字,how值也无意义。 oset:当前信号屏蔽字通过oset返回 |
返回 | 成功返回0,出错返回-1。 |
功能 | 检测或更改信号屏蔽字,或同时执行这两个操作。 |
在调用sigprocmask后如果有任何未决的、不再阻塞的信号,则在sigprocmask返回前,至少会将其中一个信号递送给该进程。
8 sigpending函数
头文件 | #include <signal.h> |
函数原型 | int sigpending(sigset_t *set); |
参数 | set:通过set返回信号集 |
返回 | 成功返回0,出错返回-1。 |
功能 | 返回信号集,其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决的。 |
#include "apue.h"
static void sig_quit(int signo)
{
printf("caught SIGQUIT\n");
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
err_sys("can't reset SIGQUIT");
}
int main(void)
{
sigset_t newmask, oldmask, pendmask;
if (signal(SIGQUIT, sig_quit) == SIG_ERR)
err_sys("can't catch SIGQUIT");
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
sleep(5);
if (sigpending(&pendmask) < 0)
err_sys("sigpending error");
if (sigismember(&pendmask, SIGQUIT))
printf("\nSIGQUIT pending\n");
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
printf("SIGQUIT unblocked\n");
sleep(5);
exit(0);
}
[root]# ./a.out
SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked
退出 (core dumped)
[root]#
9 sigsuspend函数
(1)问题:
需求: 有一段代码临界区,需要保护,以免被信号中断。这个时候,一般的做法是在代码临界区之前将相应的信号阻塞(通过调用sigprocmask),然后在代码临界区末尾将相应的信号解除阻塞(通过调用sigprocmask)。最后通过pause等待先前被阻塞的信号的发生。 附:如果为进程产生了一个选择为阻塞的信号,而且对该信号的动作是系统默认动作或捕捉该信号,则为该进程将此信号保持为未决状态,直到该进程对此信号解除了阻塞或将对此信号的动作更改为忽略。解除了阻塞后,该信号就开始递送。 方法: sigset_t newmask, oldmask; sigemptyset(&newmask); sigaddset(&newmask, SIGINT);/*代码临界区希望不被SIGINT信号中断*/ sigprocmask(SIG_BLOCK, &newmask, &oldmask);/*临界区开始前将对应信号阻塞*/ …. …. /*代码临界区*/ …. sigprocmask(SIG_SETMASK, &oldmask, NULL); /*临界区末尾将对应信号解除阻塞*/ pause(); /*等待SIGINT信号的发生*//*在解除阻塞和pause之间有一个时间窗口*/ 问题: 如果该SIGINT信号刚好发生在解除对SIGINT的阻塞和pause之间,那么就产生了问题:即在解除阻塞和pause之间的时间窗口中发生信号丢失,将使得pause永远阻塞。 |
(2)解决:
为了解决上述问题,需要在一个原子操作中解除阻塞、然后使进程休眠等待信号。该功能由sigsuspend函数提供。
头文件 | #include <signal.h> |
函数原型 | int sigsuspend(const sigset_t *sigmask); |
参数 | sigmask:新的信号屏蔽字 |
返回 | 返回-1,并将errno设置为EINTR。没有成功返回值。 |
功能 | 在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。 sigsuspend的原子操作: (1)将进程的信号屏蔽字设置为sigmask指定的值 (2)在捕捉到一个信号或发生了一个会终止该进程的信号之前该进程被挂起 (3)如果捕捉到一个信号而且从该信号处理程序返回,则sigsupending返回 (4)并且将该进程的信号屏蔽字设置为调用sigsuspend之前的值 |
(3)实例
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
void pr_mask(const char *str)
{
sigset_t sigset;
printf("%s", str);
sigprocmask(0, NULL, &sigset);
if (sigismember(&sigset, SIGUSR1))
printf(" SIGUSR");
if (sigismember(&sigset, SIGINT))
printf(" SIGINT");
printf("\n");
}
static void sig_int(int signo)
{
pr_mask("\nin sig_int: ");
}
int main(void)
{
sigset_t newmask, oldmask, waitmask;
pr_mask("program start: ");
if (signal(SIGINT, sig_int) < 0)
{
printf("signal error\n");
return -1;
}
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/*临界区开始之前,阻塞SIGINT信号*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
{
printf("SIG_BLOCK error");
return -1;
}
/*临界区代码开始..*/
pr_mask("in critical region: ");/*临界区中的代码,期间SIGINT信号被阻塞*/
/*临界区代码结束*/
/*休眠,等待除了SIGUSR1信号外的所有信号*/
if (sigsuspend(&waitmask) != -1)
{
printf("sigsuspend error\n");
return -1;
}
pr_mask("after return from sigsuspend: ");
/*临界区结束之后,解除阻塞SIGINT信号*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
printf("SIG_SETMASK error");
return -1;
}
pr_mask("program exit: ");
return 0;
}
[root]# ./a.out
program start:
in critical region: SIGINT /*临界区中,SIGINT被阻塞*/
^C /*中断命令Ctrl+C*/
in sig_int: SIGUSR1 SIGINT /*SIGINT信号处理程序中,SIGINT信号被阻塞;SIGUSR1信号也早被sigsuspend阻塞*/
after return from sigsuspend: SIGINT /*sigsuspend返回后,还原到之前的信号屏蔽字*/
program exit:
[root]#
/*用sigsuspend等待一个全局变量被设置*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
volatile sig_atomic_t quitflag;
static void sig_int(int signo)
{
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)
{
printf("signal(SIGINT) error");
return -1;
}
if (signal(SIGQUIT, sig_int) == SIG_ERR)
{
printf("signal(SIGQUIT) error");
return -1;
}
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
{
printf("SIG_BLOCK error");
return -1;
}
while (quitflag == 0)
{
sigsuspend(&zeromask);
}
quitflag = 0;
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
printf("SIG_SETMASK error");
return -1;
}
exit(0);
}
[root]# ./a.out
^C /*键入中断字符Ctrl+C*/
interrupt
^C
interrupt
^C
interrupt
^C
interrupt
^\ /*键入退出字符 Ctrl+\*/
[root]#
/*父子进程可用来实现同步的例程*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;
static void sig_usr(int signo)
{
sigflag = 1;
}
void TELL_WAIT(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
{
printf("signal(SIGUSR1) error");
return ;
}
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
{
printf("signal(SIGUSR2) error");
return ;
}
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
{
printf("SIG_BLOCK error");
return ;
}
return ;
}
void TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2);
}
void WAIT_PARENT(void)
{
while (sigflag == 0)
sigsuspend(&zeromask);
sigflag = 0;
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
printf("SIG_SETMASK error");
return ;
}
return ;
}
void TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1);
}
void WAIT_CHILD(void)
{
while (sigflag == 0)
sigsuspend(&zeromask);
sigflag = 0;
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
{
printf("SIG_SETMASK error");
return ;
}
return
}
/*上述示例的使用*/
int main(void)
{
TELL_WAIT();/*设置*/
if ((pid = fork()) < 0)
{
printf("fork error");
return -1;
}
else if (pid == 0)
{
TELL_PARENT(getppid());
WAIT_PARENT();
/*..................*/
exit(0);
}
TELL_CHILD(pid);
WAIT_CHILD();
exit(0);
}
10 sigaction函数
头文件 | #include <signal.h> |
函数原型 | int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact); |
参数 | signo:检测或修改其具体动作的信号编号 act:要修改的动作 oact:该信号的上一个动作 |
返回 | 成功返回0,出错返回-1 |
功能 | 检测或修改与指定信号相关联的处理动作。 |
11 sigsetjmp和siglongjmp函数
头文件 | #include <setjmp.h> |
函数原型 | int sigsetjmp(sigjmp_buf env, int savemask); void siglongjmp(sigjmp_buf env, int val); |
参数 |
|
返回 | 若直接调用则返回0,若从siglongjmp调用返回则返回非0值 |
功能 | 信号处理程序中进行非局部转移 |
当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动加到进程的信号屏蔽字中。这阻止了后来产生的这种信号中断该信号处理程序。
如果使用setjmp和longjmp,经常在信号处理程序中调用longjmp函数以返回到程序的主循环中,而不是从该处理程序返回。但从longjmp返回时,当前被进程屏蔽的信号仍然被屏蔽。
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
static jmp_buf jmpbuf;
void pr_mask(const char *str)
{
sigset_t sigset;
printf("%s: ", str);
sigprocmask(0, NULL, &sigset);
if (sigismember(&sigset, SIGUSR1))
printf(" SIGUSR1 is pending..\n");
printf("\n");
}
static void sig_usr1(int signo)
{
pr_mask("in sig_usr1");
longjmp(jmpbuf, 1);
}
int main(void)
{
sigset_t sigset;
if (signal(SIGUSR1, sig_usr1) < 0)
{
printf("signal error\n");
return -1;
}
pr_mask("starting main");
if (setjmp(jmpbuf))
{
printf("after longjmp...\n");
}
pr_mask("continue main");
for (;;)
pause();
return 0;
}
/*输出,从信号处理程序中longjmp转移后,信号仍被阻塞*/
[root]# ./a.out &
[1] 1511
[root]# starting main:
continue main:
[root]# kill -USR1 1511
in sig_usr1: SIGUSR1 is pending.. /*SIGUSR1信号在其处理程序中是被阻塞的*/
after longjmp...
continue main: SIGUSR1 is pending.. /*从信号处理程序中longjmp回来后,SIGUSR1信号仍被阻塞*/
<a target=_blank href="mailto:lincoln@ubuntu:~$"><span style="color:#000000;">[root]#</span></a>
POSIX.1并没有说明setjmp和longjmp对信号屏蔽字的作用,而是定义了两个新函数sigsetjmp和siglongjmp。在信号处理程序中进行的非局部转移应当使用这两个函数。与setjmp的唯一区别是sigsetjmp增加了一个参数int savemask。如果savemask非0,则sigsetjmp在env中保存进程的当前信号屏蔽字。调用siglongjmp时,如果非0 savemask的sigsetjmp调用已经保存了env,则siglongjmp从其中恢复保存的信号屏蔽字。
<pre class="cpp" name="code">#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
static sigjmp_buf jmpbuf;
void pr_mask(const char *str)
{
sigset_t sigset;
printf("%s: ", str);
sigprocmask(0, NULL, &sigset);
if (sigismember(&sigset, SIGUSR1))
printf(" SIGUSR1 is pending..\n");
printf("\n");
}
static void sig_usr1(int signo)
{
pr_mask("in sig_usr1");
siglongjmp(jmpbuf, 1);
}
int main(void)
{
sigset_t sigset;
if (signal(SIGUSR1, sig_usr1) < 0)
{
printf("signal error\n");
return -1;
}
pr_mask("starting main");
if (sigsetjmp(jmpbuf, 1))
{
printf("after siglongjmp...\n");
}
pr_mask("continue main");
for (;;)
pause();
return 0;
}
/*输出,从信号处理程序中siglongjmp转移后,信号被解除阻塞*/
[root]# ./a.out &
[1] 1630
[root]# starting main:
continue main:
[root]#kill -USR1 1630
[root]# in sig_usr1: SIGUSR1 is pending.. /*SIGUSR1信号在其处理程序中是被阻塞的*/
after siglongjmp...
continue main: /*从信号处理程序中siglongjmp回来后,SIGUSR1信号被解除阻塞*/
[root]#
只要在信号处理程序中调用siglongjmp,就需要提供一种保护技术,仅在调用sigsetjmp之后才将变量canjmp设置为非0,在信号处理程序中检测此变量,仅当它为非0值时才调用siglongjmp。使用该保护机制,使得在jmpbuf(跳转缓冲)尚未由sigsetjmp初始化时,防止调用信号处理程序。
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjump;
static void sig_mask(const char *str)
{
sigset_t sigset;
int errno_save;
errno_save = errno;
if (sigprocmask(0, NULL, &sigset) < 0)
{
printf("sigprocmask error\n");
return ;
}
printf("%s: ", str);
if (sigismember(&sigset, SIGUSR1))
printf("SIGUSR1 ");
if (sigismember(&sigset, SIGALRM))
printf("SIGALRM ");
printf("\n");
errno = errno_save;
}
static void sig_usr1(int signo)
{
sigset_t sigset;
if (canjump == 0)
return ;
sig_mask("starting sig_usr1");
alarm(3);
sleep(5);
sig_mask("ending sig_usr1");
canjump = 0;
siglongjmp(jmpbuf, 1);
}
static void sig_alrm(int signo)
{
sig_mask("sig_alrm");
}
int main(void)
{
if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
{
printf("signal(SIGUSR1) error\n");
return -1;
}
if (signal(SIGALRM, sig_alrm) == SIG_ERR)
{
perror("signal(SIGALRM) error\n");
return -1;
}
sig_mask("startint main");
if (sigsetjmp(jmpbuf, 1))
{
sig_mask("ending main");
return 0;
}
canjump = 1;
for (;;)
{
pause();
}
return 0;
}
/*输出*/
[root]# ./a.out &
[2] 3213
[root]# startint main:
[root]# kill -USR1 3213
starting sig_usr1: SIGUSR1
sig_alrm: SIGUSR1 SIGALRM
ending sig_usr1: SIGUSR1
ending main:
[2]- Done ./a.out
[root]#
12 abort函数
头文件 | #include <stdlib.h> |
函数原型 | void abort(void); |
参数 | void |
返回 | void |
功能 | 使异常程序终止 |
/*abort的POSIX.1的实现*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void abort1(void)
{
sigset_t mask;
struct sigaction action;
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);
sigfillset(&mask);
sigdelset(&mask, SIGABRT);
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT);
fflush(NULL);
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT);
exit(1);
}
13 system函数
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
int system1(const char *cmdstring)
{
pid_t pid;
int status;
struct sigaction ignore, saveintr, savequit;
sigset_t chldmask, savemask;
if (cmdstring == NULL)
return 1;
ignore.sa_handler = SIG_IGN;
sigemptyset(&ignore.sa_mask);
ignore.sa_flags = 0;
if (sigaction(SIGINT, &ignore, &saveintr) < 0)
return -1;
if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
return -1;
sigemptyset(&chldmask);
sigaddset(&chldmask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
return -1;
if ((pid = fork()) < 0)
status = -1;
else if (pid == 0)
{
sigaction(SIGINT, &saveintr, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127);
}
else
{
while (waitpid(pid, &status, 0) < 0)
{
if (errno != EINTR)
{
status = -1;
break;
}
}
}
if (sigaction(SIGINT, &saveintr, NULL) < 0)
return -1;
if (sigaction(SIGQUIT, &savequit, NULL) < 0)
return -1;
if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
return -1;
return status;
}
14 sleep函数
头文件 | #include <unistd.h> |
函数原型 | unsigned int sleep(unsigned int seconds); |
参数 | seconds:秒数 |
返回 | 返回0或未休眠够的秒数 |
功能 | 休眠 |
/*sleep的可靠实现*/
#include "apue.h"
static void sig_alrm(int signo)
{
/* nothing to do, just returning wakes up sigsuspend()*/
}
unsigned int sleep(unsigned int nsecs)
{
struct sigaction newact, oldact;
sigset_t newmask, oldmask, suspmask;
unsigned int unslept;
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
alarm(nsecs);
suspmask = oldmask;
sigdelset(&suspmask, SIGALRM);
sigsuspend(&suspmask);
unslept = alarm(0);
sigaction(SIGALRM, &oldact, NULL);
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return unslept;
}