Chinaunix首页 | 论坛 | 博客
  • 博客访问: 537092
  • 博文数量: 142
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1452
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-12 16:28
文章分类

全部博文(142)

文章存档

2016年(10)

2015年(60)

2014年(72)

我的朋友

分类: C/C++

2014-08-26 17:52:50

在早期UNIX系统中,进程在执行一个低速系统调用而阻塞,如果在此期间捕捉到一个信号,则该系统调用就被中断不再继续执行。该系统调用返回出错,其errno被设置成为EINTR。这样处理的理由是:因为一个信号发生了,进程捕捉到了它,这意味着已经发生了某种事情,所以是唤醒阻塞的系统调用的好机会。
但是,有些应用场景下,我们想让系统调用不要因为软中断(信号)而退出。典型的例子:一个进程启动了读终端操作,而是使用者却离开该终端很长时间。这种情况下进程可能处于阻塞状态几个小时甚至数天,除非系统停机,否则我们不希望退出。所以有些系统引入了某些被中断系统调用的自动重启动。
函数 系统 保持安装信号处理程序 阻塞信号的能力 被中断后的系统调用自动重启动?
signal BSD/LINUX/Free BSD/MAC OS X yes yes
默认
sigaction BSD/LINUX/Free BSD/MAC OS X
yes
yes
可选

可见在大部分系统中signal函数注册的信号,被中断后的系统调用是默认自动重启动的,而sigaction是可选的。
下面是一个通过sigaction方式定义的Signal函数,它对大部分中断采取重启动(SIGALRM除外)

点击(此处)折叠或打开

  1. typedef void Sigfunc(int);

  2. Sigfunc * Signal(int signo, Sigfunc *func)
  3. {
  4.     struct sigaction act, oact;

  5.     act.sa_handler = func;
  6.     sigemptyset(&act.sa_mask);
  7.     act.sa_flags = 0;

  8.     if(signo == SIGALRM) {
  9. #ifdef SA_INTERRUPT
  10.         act.sa_flags |= SA_INTERRUPT;
  11. #endif
  12.     }else {
  13. #ifdef SA_RESTART
  14.         act.sa_flags |= SA_RESTART;
  15. #endif
  16.     }
  17.     
  18.     if(sigaction(signo,&act,&oact) < 0) {
  19.         return SIG_ERR;
  20.     }
  21.     return oact.sa_handler;
  22. }
理论介绍完毕,下面举一个具体的例子
1.使用signal函数
alarm.c 文件

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #define MAXLINE 4096

  5. static void sig_alrm(int signo)
  6. {
  7. }
  8. int main(void)
  9. {
  10.     int n;
  11.     char line[MAXLINE];
  12.     alarm(10);
  13.     if(signal(SIGALRM, sig_alrm) == SIG_ERR)
  14.         perror("signal");
  15.     if((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
  16.         perror("read");
  17.     alarm(0);

  18.     write(STDOUT_FILENO, line, n);
  19.     exit(0);
  20. }
编译: gcc -g  alarm.c -o a
运行: ./a

发现程序并没有像我们期待的中断退出,而是一直处于阻塞状态。
这个是什么原因呢?
没错,原因就是上面表格中描述的,对于linux系统,signal函数来注册信号,它会把SA_RESTART打开,这个就是自动重启动标志,显然,有了这个标志,导致read系统调用不返回,而是重新启动read操作,所以代码一直处于阻塞状态。
那如果要看到read被中断,怎么办?
对的,当然是要使用sigaction注册信号,并且把SA_RESTART标志关闭,上面新实现的Signal函数就实现了这个功能,代码修改如下:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #define MAXLINE 4096

  5. typedef void Sigfunc(int);

  6. Sigfunc * Signal(int signo, Sigfunc *func)
  7. {
  8.     struct sigaction act, oact;

  9.     act.sa_handler = func;
  10.     sigemptyset(&act.sa_mask);
  11.     act.sa_flags = 0;

  12.     if(signo == SIGALRM) {
  13. #ifdef SA_INTERRUPT
  14.         act.sa_flags |= SA_INTERRUPT;
  15. #endif
  16.     }else {
  17. #ifdef SA_RESTART
  18.         act.sa_flags |= SA_RESTART;
  19. #endif
  20.     }
  21.     
  22.     if(sigaction(signo,&act,&oact) < 0) {
  23.         return SIG_ERR;
  24.     }
  25.     return oact.sa_handler;
  26. }

  27. static void sig_alrm(int signo)
  28. {
  29. }
  30. int main(void)
  31. {
  32.     int n;
  33.     char line[MAXLINE];
  34.     alarm(10);
  35.     if(Signal(SIGALRM, sig_alrm) == SIG_ERR)
  36.         perror("signal");
  37.     if((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
  38.         perror("read");
  39.     alarm(0);

  40.     write(STDOUT_FILENO, line, n);
  41.     exit(0);
  42. }
编译: gcc -g  alarm.c -o a
运行: ./a
过了10s后提示:
read: Interrupted system call
程序退出。

阅读(557) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~