慢系统调用(slow system call):此术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用有可能永远无法返回,多数网络支持函数都属于这一类。如:若没有客户连接到服务器上,那么服务器的accept调用就没有返回的保证。

EINTR错误的产生:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。例如:在socket服务器端,设置了信号捕获机制,有子进程,当在父进程阻塞于慢系统调用时由父进程捕获到了一个有效信号时,内核会致使accept返回一个EINTR错误(被中断的系统调用)。

当碰到EINTR错误的时候,可以采取有一些可以重启的系统调用要进行重启,而对于有一些系统调用是不能够重启的。例如:accept、read、write、select、和open之类的函数来说,是可以进行重启的。不过对于套接字编程中的connect函数我们是不能重启的,若connect函数返回一个EINTR错误的时候,我们不能再次调用它,否则将立即返回一个错误。针对connect不能重启的处理方法是,必须调用select来等待连接完成。
 
在 linux 或者 unix 环境中, errno 是一个十分重要的部分。在调用的函 数出现问题的时候,我们可以通过 errno 的值来确定出错的原因,这就会 涉及到一个问题,那就是如何保证 errno 在多线程或者进程中安全?我们希望在多线程或者进程中,每个线程或者进程都拥有自己独立和唯一的一个 errno ,这样就能够保证不会有竞争条 件的出现。一般而言,编译器会自动保证 errno 的安全性,但是为了妥善期间,我们希望在写 makefile 的时 候把 _LIBC_REENTRANT 宏定义,比 如我们在检查 <bits/errno.h> 文件中发现如下的定义:

C代码

    # ifndef __ASSEMBLER__
    /* Function to get address of global `errno' variable. */
    extern int *__errno_location (void) __THROW __attribute__ ((__const__));
     
     
    # if !defined _LIBC || defined _LIBC_REENTRANT
    /* When using threads, errno is a per-thread value. */
    # define errno (*__errno_location ())
    # endif
    # endif /* !__ASSEMBLER__ */
    #endif /* _ERRNO_H */

也就是说,在没有定义 __LIBC 或者定义 _LIBC_REENTRANT 的时候, errno 是多线程 / 进程安全的。
一般而言, __ASSEMBLER__, _LIBC 和 _LIBC_REENTRANT 都不会被编译器定义,但是如果我们定义 _LIBC_REENTRANT 一次又何妨那?
为了检测一下你编译器是否定义上述变量,不妨使用下面一个简单程序。
C代码

    #include <stdio.h>
    #include <errno.h>
     
    int main( void )
    {
    #ifndef __ASSEMBLER__
    printf( "Undefine __ASSEMBLER__\n" );
    #else
    printf( "define __ASSEMBLER__\n" );
    #endif
     
    #ifndef __LIBC
    printf( "Undefine __LIBC\n" );
    #else
    printf( "define __LIBC\n" );
    #endif
     
    #ifndef _LIBC_REENTRANT
    printf( "Undefine _LIBC_REENTRANT\n" );
    #else
    printf( "define _LIBC_REENTRANT\n" );
    #endif
     
    return 0;
    }

希望读者在进行移植的时候,读一下相关的 unix 版本的 <bits/errno.h> 文 件,来确定应该定义什么宏。不同的 unix 版本可能存在着一些小的差别!
 
有时候,在调用系统调用时,可能会接收到某个信号而导致调用退出。譬如使用system调用某个命令之后该进程会接收到SIGCHILD信号,然后如果这个进程的线程中有慢系统调用,那么接收到该信号的时候可能就会退出,返回EINTR错误码。
EINTR
  linux中函数的返回状态,在不同的函数中意义不同:
1)write
  表示:由于信号中断,没写成功任何数据。
  The call was interrupted by a signal before any data was written.
2)read
  表示:由于信号中断,没读到任何数据。
  The call was interrupted by a signal before any data was read.
3)sem_wait
  函数调用被信号处理函数中断
  The call was interrupted by a signal handler.
4)recv
  由于信号中断返回,没有任何数据可用。
  function was interrupted by a signal that was caught, before any data was available.
 
调用系统调用的时候,有时系统调用会被中断.此时,系统调用会返回-1,并且错误码被置为EINTR.但是,有时并不将这样的情况作为错误.有两种处理方法:

1.如果错误码为EINTR则重新调用系统调用,例如Postgresql中有一段代码:

    retry1:
    if (send(port->sock, &SSLok, 1, 0) != 1)
    {
       if (errno == EINTR)
       goto retry1; /* if interrupted, just retry */
    }

2.重新定义系统调用,忽略错误码为EINTR的情况.例如,Cherokee中的一段代码:

    int cherokee_stat (const char *restrict path, struct stat *buf)
    {
      int re;
      do {
         re = stat (path, buf);
      } while ((re == -1) && (errno == EINTR));
      return re;
    }

今天使用select调用的时候总是出错,返回EINTR错误->Interrupted system call,主要是由于代码中调用了signal捕获子进程退出信号SIGCHLD的处理,故我采用忽略EINTR的策略,代码改为如下解决

        signal(SIGCHLD,sig_wait);
        while(1){
            rdset=ctlset;        
    /* 如果可用处理子进程等于0个,那么select就暂时不再监听连接描述符,由listen函数的backlog控制连接数目队列
            if(iavailable_child <= 0)
                FD_CLR(ifdlisten, &rdset);    // turn off if no available children
    */            
            iselectret=select(ifdmax+1, &rdset,NULL,NULL,NULL);    
            if(iselectret<0){            
                if(errno==EINTR){
    //                printf("Receives the interrupt signal\n");
                    continue;
                }
                else{
                    printf("select error,will be exit,error msg:%s \n",strerror(errno));    
                    exit(-1);
                }
            }
---------------------  
作者:奔跑吧,行者  
来源:CSDN  
原文:https://blog.csdn.net/hnlyyk/article/details/51444617  
版权声明:本文为博主原创文章,转载请附上博文链接!

linux中对errno是EINTR的处理的更多相关文章

  1. linux中errno使用(转)

    当linux中的C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因,在实际编程中用这一招解决了不少 ...

  2. Linux中errno使用 - [Linux]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://www.blogbus.com/wzgyantai-logs/24470871.html 当linux中的C api函数发 ...

  3. linux中errno及perror的应用

    1 perror 定义在头文件<stdlib.h>中 void perror(const char *s);函数说明 perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 ...

  4. Linux编程下EAGAIN和EINTR宏的含义及处理

    Linux中的EAGAIN含义   在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中). linux下使用write\send ...

  5. LINUX 内核代码 errno 错误代码提示 /include/asm/errno.h

    首先在自己的程序中#include<errno.h> 添加打印errno的语句 printf("errno is: %d\n",errno); 根据errno的值查错. ...

  6. Linux中的IO复用接口简介(文件监视?)

    I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...

  7. Linux中线程使用详解

    线程与进程为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题. 使用多线程的理由之一是和进程相比,它是一种非常"节俭&qu ...

  8. linux中脚本扑捉(trap)信号问题

    扑捉ctrl+c信号: #!/bin/bash trap ; function trap() { echo "You press Ctrl+C."; echo "Exit ...

  9. 浅谈Linux中的信号处理机制(二)

    首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...

随机推荐

  1. 问题:两个对象值相同(x.equals(y) == true),但是可能存在hashCode不同吗?

    面试官的考察点 这道题仍然是考察JVM层面的基本知识,面试官认为,基本功扎实,才能写出健壮性和稳定性很高的代码. 涉及到的技术知识 (x.equals(y)==true),这段代码,看起来非常简单,但 ...

  2. STM32 学习笔记之中断应用概览--以f103为例

    异常类型 F103 在内核水平上搭载了一个异常响应系统, 支持为数众多的系统异常和外部中断.其中系统异常有8 个(如果把Reset 和HardFault 也算上的话就是10 个),外部中断有60个.除 ...

  3. Python课程笔记(七)

    今天学习神奇的海龟,非常有意思,还有很多图片想去绘制,分享一个turtle绘图网站: https://www.python123.io/index/turtles/latest , 要是可以分享出源码 ...

  4. 洛谷 P2120 [ZJOI2007] 仓库建设

    链接: P2120 题意: 有 \(n\) 个点依次编号为 \(1\sim n\).给出这 \(n\) 个点的信息,包括位置 \(x_i\),所拥有的的物品数量 \(p_i\),在此建设一个仓库的费用 ...

  5. 【做题记录】max-min+1=len 区间计数

    (来源:XJ高质量原创题) 原题地址 弱化版:CF526F Pudding Monsters 弱化版 题意:\(n\times n\) 的棋盘上有 \(n\) 颗棋子,每行每列都有且仅有一颗棋子,求出 ...

  6. 『学了就忘』Linux基础 — 15、了解Linux系统的目录结构

    目录 1.一级目录说明 (1)一级目录列表 (2)/bin/和/sbin/目录说明 (3)/boot/目录说明 (4)/lib/和/lib64/目录说明 (5)/lost+found/目录说明 (6) ...

  7. 手把手教你学Dapr - 4. 服务调用

    上一篇:手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序 介绍 通过使用服务调用,您的应用程序可以使用标准的gRPC或HTTP协议与其他应用程序可靠.安全地通信. 为什么不直接用Ht ...

  8. github上传和删除文件(三)

    上传文件: git init git add * git commit -m "description" //git remote rm origin 或查看当前 git remo ...

  9. k8s endpoints controller分析

    k8s endpoints controller分析 endpoints controller简介 endpoints controller是kube-controller-manager组件中众多控 ...

  10. C# 计算农历日期方法(2021版)

    解决问题 旧版农历获取方法报错,会有 到 2021年 m数组越界了 if (LunarData[m] < 4095) 此方法可以解决 主体代码 public static class China ...