• 信号对系统调用的作用---自动重启动或被中断而停止执行


      早期的UNIX系统的一个特性:如果进程在执行一个低速系统调用而阻塞期间,捕捉到一个信号,则该系统调用就被中断而不再继续执行。该系统调用返回出错,其error被设置为EINTR。即信号中断系统调用的执行
    这样处理的理由是:因为一个信号发生了,进程捕捉到了它,那么意味着已经发生了某种事情,所以是个唤醒 被阻塞的系统调用 的机会。
    为了支持系统调用被信号打断而不再执行的特性,将系统调用分为两类:低速系统调用和其他系统调用。
    低速系统调用是可能会使进程永远阻塞的一类系统调用,他们包括:
    1. read()、readv(),在读某些类型的文件(管道、终端设备以及网络设备)时,如果数据并不存在则可能会使调用者永远阻塞;
    2. write()、writev(),在写这些类型的文件时,如果不能立即接受这些数据,则也可能会使调用者永远阻塞;
    3. 打开某些类型的文件,在某种条件发生之前也可能会使调用者阻塞(例如,打开终端设备,他要等待直到所连接的调制解调器应答了电话)--->这种类型还没有实际例子测试
    4. pause()和wait();
    5. 某些ioctl;
    6. 某些进程间通信函数;

    什么是系统调用的自动重启动?
      当系统调用被信号中断时,并不返回,而是继续执行。如果read()阻塞等待,当进程接受到信号时,并不将read返回,而是继续阻塞等待。
    为什么要引入自动重启动的?
      有时用户并不知道所使用的输入、输出设备是否是低速设备。如果编写的程序可以用交互方式运行,则他可能读、写低速终端设备。
      如果在程序中捕捉到信号,而系统调用并不提供重启动功能,则对每次读、写系统调用都要进行是否出错返回的测试,如果是被信号中断,则再调用读、写系统调用。
    什么时候引入系统调用的自动重启动?
      4.2BSD支持某些被中断系统调用的自动重启动。
      4.3BSD允许进程基于每个信号禁用自动重启动功能(因为也存在某些应用程序并不希望系统调用被中断后自动重启)

    默认自动重启动的系统调用包括:ioctl(),read(),readv(),write(),writev(),wait(),waitpid();其中前5个函数只有在对低速设备进行操作时才会被信号中断。而wait和waitpid在捕捉到信号时总是被中断。

    我的测试环境是4.3BSD,下面以read()为例来说明自动重启动和被中断。

    自动重启动:

    restart.c

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <string.h>
     5 #include <signal.h>
     6 #include <errno.h>
     7 
     8 #define BUFSIZE (1024)
     9 
    10 static void sig_alrm(int signo)
    11 {
    12     printf("caught alarm...
    ");
    13 }
    14 
    15 void sig_usr(int signo)
    16 {
    17     if (signo == SIGUSR1)
    18         printf("received SIGUSR1
    ");
    19     else if (signo == SIGUSR2)
    20         printf("received SIGUSR2
    ");
    21     else
    22         printf("received signal %d
    ", signo);
    23 }
    24 
    25 int main(int argc, char** argv)
    26 {
    27     int nSize = 0;
    28     char acBuf[BUFSIZE] = {0};
    29 
    30     signal(SIGUSR1, sig_usr);
    31     signal(SIGALRM, sig_alrm);
    32 
    33     alarm(5);
    34 
    35 //    while(1)
    36     {
    37         
    38         memset(acBuf, '', BUFSIZE);
    39         nSize = read(STDIN_FILENO, acBuf, BUFSIZE); 
    40         if (errno == EINTR)
    41             printf("interrupt, size=%d
    ", nSize);
    42 
    43         if (1 == nSize && acBuf[0] == 10)
    44             ;
    45         else if (nSize != -1)
    46         {
    47             printf("nSize=%d, acBuf=%s", nSize, acBuf);        
    48         }
    49     }
    50 
    51     return 0;
    52 }

    运行结果是:

    定时器计数到5秒后,会发送alarm信号,从打印可以看出确实接收到了alarm信号,且read()并没有返回而是继续阻塞接受标准输入;然后在手动发送一个SIGUSR1信号,同样,read()并没有被中断,当输入jfdk后,read能正常读出。说明signal()默认是将信号设置为自动重启动

    被信号中断停止执行:

    no_restart.c

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <string.h>
     5 #include <signal.h>
     6 #include <errno.h>
     7 
     8 #define BUFSIZE (1024)
     9 
    10 static void sig_alrm(int signo)
    11 {
    12     printf("caught alarm...
    ");
    13 }
    14 
    15 void sig_usr(int signo)
    16 {
    17     if (signo == SIGUSR1)
    18         printf("received SIGUSR1
    ");
    19     else if (signo == SIGUSR2)
    20         printf("received SIGUSR2
    ");
    21     else
    22         printf("received signal %d
    ", signo);
    23 }
    24 
    25 int main(int argc, char** argv)
    26 {
    27     int nSize = 0;
    28     char acBuf[BUFSIZE] = {0};
    29     struct sigaction act, oact;
    30 
    31     act.sa_handler = sig_usr;
    32     sigemptyset(&act.sa_mask);
    33     act.sa_flags = 0|SA_INTERRUPT;
    34 
    35     sigaction(SIGUSR1, &act, &oact);
    36     signal(SIGALRM, sig_alrm);
    37 
    38     alarm(5);
    39 
    40     //while(1)
    41     {
    42         
    43         memset(acBuf, '', BUFSIZE);
    44         nSize = read(STDIN_FILENO, acBuf, BUFSIZE); 
    45         if (errno == EINTR)
    46             printf("interrupt, size=%d
    ", nSize);
    47 
    48         if (1 == nSize && acBuf[0] == 10)
    49             ;
    50         else if (nSize != -1)
    51         {
    52             printf("nSize=%d, acBuf=%s", nSize, acBuf);
    53         }
    54     }
    55 
    56     return 0;
    57 }

    运行结果为:

    此例子中,alarm信号还是自动重启动,手动发送一个SIGUSR1信号后,SIGUSR1信号处理程序执行完后,read()立马就返回了。

    从上面两个例子中还可以看出signal()和sigaction()两个函数的差异,显然sigaction对信号的的处理更加灵活。

  • 相关阅读:
    ORACLE数据库——触发器的创建和使用
    Oracle——游标的创建和使用
    Oracle数据库和表的操作
    JavaScript中的this,call,apply使用及区别详解
    jQuery插件开发的五种形态小结
    前端图片上传预览
    location.pathname:返回URL的域名(域名IP)后的部分。
    使用Selectivizr让IE6~8支持CSS3
    respond.js有什么作用?
    截取url参数
  • 原文地址:https://www.cnblogs.com/black-mamba/p/6870119.html
Copyright © 2020-2023  润新知