Linux信号详解

Linux信号详解


一 信号的种类

可靠信号与不可靠信号, 实时信号与非实时信号

可靠信号就是实时信号, 那些从UNIX系统继承过来的信号都是非可靠信号, 表现在信号

不支持排队,信号可能会丢失, 比如发送多次相同的信号, 进程只能收到一次. 信号值小于

SIGRTMIN的都是非可靠信号.

非可靠信号就是非实时信号, 后来, Linux改进了信号机制, 增加了32种新的信号, 这些信

号都是可靠信号, 表现在信号支持排队, 不会丢失, 发多少次, 就可以收到多少次. 信号值

位于 [SIGRTMIN, SIGRTMAX] 区间的都是可靠信号.

关于可靠信号, 还可以参考WIKI的一段话:

Text代码  

  1. The real-time signals, ranging from SIGRTMIN to SIGRTMAX, are a set of signals that can be used for application-defined purposes.
  2. Because SIGRTMIN may have different values on different Unix-like systems, applications should always refer to the signals in the form SIGRTMIN+n, where n is a constant integer expression.
  3. The real-time signals have a number of properties that differentiate them from other signals and make them suitable for application-defined purposes:
  4. * Multiple instances of a real-time signal can be sent to a process and all will be delivered.
  5. * Real-time signals can be accompanied by an integer or pointer value (see sigqueue[2]).
  6. * Real-time signals are guaranteed to be delivered in the order they were emitted.

命令行输入 kill -l, 可以列出系统支持的所有信号:

C代码  

  1. ~> kill -l
  2. 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
  3. 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
  4. 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
  5. 16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
  6. 21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
  7. 26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
  8. 31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
  9. 38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
  10. 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
  11. 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
  12. 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
  13. 58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
  14. 63) SIGRTMAX-1  64) SIGRTMAX

非可靠信号一般都有确定的用途及含义,  可靠信号则可以让用户自定义使用

二 信号的安装

早期的Linux使用系统调用 signal 来安装信号

#include <signal.h>

void (*signal(int signum, void (*handler))(int)))(int);

该函数有两个参数, signum指定要安装的信号, handler指定信号的处理函数.

该函数的返回值是一个函数指针, 指向上次安装的handler

经典安装方式:

if (signal(SIGINT, SIG_IGN) != SIG_IGN) {

signal(SIGINT, sig_handler);

}

先获得上次的handler, 如果不是忽略信号, 就安装此信号的handler

由于信号被交付后, 系统自动的重置handler为默认动作, 为了使信号在handler

处理期间, 仍能对后继信号做出反应, 往往在handler的第一条语句再次调用 signal

sig_handler(ing signum)

{

/* 重新安装信号 */

signal(signum, sig_handler);

......

}

我们知道在程序的任意执行点上, 信号随时可能发生, 如果信号在sig_handler重新安装

信号之前产生, 这次信号就会执行默认动作, 而不是sig_handler. 这种问题是不可预料的.

使用库函数 sigaction  来安装信号

为了克服非可靠信号并同一SVR4和BSD之间的差异, 产生了 POSIX 信号安装方式, 使用

sigaction安装信号的动作后, 该动作就一直保持, 直到另一次调用 sigaction建立另一个

动作为止. 这就克服了古老的 signal 调用存在的问题

#include <signal.h> 
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

经典安装方式:

struct sigaction action, old_action;

/* 设置SIGINT */

action.sa_handler = sig_handler;

sigemptyset(&action.sa_mask);

sigaddset(&action.sa_mask, SIGTERM);

action.sa_flags = 0;

/* 获取上次的handler, 如果不是忽略动作, 则安装信号 */

sigaction(SIGINT, NULL, &old_action);

if (old_action.sa_handler != SIG_IGN) {

sigaction(SIGINT, &action, NULL);

}

基于 sigaction 实现的库函数: signal

sigaction 自然强大, 但安装信号很繁琐, 目前linux中的signal()是通过sigation()函数

实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必

再调用一次信号安装函数。

三 如何屏蔽信号

所谓屏蔽, 并不是禁止递送信号, 而是暂时阻塞信号的递送,

解除屏蔽后, 信号将被递送, 不会丢失. 相关API为

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

int sigsuspend(const sigset_t *mask);

int sigpending(sigset_t *set);

-----------------------------------------------------------------

int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));

sigprocmask()函数能够根据参数how来实现对信号集的操作,操作主要有三种:

* SIG_BLOCK 在进程当前阻塞信号集中添加set指向信号集中的信号

* SIG_UNBLOCK 如果进程阻塞信号集中包含set指向信号集中的信号,则解除

对该信号的阻塞

* SIG_SETMASK 更新进程阻塞信号集为set指向的信号集

屏蔽整个进程的信号:

C代码  

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <error.h>
  5. #include <string.h>
  6. void sig_handler(int signum)
  7. {
  8. printf("catch SIGINT\n");
  9. }
  10. int main(int argc, char **argv)
  11. {
  12. sigset_t block;
  13. struct sigaction action, old_action;
  14. /* 安装信号 */
  15. action.sa_handler = sig_handler;
  16. sigemptyset(&action.sa_mask);
  17. action.sa_flags = 0;
  18. sigaction(SIGINT, NULL, &old_action);
  19. if (old_action.sa_handler != SIG_IGN) {
  20. sigaction(SIGINT, &action, NULL);
  21. }
  22. /* 屏蔽信号 */
  23. sigemptyset(&block);
  24. sigaddset(&block, SIGINT);
  25. printf("block SIGINT\n");
  26. sigprocmask(SIG_BLOCK, &block, NULL);
  27. printf("--> send SIGINT -->\n");
  28. kill(getpid(), SIGINT);
  29. printf("--> send SIGINT -->\n");
  30. kill(getpid(), SIGINT);
  31. sleep(1);
  32. /* 解除信号后, 之前触发的信号将被递送,
  33. * 但SIGINT是非可靠信号, 只会递送一次
  34. */
  35. printf("unblock SIGINT\n");
  36. sigprocmask(SIG_UNBLOCK, &block, NULL);
  37. sleep(2);
  38. return 0;
  39. }

运行结果:

C代码  

  1. work> ./a.out
  2. block SIGINT
  3. --> send SIGINT -->
  4. --> send SIGINT -->
  5. unblock SIGINT
  6. catch SIGINT

这里发送了两次SIGINT信号 可以看到, 屏蔽掉SIGINT后,

信号无法递送, 解除屏蔽后, 才递送信号, 但只被递送一次,

因为SIGINT是非可靠信号, 不支持排队.

只在信号处理期间, 屏蔽其它信号

在信号的handler执行期间, 系统将自动屏蔽此信号, 但如果

还想屏蔽其它信号怎么办?  可以利用 struct sigaction 结构体

的 sa_mask 属性.

C代码  

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <error.h>
  5. #include <string.h>
  6. void sig_handler(int signum)
  7. {
  8. printf("in handle, SIGTERM is blocked\n");
  9. /* 在此handler内将屏蔽掉SIGTERM, 直到此handler返回 */
  10. printf("--> send SIGTERM -->\n");
  11. kill(getpid(), SIGTERM);
  12. sleep(5);
  13. printf("handle done\n");
  14. }
  15. void handle_term(int signum)
  16. {
  17. printf("catch sigterm and exit..\n");
  18. exit(0);
  19. }
  20. int main(int argc, char **argv)
  21. {
  22. struct sigaction action, old_action;
  23. /* 设置SIGINT */
  24. action.sa_handler = sig_handler;
  25. sigemptyset(&action.sa_mask);
  26. /* 安装handler的时候, 设置在handler
  27. * 执行期间, 屏蔽掉SIGTERM信号 */
  28. sigaddset(&action.sa_mask, SIGTERM);
  29. action.sa_flags = 0;
  30. sigaction(SIGINT, NULL, &old_action);
  31. if (old_action.sa_handler != SIG_IGN) {
  32. sigaction(SIGINT, &action, NULL);
  33. }
  34. /* 设置SIGTERM */
  35. action.sa_handler = handle_term;
  36. sigemptyset(&action.sa_mask);
  37. action.sa_flags = 0;
  38. sigaction(SIGTERM, NULL, &old_action);
  39. if (old_action.sa_handler != SIG_IGN) {
  40. sigaction(SIGTERM, &action, NULL);
  41. }
  42. printf("--> send SIGINT -->\n");
  43. kill(getpid(), SIGINT);
  44. while (1) {
  45. sleep(1);
  46. }
  47. return 0;
  48. }

运行结果:

C代码  

  1. work> ./a.out
  2. --> send SIGINT -->
  3. in handle, SIGTERM is blocked
  4. --> send SIGTERM -->
  5. handle done
  6. catch sigterm and exit..

收到SIGINT后, 进入sig_handler,此时发送SIGTERM信号将被屏蔽,

等sig_handler返回后, 才收到SIGTERM信号, 然后退出程序

四 如何获取未决信号

所谓未决信号, 是指被阻塞的信号, 等待被递送的信号.

int sigsuspend(const sigset_t *mask));

sigpending(sigset_t *set))获得当前已递送到进程,

却被阻塞的所有信号,在set指向的信号集中返回结果。

C代码  

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <error.h>
  5. #include <string.h>
  6. /* 版本1, 可靠信号将被递送多次 */
  7. //#define MYSIGNAL SIGRTMIN+5
  8. /* 版本2, 不可靠信号只被递送一次 */
  9. #define MYSIGNAL SIGTERM
  10. void sig_handler(int signum)
  11. {
  12. psignal(signum, "catch a signal");
  13. }
  14. int main(int argc, char **argv)
  15. {
  16. sigset_t block, pending;
  17. int sig, flag;
  18. /* 设置信号的handler */
  19. signal(MYSIGNAL, sig_handler);
  20. /* 屏蔽此信号 */
  21. sigemptyset(&block);
  22. sigaddset(&block, MYSIGNAL);
  23. printf("block signal\n");
  24. sigprocmask(SIG_BLOCK, &block, NULL);
  25. /* 发两次信号, 看信号将会被触发多少次 */
  26. printf("---> send a signal --->\n");
  27. kill(getpid(), MYSIGNAL);
  28. printf("---> send a signal --->\n");
  29. kill(getpid(), MYSIGNAL);
  30. /* 检查当前的未决信号 */
  31. flag = 0;
  32. sigpending(&pending);
  33. for (sig = 1; sig < NSIG; sig++) {
  34. if (sigismember(&pending, sig)) {
  35. flag = 1;
  36. psignal(sig, "this signal is pending");
  37. }
  38. }
  39. if (flag == 0) {
  40. printf("no pending signal\n");
  41. }
  42. /* 解除此信号的屏蔽, 未决信号将被递送 */
  43. printf("unblock signal\n");
  44. sigprocmask(SIG_UNBLOCK, &block, NULL);
  45. /* 再次检查未决信号 */
  46. flag = 0;
  47. sigpending(&pending);
  48. for (sig = 1; sig < NSIG; sig++) {
  49. if (sigismember(&pending, sig)) {
  50. flag = 1;
  51. psignal(sig, "a pending signal");
  52. }
  53. }
  54. if (flag == 0) {
  55. printf("no pending signal\n");
  56. }
  57. return 0;
  58. }

这个程序有两个版本:

可靠信号版本, 运行结果:

C代码  

  1. work> ./a.out
  2. block signal
  3. ---> send a signal --->
  4. ---> send a signal --->
  5. this signal is pending: Unknown signal 39
  6. unblock signal
  7. catch a signal: Unknown signal 39
  8. catch a signal: Unknown signal 39
  9. no pending signal

发送两次可靠信号, 最终收到两次信号

非可靠信号版本, 运行结果:

C代码  

  1. work> ./a.out
  2. block signal
  3. ---> send a signal --->
  4. ---> send a signal --->
  5. this signal is pending: Terminated
  6. unblock signal
  7. catch a signal: Terminated
  8. no pending signal

发送两次非可靠信号, 最终只收到一次

五 被中断了的系统调用

一些IO系统调用执行时, 如 read 等待输入期间, 如果收到一个信号,

系统将中断read, 转而执行信号处理函数. 当信号处理返回后, 系统

遇到了一个问题: 是重新开始这个系统调用, 还是让系统调用失败?

早期UNIX系统的做法是, 中断系统调用, 并让系统调用失败, 比如read

返回 -1, 同时设置 errno 为 EINTR

中断了的系统调用是没有完成的调用, 它的失败是临时性的, 如果再次调用

则可能成功, 这并不是真正的失败, 所以要对这种情况进行处理, 典型的方式为:

while (1) {

n = read(fd, buf, BUFSIZ);

if (n == -1 && errno != EINTR) {

printf("read error\n");

break;

}

if (n == 0) {

printf("read done\n");

break;

}

}

这样做逻辑比较繁琐, 事实上, 我们可以从信号的角度

来解决这个问题,  安装信号的时候, 设置 SA_RESTART

属性, 那么当信号处理函数返回后, 被该信号中断的系统

调用将自动恢复.

C代码  

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <error.h>
  5. #include <string.h>
  6. void sig_handler(int signum)
  7. {
  8. printf("in handler\n");
  9. sleep(1);
  10. printf("handler return\n");
  11. }
  12. int main(int argc, char **argv)
  13. {
  14. char buf[100];
  15. int ret;
  16. struct sigaction action, old_action;
  17. action.sa_handler = sig_handler;
  18. sigemptyset(&action.sa_mask);
  19. action.sa_flags = 0;
  20. /* 版本1:不设置SA_RESTART属性
  21. * 版本2:设置SA_RESTART属性 */
  22. //action.sa_flags |= SA_RESTART;
  23. sigaction(SIGINT, NULL, &old_action);
  24. if (old_action.sa_handler != SIG_IGN) {
  25. sigaction(SIGINT, &action, NULL);
  26. }
  27. bzero(buf, 100);
  28. ret = read(0, buf, 100);
  29. if (ret == -1) {
  30. perror("read");
  31. }
  32. printf("read %d bytes:\n", ret);
  33. printf("%s\n", buf);
  34. return 0;
  35. }

版本1, 不设置 SA_RESTART 属性 :

C代码  

  1. work> gcc signal.c
  2. work> ./a.out
  3. ^Cin handler
  4. handler return
  5. read: Interrupted system call
  6. read -1 bytes:

在 read 等待数据期间, 按下ctrl + c, 触发 SIGINT 信号,

handler 返回后, read 被中断, 返回 -1

版本2, 设置 SA_RESTART 属性:

C代码  

  1. work> gcc signal.c
  2. work> ./a.out
  3. ^Cin handler
  4. handler return
  5. hello, world
  6. read 13 bytes:
  7. hello, world

handler 返回后, read 系统调用被恢复执行, 继续等待数据.

六 非局部控制转移

int setjmp(jmp_buf env);

int sigsetjmp(sigjmp_buf env, int savesigs);

void longjmp(jmp_buf env, int val);

void siglongjmp(sigjmp_buf env, int val);

--------------------------------------------------------

setjmp()会保存目前堆栈环境,然后将目前的地址作一个记号,

而在程序其他地方调用 longjmp 时便会直接跳到这个记号位置,

然后还原堆栈,继续程序好执行。

setjmp调用有点fork的味道, setjmp()return 0 if returning directly,

and non-zero when returning from longjmp using  the saved context.

if (setjmp(jmpbuf)) {

printf("return from jmp\n");

} else {

printf("return directly\n");

}

setjmp 和 sigsetjmp 的唯一区别是: setjmp 不一定会恢复信号集合,

而sigsetjmp可以保证恢复信号集合

C代码  

  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <string.h>
  6. #include <setjmp.h>
  7. void sig_alrm(int signum);
  8. void sig_usr1(int signum);
  9. void print_mask(const char *str);
  10. static sigjmp_buf jmpbuf;
  11. static volatile sig_atomic_t canjmp;
  12. static int sigalrm_appear;
  13. int main(int argc, char **argv)
  14. {
  15. struct sigaction action, old_action;
  16. /* 设置SIGUSR1 */
  17. action.sa_handler = sig_usr1;
  18. sigemptyset(&action.sa_mask);
  19. action.sa_flags = 0;
  20. sigaction(SIGUSR1, NULL, &old_action);
  21. if (old_action.sa_handler != SIG_IGN) {
  22. sigaction(SIGUSR1, &action, NULL);
  23. }
  24. /* 设置SIGALRM */
  25. action.sa_handler = sig_alrm;
  26. sigemptyset(&action.sa_mask);
  27. action.sa_flags = 0;
  28. sigaction(SIGALRM, NULL, &old_action);
  29. if (old_action.sa_handler != SIG_IGN) {
  30. sigaction(SIGALRM, &action, NULL);
  31. }
  32. print_mask("starting main:");
  33. if (sigsetjmp(jmpbuf, 1) != 0) {
  34. print_mask("exiting main:");
  35. } else {
  36. printf("sigsetjmp return directly\n");
  37. canjmp = 1;
  38. while (1) {
  39. sleep(1);
  40. }
  41. }
  42. return 0;
  43. }
  44. void sig_usr1(int signum)
  45. {
  46. time_t starttime;
  47. if (canjmp == 0) {
  48. printf("please set jmp first\n");
  49. return;
  50. }
  51. print_mask("in sig_usr1:");
  52. alarm(1);
  53. while (!sigalrm_appear);
  54. canjmp = 0;
  55. siglongjmp(jmpbuf, 1);
  56. }
  57. void sig_alrm(int signum)
  58. {
  59. print_mask("in sig_alrm:");
  60. sigalrm_appear = 1;
  61. return;
  62. }
  63. void print_mask(const char *str)
  64. {
  65. sigset_t sigset;
  66. int i, errno_save, flag = 0;
  67. errno_save = errno;
  68. if (sigprocmask(0, NULL, &sigset) < 0) {
  69. printf("sigprocmask error\n");
  70. exit(0);
  71. }
  72. printf("%s\n", str);
  73. fflush(stdout);
  74. for (i = 1; i < NSIG; i++) {
  75. if (sigismember(&sigset, i)) {
  76. flag = 1;
  77. psignal(i, "a blocked signal");
  78. }
  79. }
  80. if (!flag) {
  81. printf("no blocked signal\n");
  82. }
  83. printf("\n");
  84. errno = errno_save;
  85. }

运行结果:

C代码  

  1. work> ./a.out &
  2. [4] 28483
  3. starting main:
  4. no blocked signal
  5. sigsetjmp return directly
  6. kill -USR1 28483
  7. in sig_usr1:
  8. a blocked signal: User defined signal 1
  9. in sig_alrm:
  10. a blocked signal: User defined signal 1
  11. a blocked signal: Alarm clock
  12. exiting main:
  13. no blocked signal

七 信号的生命周期

从信号发送到信号处理函数的执行完毕

对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,

可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:

信号诞生;信号在进程中注册完毕;信号在进程中的注销完毕;信号处理函数执行完毕。

下面阐述四个事件的实际意义:

信号"诞生"。信号的诞生指的是触发信号的事件发生

(如检测到硬件异常、定时器超时以及调用信号发送函数

kill()或sigqueue()等)。信号在目标进程中"注册";

进程的task_struct结构中有关于本进程中未决信号的数据成员:

struct sigpending pending:

struct sigpending{

struct sigqueue *head, **tail;

sigset_t signal;

};

第三个成员是进程中所有未决信号集,第一、第二个成员分别指向一个

sigqueue类型的结构链(称之为"未决信号链表")的首尾,链表中

的每个sigqueue结构刻画一个特定信号所携带的信息,并指向下一个

sigqueue结构:

struct sigqueue{

struct sigqueue *next;

siginfo_t info;

}

信号的注册

信号在进程中注册指的就是信号值加入到进程的未决信号集中

(sigpending结构的第二个成员sigset_t signal),

并且加入未决信号链表的末尾。 只要信号在进程的未决信号集中,

表明进程已经知道这些信号的存在,但还没来得及处理,或者该信号被进程阻塞。

当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,

都会被再注册一次,因此,信号不会丢失,因此,实时信号又叫做"可靠信号"。

这意味着同一个实时信号可以在同一个进程的未决信号链表中添加多次.

当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册,

则该信号将被丢弃,造成信号丢失。因此,非实时信号又叫做"不可靠信号"。

这意味着同一个非实时信号在进程的未决信号链表中,至多占有一个sigqueue结构.

一个非实时信号诞生后,

(1)、如果发现相同的信号已经在目标结构中注册,则不再注册,对于进程来说,

相当于不知道本次信号发生,信号丢失.

(2)、如果进程的未决信号中没有相同信号,则在进程中注册自己。

信号的注销。

在进程执行过程中,会检测是否有信号等待处理

(每次从系统空间返回到用户空间时都做这样的检查)。如果存在未决

信号等待处理且该信号没有被进程阻塞,则在运行相应的信号处理函数前,

进程会把信号在未决信号链中占有的结构卸掉。是否将信号从进程未决信号集

中删除对于实时与非实时信号是不同的。对于非实时信号来说,由于在未决信

号信息链中最多只占用一个sigqueue结构,因此该结构被释放后,应该把信

号在进程未决信号集中删除(信号注销完毕);而对于实时信号来说,可能在

未决信号信息链中占用多个sigqueue结构,因此应该针对占用sigqueue结构

的数目区别对待:如果只占用一个sigqueue结构(进程只收到该信号一次),

则应该把信号在进程的未决信号集中删除(信号注销完毕)。否则,不应该在进程

的未决信号集中删除该信号(信号注销完毕)。

进程在执行信号相应处理函数之前,首先要把信号在进程中注销。

信号生命终止。

进程注销信号后,立即执行相应的信号处理函数,执行完毕后,

信号的本次发送对进程的影响彻底结束。

八 关于可重入函数

在信号处理函数中应使用可重入函数。

信号处理程序中应当使用可重入函数

(注:所谓可重入函数是指一个可以被多个任务调用的过程,

任务在调用时不必担心数据是否会出错)。因为进程在收到信号

后,就将跳转到信号处理函数去接着执行。如果信号处理函数中

使用了不可重入函数,那么信号处理函数可能会修改原来进程中

不应该被修改的数据,这样进程从信号处理函数中返回接着执行时,

可能会出现不可预料的后果。不可再入函数在信号处理函数中被视为

不安全函数。满足下列条件的函数多数是不可再入的:

(1)使用静态的数据结构,如getlogin(),gmtime(),getgrgid(),

getgrnam(),getpwuid()以及getpwnam()等等;

(2)函数实现时,调用了malloc()或者free()函数;

(3)实现时使用了标准I/O函数的。The Open Group视下列函数为可再入的:

_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、

cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown() 、

close()、creat()、dup()、dup2()、execle()、execve()、

fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、

geteuid()、getgid()、getgroups()、getpgrp()、getpid()、

getppid()、getuid()、kill()、link()、lseek()、mkdir()、

mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、

read()、rename()、rmdir()、setgid()、setpgid()、setsid()、

setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、

sigfillset()、sigismember()、signal()、sigpending()、

sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、

tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、

tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、

umask()、uname()、unlink()、utime()、wait()、waitpid()、

write()。

即使信号处理函数使用的都是"安全函数",同样要注意进入处理函数时,

首先要保存errno的值,结束时,再恢复原值。因为,信号处理过程中,

errno值随时可能被改变。另外,longjmp()以及siglongjmp()没有被列为可重入函数,

因为不能保证紧接着两个函数的其它调用是安全的。

转自http://kenby.iteye.com/blog/1173862

时间: 2024-10-13 22:29:19

Linux信号详解的相关文章

Linux 信号详解二(信号分类)

信号分类 信号分为可靠信号和不可靠信号 不可靠信号的缺点 ①:处理完信号,需要重新再注册信号:②信号可能丢失. Linux已经对缺点①做了优化,现在的不可靠问题主要指的是信号可能丢失 信号还可以分为实时信号和非实时信号--一般不可靠信号指的是前32种信号,这32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作 --可靠信号指的后32种信号 --非实时信号都不支持排队,都是不可靠信号:实时信号都支持排队,都是可靠信号.

Linux 信号详解

信号列表 SIGABRT 进程停止运行 6 SIGALRM 警告钟 SIGFPE 算述运算例外 SIGHUP 系统挂断 SIGILL 非法指令 SIGINT 终端中断 2 SIGKILL 停止进程(此信号不能被忽略或捕获) SIGPIPE 向没有读的管道写入数据 SIGSEGV 无效内存段访问 SIGQOUT 终端退出 3 SIGTERM 终止 SIGUSR1 用户定义信号1 SIGUSR2 用户定义信号2 SIGCHLD 子进程已经停止或退出 SIGCONT 如果被停止则继续执行 SIGSTO

linux系统下信号详解2

信号是UNIX 系统所使用的进程通信方法中,最古老的一种.信号不但能从内核发往一个进程,也能从一个进程发往另一个进程.例如,用户在后台启动了一个要运行较长时间的程序,如果想中断其执行,可以用kill 命令把SIGTERM信号发送给这个进程,SIGTERM 将终止此进程的执行.信号还提供了向UNIX 系统进程传送软中断的简单方法.信号可以中断一个进程,而不管它正在作什么工作.由于信号的特点,所以不用它来作进程间的直接数据传送,而把它用作对非正常情况的处理.由于信号本身不能直接携带信息,这就限制了它

Linux 目录详解 树状目录结构图

1.树状目录结构图 2./目录 目录 描述 / 第一层次结构的根.整个文件系统层次结构的根目录. /bin/ 需要在单用户模式可用的必要命令(可执行文件):面向所有用户,例如:cat.ls.cp,和/usr/bin类似. /boot/ 引导程序文件,例如:kernel.initrd:时常是一个单独的分区[6] /dev/ 必要设备, 例如:, /dev/null. /etc/ 特定主机,系统范围内的配置文件. 关于这个名称目前有争议.在贝尔实验室关于UNIX实现文档的早期版本中,/etc 被称为

linux awk详解与应用

文章来自于本人个人博客: linux awk详解与应用 1.awk awk是一个强大的文本分析工具,它可以通过分析文本来生成一个数据报告.它的原理就是读取每行的输入,然后按照分隔符切分(默认是空格),再进行定制计算. awk '{print $1}' /etc/passwd   #打印出passwd文件的所有行的第一列 这是awk的基础语法,在awk中$n代表列数,即$1--第一列,$2---第二列....,但是$0代表整行 接下来我们按照指定的分隔符打印数据: awk -F ':' '{pri

Gentoo Linux安装详解--根据官方WiKi整理

1. 前期准备 远程登录: 开启ssh服务: /etc/init.d/sshd start 设置密码: passwd 以便使用putty.ssh client远程登录上传stage等(有时在线下载很慢,而局域网上传很快) 准备磁盘: 分区: fdisk /dev/sda /dev/sda1 : /boot 100M(32-100M) 设启动笔记-a/dev/sda2 : / 20G/dev/sda3 : /home 20G/dev/sda5 : /swap 1G (内存< 512 MB,分区分配

linux命令详解

一.cat主要有三大功能: 1.一次显示整个文件.$ cat filename2.从键盘创建一个文件.$ cat > filename (只能创建新文件,不能编辑已有文件)3.将几个文件合并为一个文件: $cat file1 file2 > file 参数:-n 或 --number 由 1 开始对所有输出的行数编号-b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号-s 或 --squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行-v

PXE的概述及Linux使用详解

PXE的概述及使用详解 现在企业采购的很多计算机都是没光驱的,怎么安装系统呢?另外,如何能快速大规模安装Linux服务器操作系统呢,有什么好办法吗? 答案是有的,那就是本文要说的:PXE PXE(preboot execute environment,预启动执行环境)是由Intel公司开发的最新技术,工作于Client/Server的网络模式,支持工作站通过网络从远端服务器下载映像,并由此支持通过网络启动操作系统,在启动过程中,终端要求服务器分配IP地址,再用TFTP(trivial filet

Gentoo Linux安装详解

1. 前期准备 远程登录: 开启ssh服务: /etc/init.d/sshd start 设置密码: passwd 以便使用putty.ssh client远程登录上传stage等(有时在线下载很慢,而局域网上传很快) 准备磁盘: 分区: fdisk /dev/sda /dev/sda1 : /boot 100M(32-100M) 设启动笔记-a/dev/sda2 : / 20G/dev/sda3 : /home 20G/dev/sda5 : /swap 1G (内存< 512 MB,分区分配