zoukankan      html  css  js  c++  java
  • Linux被中断的系统调用

    慢系统调用,指的是可能永远无法返回,从而使进程永远阻塞的系统调用,比如无客户连接时的accept、无输入时的read都属于慢速系统调用。
    在Linux中,当阻塞于某个慢系统调用的进程捕获一个信号,则该系统调用就会被中断,转而执行信号处理函数,这就是被中断的系统调用。
    然而,当信号处理函数返回时,有可能发生以下的情况:

    • 如果信号处理函数是用signal注册的,系统调用会自动重启,函数不会返回
    • 如果信号处理函数是用sigaction注册的
      • 默认情况下,系统调用不会自动重启,函数将返回失败,同时errno被置为EINTR
      • 只有中断信号的SA_RESTART标志有效时,系统调用才会自动重启

    下面我们编写代码,分别验证上述几种情形,其中系统调用选择read,中断信号选择SIGALRM,中断信号由alarm产生。

    使用signal

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <errno.h>
    
    void handler(int s)
    {
        printf("read is interrupt by signal handler
    ");
        return;
    }
    
    int main()
    {
        char buf[10];
        int nread = 0;
    
        signal(SIGALRM, handler);
        alarm(2);
    
        printf("read start
    ");
        nread = read(STDIN_FILENO, buf, sizeof(buf));
        printf("read return
    ");
    
        if ((nread < 0) && (errno == EINTR))
        {
            printf("read return failed, errno is EINTR
    ");
        }
    
        return 0;
    }
    

    使用sigaction + 默认情况

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <errno.h>
    
    void handler(int s)
    {
        printf("read is interrupt by signal handler
    ");
        return;
    }
    
    int main()
    {
        char buf[10];
        int nread = 0;
        struct sigaction act;
    
        sigemptyset(&act.sa_mask);
        act.sa_handler = handler;
        act.sa_flags = 0;  //不给SIGALRM信号设置SA_RESTART标志,使用sigaction的默认处理方式
        //act.sa_flag |= SA_INTERRUPT;  //SA_INTERRUPT是sigaction的默认处理方式,即不自动重启被中断的系统调用
        //实际上,不管act.sa_flags值为多少,只要不设置SA_RESTART,sigaction都是按SA_INTERRUPT处理的
    
        sigaction(SIGALRM, &act, NULL);
        alarm(2);
    
        printf("read start
    ");
        nread = read(STDIN_FILENO, buf, sizeof(buf));
        printf("read return
    ");
    
        if ((nread < 0) && (errno == EINTR))
        {
            printf("read return failed, errno is EINTR
    ");
        }
    
        return 0;
    }
    

    使用sigaction + 指定SA_RESTART标志

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <errno.h>
    
    void handler(int s)
    {
        printf("read is interrupt by signal handler
    ");
        return;
    }
    
    int main()
    {
        char buf[10];
        int nread = 0;
        struct sigaction act;
    
        sigemptyset(&act.sa_mask);
        act.sa_handler = handler;
        act.sa_flags = 0;
        act.sa_flags |= SA_RESTART;  //给SIGALRM信号设置SA_RESTART标志
    
        sigaction(SIGALRM, &act, NULL);
        alarm(2);
    
        printf("read start
    ");
        nread = read(STDIN_FILENO, buf, sizeof(buf));
        printf("read return
    ");
    
        if ((nread < 0) && (errno == EINTR))
        {
            printf("read return failed, errno is EINTR
    ");
        }
    
        return 0;
    }
    

    由于对被中断系统调用处理方式的差异性,因此对应用程序来说,与被中断的系统调用相关的问题是:

    • 应用程序无法保证总是知道信号处理函数的注册方式,以及是否设置了SA_RESTART标志
    • 可移植的代码必须显式处理关键函数的出错返回,当函数出错且errno等于EINTR时,可以根据实际需求进行相应处理,比如重启该函数
    int nread = read(fd, buf, 1024);
    
    if (nread < 0)
    {
        if (errno == EINTR)
        {
            //read被中断,其实不应该算作失败,可以根据实际需求进行处理,比如重写调用read,也可以忽略它
        }
        else
        {
            //read真正的读错误
        }
    }
    
  • 相关阅读:
    使用mui框架后,input标签失焦后,会滚到顶部的解决方案
    接口处理请求时间过长,前台响应“服务器超时”的解决办法
    正则匹配手机号码
    使用hexo+github免费搭建个人博客网站超详细教程
    微信小程序获取当前位置 地图定位导航-详细代码
    小程序 获取当前所在地理位置 城市 信息
    溯源:Cesium.Appearance 中的顶点着色器
    【Cesium 颜狗初步】fabric 材质定义与自定义着色器实践
    VM创建虚拟机报错
    zxing.net生成二维码的000026问题
  • 原文地址:https://www.cnblogs.com/songhe364826110/p/11657198.html
Copyright © 2011-2022 走看看