• Linux 慢系统调用与EINTR(被中断)


    1. 慢系统调用

    UNP 5.9提到, 慢系统调用(slow system call)指不会立即返回的系统调用, 可能永远阻塞而无法返回. 诸如多数网络支持函数, 包括read/write, connect, accept等, 都属于这一类.
    慢系统调用, 主要分为以下类别:

    1. 读写"慢"设备
      包括pipe, fifo, 终端设备, 网络连接等. 读时, 数据不存在, 需要等待缓冲区有数据输入; 写时, 缓冲区满, 需要等待缓冲区有空闲位置.
      注意: 读写磁盘文件一般不会阻塞.

    2. 打开某些特殊文件时, 需要等待某些条件才能打开
      如打开终端设备, 需要等待连接设备的modern响应, 才能打开

    3. pause和wait系统调用
      pause阻塞进程, 直到收到信号唤醒;
      wait等待任意子进程终止;

    4. 某些ioctl操作

    5. 某些IPC操作
      如pipe, fifo, 没有指定NON_BLOCKING选项时的写操作, 如果管道缓冲区满, write阻塞;
      互斥锁, 条件变量, 信号量, 记录锁等等.

    2. EINTR

    EINTR是什么?
    EINTR指Interrupted system call, 表示由于中断而错误返回.

    适用于满系统调用的基本规则: 当阻塞某个慢系统调用的一个进程捕获某个信号, 且相应信号处理函数返回时, 该系统调用返回一个EINTR错误.
    通常, 也会置errno = EINTR

    3. 如何处理被中断的系统调用?

    编写捕获信号的程序时, 必须对慢系统调用返回EINTR有所准备. 通常, 有三种处理方式:

    1) 人为启动被中断的系统调用
    有些系统调用被中断后能重启, 有些不能. 所谓重启, 是指再次调用系统调用, 对于那些由于中断导致临时返回错误的情况, 可以再次调用获得正确结果; 而对于那些已经修改了内部状态的情况如connect会修改套接字状态, 则不能再次调用.
    如open, read, write, select(源自Berkeley的实现不自动重启select), accept/recvfrom(有些实现不重启) , 能人为重启;
    而如connect不能重启, 因为connect失败时, 可能导致socket状态改变, 不能直接重新调用connect. 可以调用select等待连接完成; 当然也可以创建新socket, 然后再次调用connect.

    下面这段人为重启被中断系统调用的代码, 适用于能重启的系统调用:

    for (; ;) {
        clilen = sizeof (cliaddr);
        if ((connfd = accept(listenfd, (SA*)&cliaddr, &clilen)) < 0) {
            if (errno == EINTR) continue;
            else err_sys("accept error");
        }
    }
    

    2) 安装信号时设置SA_RESTART属性, 实现自动重启(非适用于所有被中断系统调用)
    只能针对设置的信号, 发生捕获返回后, 恢复被中断的系统调用.
    比如, 针对捕获SIGALRM信号导致中断系统调用后, 利用设置SA_RESTART属性自动重启系统调用:

    struct sigaction sa
    
    sa.sa_handler = 
    sigsetempty(&sa.sa_mask);
    
    sa.sa_flags = 0;
    sa.sa_flags |= SA_RESTART;
    
    sigaction(SIGALRM, &sa, NULL);
    

    注意: 并不是所有系统调用都支持SA_RESTART属性, 自动重启, 比如msgsnd/msgrcv(System V队列 消息发送/接收).

    a signal is caught, in which case the system call fails with errno set to  EINTR;see  sig‐
             nal(7).   (msgsnd()  is  never automatically restarted after being interrupted by a signal
             handler, regardless of the setting of the SA_RESTART flag when establishing a signal  han‐
             dler.)
    
          * The calling process catches a signal.  In this case, the system call fails with errno  set
             to  EINTR.  (msgrcv() is never automatically restarted after being interrupted by a signal
             handler, regardless of the setting of the SA_RESTART flag when establishing a signal  han‐
             dler.)
    
    

    3) 忽略信号
    设置信号处置方式为忽略(SIG_IGN), 告知进程不会处理对应信号, 也就不会因为该信号而中断了.
    例如, 忽略SIGALRM信号

    signal(SIGALRM, SIG_IGN);
    

    参考 信号中断 与 慢系统调用 | CSDN

  • 相关阅读:
    Mac下安装brew
    Mac下安装node.js
    Mac下mysql服务端密码重置及环境配置
    Mac配置jdk以及maven
    Mac下卸载jdk
    34个漂亮的应用程序后台管理界面(系列一)
    ViewState
    get和post
    无刷新 分页评论
    isPostBack原理
  • 原文地址:https://www.cnblogs.com/fortunely/p/14850804.html
Copyright © 2020-2023  润新知