kill将信号发送给进程。raise函数则允许进程向自身发送信号。
- #include
-
- // 成功,返回0;失败,返回-1
- int kill(pid_t pid, int signo);
- int raise(int signo);
调用raise(signo)等价于调用kill(getpid(), signo)
kill函数的pid参数分为以下4种情况:
(1)pid>0
将信号发送给进程ID为pid的进程
(2)pid=0
将信号发送给与发送进程属于同一进程组的所有进程(发送进程具有权限向这些进程发送信号)
(3)pid<0
将信号给其进程组ID等于pid绝对值,且发送进程具有权限向其发送信号的所有进程
(4)pid==-1
将信号发送给发送进程有权限向它们发送信号的所有进程
进程将信号发送给其他进程需要权限。超级用户可以将信号发送给任一进程。对于非超级用户,规则是发送者的实际用户ID或有效用户ID必须等于接收者的实际用户ID或有效用户ID。
alarm函数可以设置一个定时器,在将来的某个时刻该定时器会超时。当定时器超时时,产生SIGALARM信号。如果忽略或不捕捉该信号,则默认动作是终止调用alarm函数的进程。
- #include
-
- //返回值:0或之前设置的闹钟时间的余留值
- unsigned int alarm(unsigned int seconds);
每个进程只能有一个闹钟时间。如果在调用alarm时,之前已为该进程注册的闹钟时间还没有超时,则该闹钟时间的余留值作为本次alarm函数调用的返回值。以前注册的闹钟时间则被新值代替。如果有之前注册的尚未超过的闹钟时间,且本次调用的seconds值为0,那么其余留值仍作为alarm函数返回,然后取消以前的闹钟时间。
pause函数使调用进程挂起直到捕捉到一个信号。
- #include
-
- //返回值:-1,errno设置为EINTR
- int pause(void);
只有执行了一个信号处理程序并从其返回时,pause才返回。这时,pause返回-1,errno设置为EINTR。
使用alarm和pause,进程可以使自己休眠一段指定的时间。
- #include
- #include
- #include
-
- static void sig_alrm(int signo)
- {
- /* nothing to do, just return to wake up the pause */
- printf("sig_alrm !!! \n");
- }
-
- unsigned int sleep1(unsigned int seconds)
- {
- if (signal(SIGALRM, sig_alrm) == SIG_ERR)
- return(seconds);
- printf("alarm start !!!\n");
- alarm(seconds); /* start the timer */
- pause(); /* next caught signal wakes us up */
- printf("alarm done !!!\n");
- return(alarm(0)); /* turn off timer, return unslept time */
- }
-
- int main(void) {
- unsigned int unslept;
-
- unslept = sleep1(5);
- printf("unslept:%d \n", unslept);
- return 0;
- }
测试结果如下:
- xxx$ ./sleep1
- alarm start !!!
- sig_alrm !!!
- alarm done !!!
- unslept:0
实际上,sleep1函数有竞争关系:alarm在调用pause之前超时。如果发生这种情况,则在调用pause后将不会再捕捉到信号,调用者将永远被挂起。
可以使用setjmp和longjmp来避免如上的竞争条件:
- #include
- #include
- #include
- #include
-
- static jmp_buf env_alrm;
-
- static void sig_alrm(int signo)
- {
- printf("sig_alrm!!! \n");
- longjmp(env_alrm, 1);
- }
-
- unsigned int sleep2(unsigned int seconds)
- {
- if (signal(SIGALRM, sig_alrm) == SIG_ERR)
- return(seconds);
- if (setjmp(env_alrm) == 0) {
- printf("alarm start!!! \n");
- alarm(seconds); /* start the timer */
- pause(); /* next caught signal wakes us up */
- printf("after pause 1 !!! \n");
- }
- // longjmp to here!
- printf("after pause 2 !!! alarm done!!! \n");
- return(alarm(0)); /* turn off timer, return unslept time */
- }
-
- int main(void) {
- unsigned int unslept;
-
- unslept = sleep2(5);
- printf("unslept:%d \n", unslept);
- return 0;
- }
测试结果如下:
- xxx$ ./sleep2
- alarm start!!!
- sig_alrm!!!
- after pause 2 !!! alarm done!!!
- unslept:0
sleep2函数也有问题,涉及到与其他信号的交互。例如如下伪代码:
- unsigned int sleep2(unsigned int);
- static void sig_int(int);
-
- int main(void)
- {
- unsigned int unslept;
-
- if (signal(SIGINT, sig_int) == SIG_ERR)
- err_sys("signal(SIGINT) error");
- unslept = sleep2(5);
- printf("sleep2 returned: %u\n", unslept);
- exit(0);
- }
-
- static void sig_int(int signo)
- {
- //耗时操作>5s
- }
如上程序若SIGINT的信号处理程序先触发,且是耗时操作>5s。则等sleep2(5) 5s超时后,sig_int程序会被突然打断,引起异常。
alarm函数还常用于对可能阻塞的操作设置时间上限值。
如下程序,从标准输入读一行,然后将其写到标准输出上。
- #include
- #include
- #include
- #include
- #include
-
- #define MAXLINE 4096
-
- static void sig_alrm(int);
- static jmp_buf env_alrm;
-
- int main(void)
- {
- int n;
- char line[MAXLINE];
-
- if (signal(SIGALRM, sig_alrm) == SIG_ERR)
- printf("signal(SIGALRM) error");
- if (setjmp(env_alrm) != 0) {
- // longjmp here!!!
- printf("read timeout \n");
- goto out1;
- }
-
- alarm(10);
- if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
- printf("read error \n");
- alarm(0);
-
- write(STDOUT_FILENO, line, n);
- exit(0);
- out1:
- printf("out1 exit(1) >>> \n");
- exit(1);
- }
-
- static void sig_alrm(int signo)
- {
- longjmp(env_alrm, 1);
- }
测试结果如下:
- xxx$ ./read2
- read timeout
- out1 exit(1) >>>
- xxx$ ./read2
- safasdf
- safasdf