Outline
- 1.可重入函数
- 2.可靠的信号
- 3.中断的系统调用
- 4.kill/rise
- 5.alarm/pause
- 6.信号集及其操作
=================================================================================================
1. 可重入函数
举一个例子,什么是不可重入的函数。假设一个进程正在执行malloc函数,此时接收到一个信号,转入信号处理例程中,在此例程中,又再次调用了malloc函数,将发生错误。原因是malloc通常需要操作一些系统全局变量,而插入的例程将在上一次malloc结束之前修改该变量,当例程返回时,malloc继续执行,但是由于全局变量已经被修改,将发生错误。
2. 可靠的信号
pending:信号已经发生,但是尚未被进程处理
delivery:信号发生却已经被处理
block阻塞:一个信号将保持penging状态知道进程对它接触了阻塞或者将其处理的工作改成为忽略。假设在该信号被接触阻塞之前,又发生了多次该信号,结果还是与只产生一次该信号相同
3. 中断的系统调用
有些可能会阻塞的系统调用处于阻塞状态时,进程若收到信号,那么该系统调用将被中断,并且立刻返回。
4. kill / raise
- #include <signal.h>
- int kill(pid_t pid, int signo);
- int raise(int signo);
调用kill / raise将产生某个信号,两个函数的区别在于kill可以发送给任意进程而raise只发送给调用进程。
5. alarm / pause
- #include <unistd.h>
- unsigned int alarm(unsigned int seconds);
- int pause(void);
alarm设置一个定时器,当超过指定时间后产生SIGALRM信号
pause函数使调用进程挂起,直到捕捉到一个信号,注意,pause不是一个安全的函数,在某些情况下会造成信号丢失引起的进程挂起,只能用sigsuspend解决。
- #include <unistd.h>
- #include <signal.h>
- #include <stdio.h>
- static void alarm_handler(int signo);
- int main()
- {
- if(signal(SIGALRM, alarm_handler) == SIG_ERR)
- printf("can't catch SIGALRM'");
- alarm(1);
- while(1)
- pause();
- return 0;
- }
- static void alarm_handler(int signo)
- {
- if(signo == SIGALRM)
- printf("signal alarm captured.\n");
- }
输出:
- deepsky@Debian:~/apue$ ./a.out
- signal alarm captured.
alarm还有一个非常常见的用法,用来对可能阻塞的操作设置时间上限值,其原理是系统调用可被信号中断,如欲深入了解其原因详请参见:
例子:设定read最多只能阻塞5s
- #include <signal.h>
- #include <stdio.h>
- #include <unistd.h>
- static void alarm_handler(int signo);
- int main()
- {
- int n;
- char line[100];
- if(signal(SIGALRM, alarm_handler) == SIG_ERR)
- printf("can't catch SIGALRM\n");
- alarm(5);
- if ((n = read(STDIN_FILENO, line, 100)) < 0)
- printf("read error\n");
- alarm(0); //if read() returns within 5s, we need to clear the previous alarm
- write(STDOUT_FILENO, line, n);
- return 0;
- }
- static void alarm_handler(int signo)
- {
- if(signo == SIGALRM)
- printf("caught SIGALRM.\n");
- }
6. 信号集及其操作
a )之前对信号的操作均基于每一个特定的信号,如SIGALRM,现在开始讨论如何对一个信号集合进行操作
- #include <signal.h> //sigset_t 为信号集合的结构
- int sigempty(sigset_t *set); //清空集合
- int sigfillset(sigset_t *set); //将所有信号加入集合
- int sigaddset(sigset_t *set, int signo); //向集合添加signo信号
- int sigdelset(sigset_t *set, int signo); //删除信号
- int sigismember(const sigset_t *set, int signo);//判断signo是否在集合中
b)进程可以将某些信号设定对阻塞,即产生信号后不立即处理,等待空闲或许要是才主动去处理,设定哪些信号需要阻塞使用了函数:
- #include <signal.h>
- int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
参数说明:
- how: 指定以何种方式修改当前信号屏蔽集合,删除/添加/替换
- set:若非空指针,则表示how指定的操作有效,否则不会修改当前信号屏蔽集合
- oset:若非空指针,返回修改前的信号屏蔽字
c)获取当前被阻塞的信号,即已经发生,但未被处理:
- #include <signal.h>
- int sigpending(sigset_t *set);
d)sigaction函数
该函数作用与signal类似,只不过加入了信号集合以及信号屏蔽字的概念:
- #include <signal.h>
- int sigaction(int signo, const struct sigaction *restriict act, struct sigaction *restrict oact);
e)sigsuspend函数
此函数作用是可以安全地解除对某信号的阻塞,下面将通过实例说明。
设想一个场景,我们有一段代码在执行时,不希望被某个信号打断,我可以在这段代码之前将该信号加入屏蔽字中,等代码执行完后,将该信号从屏蔽字中删除:
- sigset_t tempset, oldset;
- sigemptyset(&tempset);
- sigaddset(&tempset, SIGUSR1);
- sigprocmask(SIG_BLOCK, &tempset, &oldset);
- /* 被保护的代码 */
- sigprocmask(SIG_SETMASK, &oldset, NULL);
- /* 窗口 */
- pause(); //因为在被屏蔽期间,可能发生过SIGUSR1,被解除后,SIGUSR1将被处理
以上代码乍看之下,没什么问题,但其实存在隐患。由于在调用sigprocmask之后,之前被阻塞的信号将立即生效,进程将调用相应的处理函数,也就是说在 /* 窗口 */位置就会发生信号,那么在pause执行以前,信号实际上已经被处理掉了,显然与我们的本意不符,我们本来期待pause可以捕获到该信号。这种现象称之为信号丢失。那么如果防止这种错误呢?答案就是使用sigsuspend。
- #include <stdio.h>
- #include <signal.h>
- int pr_mask(char *s)
- {
- sigset_t sigset;
- sigprocmask(0, NULL, &sigset);
- printf("%s: ", s);
-
- 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");
- return 0;
- }
- void sig_quit(int signo)
- {
- pr_mask("in sig quit");
- }
- int main()
- {
- sigset_t new, old, tempset;
- signal(SIGQUIT, sig_quit);
-
- sigemptyset(&tempset);
- sigemptyset(&new);
- sigaddset(&new, SIGQUIT);
- sigprocmask(SIG_BLOCK, &new, &old);
- pr_mask("in critical section");
- /* critical section */
-
- sigsuspend(&tempset); //解除阻塞,并将现有mask替换为空集
- pr_mask("after return form sigsuspend");
-
- sigprocmask(SIG_UNBLOCK, &new, NULL);
- pr_mask("program exit");
-
- return 0;
- }
输出:
- in critical section: SIGQUIT
- in sig quit: SIGINT SIGQUIT //此处为向进程发送SIGQUIT后的输出,可见SIGQUIT已不再阻塞,但是为什么打印结果中还包括了SIGQUIT,因为在任何的信号处理程序中,都会将当前正在处理的信号加入到屏蔽字中,防止信号重入
- after return form sigsuspend: SIGQUIT //从sigsuspend返回后,它会将屏蔽字恢复到之前的状态,此时其实SIGQUTI还未被解除,只是在sigsuspend我们已经零时将其解除并处理了该信号,所以现在再调能够用sigprocmask就不用担心丢失信号的问题了
- program exit:
阅读(2027) | 评论(0) | 转发(1) |