linux系统中socket错误码:EINTR和EAGAIN的处理

article/2025/10/6 13:44:17

 

目录

人为重启被中断的系统调用

安装信号时设置 SA_RESTART属性

忽略信号


永远阻塞的系统调用,被信号中断,导致其不继续等待,转而去执行signal_handler

1、什么是慢系统调用?

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

慢系统调用可以被永久阻塞,包括以下几个类别:
(1)读写‘慢’设备(包括pipe,终端设备,网络连接等)。读时,数据不存在,需要等待写时,缓冲区满或其他原因,需要等待
(2)当打开某些特殊文件时,需要等待某些条件,才能打开。例如:打开中断设备时,需要等到连接设备的modem响应才能完成。
(3)pause和wait函数。pause函数使调用进程睡眠,直到捕获到一个信号。wait等待子进程终止。
(4)某些ioctl操作。
(5)某些IPC操作。

2、EINTR错误产生的原因-(阻塞的系统调用、或者非阻塞的系统调用)

如果进程在一个慢系统调用(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调用不再阻塞而是被中断,就会调用返回错误(一般为-1)&&设置errno为EINTR(相应的错误描述为“Interrupted system call”)。

如下表所示的系统调用就会产生EINTR错误,当然不同的函数意义也不同。

系统调用函数errno为EINTR表征的意义
write由于信号中断,没写成功任何数据。The call was interrupted by a signal before any data was written.
open由于信号中断,没读到任何数据。The call was interrupted by a signal before any data was read.
recv 
sem_wait函数调用被信号处理函数中断。The call was interrupted by a signal handler.

3、解决办法

既然系统调用会被中断,那么别忘了要处理被中断的系统调用。有三种处理方式:

解决方法1:重启被中断的系统调用

当碰到EINTR错误的时候,有一些可以重启的系统调用要进行重启,而对于有一些系统调用是不能够重启的。例如:accept、read、write、select、和open之类的函数来说,是可以进行重启的。不过对于套接字编程中的connect函数是不能重启的,若connect函数返回一个EINTR错误的时候,我们不能再次调用它,否则将立即返回一个错误。针对connect不能重启的处理方法是,必须调用select来等待连接完成。

理解“重启”?一些IO系统调用执行时,如 read 等待输入期间,如果收到一个信号,系统将中断read, 转而执行信号处理函数. 当信号处理返回后, 系统遇到了一个问题: 是重新开始这个系统调用? 还是让系统调用失败?早期UNIX系统的做法是:中断系统调用,并让系统调用失败, 比如read返回 -1, 同时设置 errno 为EINTR中断了的系统调用是没有完成的调用,它的失败是临时性的,如果再次调用则可能成功,这并不是真正的失败,所以要对这种情况进行处理, 典型的方式为“重启”,采用accept函数为例子,代码如下

ACCEPT:clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);if (clifd == -1) {if (errno == EINTR) {goto ACCEPT;} else {fprintf(stderr, "accept fail,error:%s\n", strerror(errno));return -1;}}

解决方法2:安装信号时设置 SA_RESTART属性(该方法对有的系统调用无效)

struct sigaction action;  action.sa_handler = handler_func;  sigemptyset(&action.sa_mask);  action.sa_flags = 0;  /* 设置SA_RESTART属性 */  action.sa_flags |= SA_RESTART;  sigaction(SIGALRM, &action, NULL); 

解决方法3: 忽略信号(让系统不产生信号中断)

struct sigaction action;  action.sa_handler = SIG_IGN;  sigemptyset(&action.sa_mask);  sigaction(SIGALRM, &action, NULL); 

EAGAIN-(一般用于非阻塞的系统调用

非阻塞的系统调用,由于资源限制/不满足条件,导致返回值为EAGAIN

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

如:首先是把套接字设置为异步的了,然后在使用write发送数据时采取的方式是循环发送大量的数据;由于是异步的,write\send将要发送的数据提交到发送缓冲区后是立即返回的,并不需要对端确认数据已接收。在这种情况下是很有可能出现发送缓冲区被填满,导致write\send无法再向缓冲区提交要发送的数据。因此就产生了Resource temporarily unavailable的错误(资源暂时不可用),EAGAIN 的意思也很明显,就是要你再次尝试。

从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。

如:以 O_NONBLOCK的标志打开文件/socket/FIFO,如果连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。
又例如,当一个系统调用(比如fork)因为没有足够的资源(比如虚拟内存)而执行失败,返回EAGAIN提示其再调用一次(也许下次就能成功)。

Linux - 非阻塞socket编程处理EAGAIN错误

在linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),这是什么意思? ⇒ ⇒ ⇒ 这表明在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。对非阻塞socket而言,EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。

iReadSizeOnce=read(iOpenCom,RxBuf+iReadSize,1024);
if (iReadSizeOnce != ZERO)
{if (iReadSizeOnce != EAGAIN){continue;}else{//stCComApiLog.LogError("读串口操作错误");return(FUN_ERROR);}
}

借鉴于:

1  http://blog.csdn.net/yanook/article/details/7226019  慢系统调用函数如何处理中断信号EINTR
2  http://blog.csdn.net/benkaoya/article/details/17262053 信号中断 与 慢系统调用
3  http://1.guotie.sinaapp.com/?p=235    socket,accept,connect出现EINTR错误的解决方法

慢系统调用:可能永远阻塞的系统调用,这很关键,不适用于非诸塞的情况。永远阻塞的系统调用是指调用永远无法返回,多数网络支持函数都属于这一类。如:若没有客户连接到服务器上,那么服务器的accept调用就会一直阻塞。
(以下为抄袭2原文)
EINTR说明:如果进程在一个慢系统调用(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调用被中断,调用返回错误,设置errno为EINTR(相应的错误描述为“Interrupted system call”)。

怎么看哪些系统条用会产生EINTR错误呢?man 7 signal,在ubuntu 10.04上可以查看,哪些系统调用会产生 EINTR错误。

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

既然系统调用会被中断,那么别忘了要处理被中断的系统调用。有三种处理方式:

◆ 人为重启被中断的系统调用

◆ 安装信号时设置 SA_RESTART属性(该方法对有的系统调用无效)

◆  忽略信号(让系统不产生信号中断)

人为重启被中断的系统调用

人为当碰到EINTR错误的时候,有一些可以重启的系统调用要进行重启,而对于有一些系统调用是不能够重启的。例如:accept、read、write、select、和open之类的函数来说,是可以进行重启的。不过对于套接字编程中的connect函数我们是不能重启的,若connect函数返回一个EINTR错误的时候,我们不能再次调用它,否则将立即返回一个错误。针对connect不能重启的处理方法是,必须调用select来等待连接完成。

这里的“重启”怎么理解?

一些IO系统调用执行时,如 read 等待输入期间,如果收到一个信号,系统将中断read, 转而执行信号处理函数. 当信号处理返回后, 系统遇到了一个问题: 是重新开始这个系统调用, 还是让系统调用失败?早期UNIX系统的做法是, 中断系统调用,并让系统调用失败, 比如read返回 -1, 同时设置 errno 为EINTR中断了的系统调用是没有完成的调用,它的失败是临时性的,如果再次调用则可能成功,这并不是真正的失败,所以要对这种情况进行处理, 典型的方式为:

 

connect处理方式,抄袭3原文,没有测试过,处理方法是对的。
connect的问题,当connect遇到EINTR错误时,不能向上面那样重新进入循环处理,原因是,connect的请求已经发送向对方,正在等待对方回应,这是如果重新调用connect,而对方已经接受了上次的connect请求,这一次的connect就会被拒绝,因此,需要使用select或poll调用来检查socket的状态,如果socket的状态就绪,则connect已经成功,否则,视错误原因,做对应的处理。

connect处理方式,抄袭3原文,没有测试过,处理方法是对的。
connect的问题,当connect遇到EINTR错误时,不能向上面那样重新进入循环处理,原因是,connect的请求已经发送向对方,正在等待对方回应,这是如果重新调用connect,而对方已经接受了上次的connect请求,这一次的connect就会被拒绝,因此,需要使用select或poll调用来检查socket的状态,如果socket的状态就绪,则connect已经成功,否则,视错误原因,做对应的处理。

#include poll.hint check_conn_is_ok(socket_t sock) {struct pollfd fd;int ret = 0;socklen_t len = 0;fd.fd = sock;fd.events = POLLOUT;while ( poll (&fd, 1, -1) == -1 ) {if( errno != EINTR ){perror("poll");return -1;}}len = sizeof(ret);if ( getsockopt (sock, SOL_SOCKET, SO_ERROR,&ret,&len) == -1 ) {perror("getsockopt");return -1;}if(ret != 0) {fprintf (stderr, "socket %d connect failed: %s\n",sock, strerror (ret));return -1;}return 0;
}

在调用connect时,这样使用:

#include erron.h....
if(connnect()) {if(errno == EINTR) {if(check_conn_is_ok() < 0) {perror();return -1;}else {printf("connect is success!\n");}}else {perror("connect");return -1;}
}

我一般使用continue或者goto来处理。

安装信号时设置 SA_RESTART属性

我们还可以从信号的角度来解决这个问题,  安装信号的时候, 设置 SA_RESTART属性,那么当信号处理函数返回后, 不会让系统调用返回失败,而是让被该信号中断的系统调用将自动恢复。

struct sigaction action;  action.sa_handler = handler_func;  
sigemptyset(&action.sa_mask);  
action.sa_flags = 0;  
/* 设置SA_RESTART属性 */  
action.sa_flags |= SA_RESTART;  sigaction(SIGALRM, &action, NULL);  

但注意,并不是所有的系统调用都可以自动恢复。如msgsnd喝msgrcv就是典型的例子,msgsnd/msgrcv以block方式发送/接收消息时,会因为进程收到了信号而中断。此时msgsnd/msgrcv将返回-1,errno被设置为EINTR。且即使在插入信号时设置了SA_RESTART,也无效。在man msgrcv中就有提到这点:

msgsnd and msgrcv are never automatically restarted after being interrupted by a signal handler, regardless of the setting  of the SA_RESTART flag when establishing a signal  handler.

 

忽略信号

当然最简单的方法是忽略信号,在安装信号时,明确告诉系统不会产生该信号的中断。

struct sigaction action;  action.sa_handler = SIG_IGN;  
sigemptyset(&action.sa_mask);  sigaction(SIGALRM, &action, NULL);  
#include   
#include   
#include   
#include   
#include   
#include   void sig_handler(int signum)  
{  printf("in handler\n");  sleep(1);  printf("handler return\n");  
}  int main(int argc, char **argv)  
{  char buf[100];  int ret;  struct sigaction action, old_action;  action.sa_handler = sig_handler;  sigemptyset(&action.sa_mask);  action.sa_flags = 0;  /* 版本1:不设置SA_RESTART属性 * 版本2:设置SA_RESTART属性 */  //action.sa_flags |= SA_RESTART;  sigaction(SIGALRM, NULL, &old_action);  if (old_action.sa_handler != SIG_IGN) {  sigaction(SIGALRM, &action, NULL);  }  alarm(3);  bzero(buf, 100);  ret = read(0, buf, 100);  if (ret == -1) {  perror("read");  }  printf("read %d bytes:\n", ret);  printf("%s\n", buf);  return 0;  
}  

在ubuntu 10.04 上测试结果:
不设置SA_RESTART,执行结果如下:

 

说明接受信号处理完成以后,主函数收到EINTR信号,read函数返回-1,退出
设置SA_RESTART,执行结果如下:

说明设置SA_RESTART参数以后,自动重新调用read函数,没有体现在应用层代码中,在应用层看来,这个EINTR没有造成任何影响。

个人认为下面的总结很重要:

慢系统调用(slow system call)会被信号中断,系统调用函数返回失败,并且errno被置为EINTR(错误描述为“Interrupted system call”)。

处理方法有以下三种:①人为重启被中断的系统调用;②安装信号时设置 SA_RESTART属性;③忽略信号(让系统不产生信号中断)。

有时我们需要捕获信号,但又考虑到第②种方法的局限性(设置 SA_RESTART属性对有的系统无效,如msgrcv),所以在编写代码时,一定要“人为重启被中断的系统调用”。


http://chatgpt.dhexx.cn/article/KeGeo4kD.shtml

相关文章

iosetup mysql_InnoDB: Error: io_setup() failed with EAGAIN

最近安装好了MySQL之后&#xff0c;在启动MySQL服务时无法正常启动MySQL。提示没有更新/var/lib/mfailedZDB.pid并退出。该MySQL与Oracle位于同一主机。有些内核参数进行过调整应该也是使用与MySQL。下面是该问题的具体描述。 1、故障现象SZDB:/usr/src/mysql_src # /etc/init.…

blocking socket 读写返回 EAGAIN

非阻塞的 socket 读写操作返回 EAGAIN&#xff0c;表示当前已经读完内核缓冲区或者写满内核缓冲区&#xff0c;需要等待下一次 select/poll/epoll 事件到来时再操作。 对于阻塞的 socket 读写操作&#xff0c;如果内核缓冲区是空&#xff0c;read 将一直阻塞&#xff1b;如果不…

【Linux Socket C++】为什么IO复用需要用到非阻塞IO?EAGAIN的简单介绍与应用

目录 为什么IO复用需要非阻塞的IO EAGAIN的介绍 EAGAIN的应用 为什么IO复用需要非阻塞的IO 我们可以先看一下官方的回答&#xff1a; 在Linux命令行输入&#xff1a;man 2 select 找到[BUGS]&#xff0c;如下&#xff1a; 官方给予的回答是这样的&#xff1a; Under Lin…

avcodec_receive_frame始终返回EAGAIN

今天我们研究一个问题&#xff1a; avcodec_receive_frame()始终返回EAGAIN 根本的解决方案还需要深入debug&#xff0c;但是这个函数很太复杂&#xff0c;需要些时间和耐心&#xff1b; 目前在不考虑编解码性能的情况下&#xff0c;能work around的方法只有一个&#xff0c;那…

APK加固(梆梆助手)

前言&#xff1a;朋友在使用梆梆时出现Apk加固后安装失败的现象&#xff0c;所以自己写篇小白文 1.进入梆梆官网&#xff08;注册&#xff09; 2.下载梆梆助手&#xff08;点击加固工具&#xff09; 本人电脑是Windows系统&#xff08;下载&#xff09; 3.安装后&#xff0c…

使用360进行apk加固并进行2次签名整体流程

因新版360加固助手需要付费才能进行自动签名&#xff0c;故只能自己手动来签名了~ 1.使用Android studio进行首次签名并打包apk 首先选择build下该选项 选择apk 如果没有key&#xff0c;则点击新建 需要输入key存储的位置&#xff0c;key store密码&#xff0c;key别名&#…

Android-APK加固-简单版

Android-APK加固-简单版 Proguard的使用与配置介绍开启proguard常用配置 加固大体思路 源码&#xff08;浅析&#xff09;思路 撸码解密工具类-AES&#xff08;解密时用&#xff09;工具类-Zip&#xff08;压缩、解压&#xff09;工具类-Utils&#xff08;反射操作&#xff09;…

Android Apk加固后手动签名

手动签名 &#xff1a; 不用任何第三方可视化工具签名 &#xff0c;使用命令做签名。手动签名原因&#xff1a;以前加固签名都是使用第三方工具操作&#xff0c;最近发现工具都开始收费了&#xff0c;免费的羊毛没得薅了&#xff0c;收费价格极高 5000/年/App, &#xff08;加固…

手写apk加固

手写apk加固 加壳解压原apk并加密重命名dex文件对壳文件操作打包压缩成apk文件签名 脱壳运行解压原apk, 解密原dex文件加载原dex文件 demo下载 apk加固的目的其实就是对app的核心代码做防护工作&#xff0c;避免被其他人反编译&#xff1b; 废话不多说了&#xff0c;直接开始! …

android apk 加固后重新签名

针对于加固平台在加固的过程中不能配置签名文件&#xff0c;加固后的apk需要进行重新签名才能安装&#xff0c;并发布到应用市场。 第一步&#xff0c;用AS对项目进行打包&#xff0c;生成签名的apk文件。 第二步&#xff0c;使用加固平台&#xff0c;对apk包进行加固&#xff…

Android Apk加固原理解析

前言 为什么要加固 对APP进行加固&#xff0c;可以有效防止移动应用被破解、盗版、二次打包、注入、反编译等&#xff0c;保障程序的安全性、稳定性。 常见的加固方案有很多&#xff0c;本文主要介绍如果通过对dex文件进行加密来达到apk加固的目的&#xff1b; APK加固整体…

apk加固后再签名

目录 前言v1签名v1v2签名 前言 apk更新之前需要做安全检测&#xff0c;检测之前一版会做加固处理&#xff0c;加固后还需要重新进行签名。本文介绍一下v1签名和v1v2签名两种方式。 有文章说需要把apk原来的签名文件&#xff0c;即META-INF文件夹删除&#xff0c;实测不删好像也…

Android apk 加固混淆的作用之解决apk报毒

现在市面上对apk的安全合规管控越来越严格了&#xff0c;也就要求了apk在上架之前一定要做合规检测和加固处理。对apk就是加固的好处&#xff0c;可以提高apk的安全性&#xff0c;提高apk被逆向分析破解的门槛&#xff0c;同时通过加固保护可以提高过安全合规的检测。由于APP加…

简书 android 加固,Android apk加固(加壳)整理

一、Dex加壳由来 最近在学习apk加密&#xff0c;在网上看了一篇《Android中的Apk的加固(加壳)原理解析和实现》&#xff0c;我发现原文把整个apk都写入到dex文件中&#xff0c;如果apk小还好&#xff0c;当原APK大于200M&#xff0c;客户端解壳很费劲&#xff0c;打开后应用就卡…

019 Android加固之APK加固的原理和实现

文章目录 前言加载Activity遇到的问题APK的启动过程替换ClassLoader流程获取ActivityThread类对象获取AppBindData类对象mBoundApplication获取LoadedApk类对象info获取info对象中的ClassLoader 设计傀儡dex文件手工加固APK代码实现APK加固实现步骤 总结 前言 动态加载dex之后…

【Android 安全】Android 应用 APK 加固总结 ( 加固原理 | 应用加固完整的实现方案 | 源码资源 )

文章目录 一、 APK 加固原理1、 Android 应用反编译2、 ProGuard 混淆3、 多 dex 加载原理4、 代理 Application 开发5、Java 工具开发6、Application 替换 二、 应用加固完整的实现方案1、 代理 Application( 1 ) ProxyApplication( 2 ) OpenSSL 解码 Kotlin 类( 3 ) 反射工具…

android资源加固,Android apk加固实现原理

apk加固是每一个app发布之前必须要做的事情;如果一个apk没有加固那么别人就很容易被别人反编译&#xff0c;看到这其中的原码&#xff0c;虽然现在有代码混淆、把业务写到native层&#xff0c;但是这都是治标不治本。反编译的技术在更新&#xff0c;那么保护Apk的技术就不能停止…

Android中Apk加固代码实现

前言&#xff1a;上一篇博客已经把Apk加固的思路详细的介绍过了&#xff0c;也开始创建了一个空的demo进行&#xff0c;然后在项目中添加一个代理module&#xff08;解密&#xff0c;和系统源码交互功能&#xff09;和tools工具加密Java library 的module &#xff0c;这里开始…

Android APK加固原理

一、前言 Android作为开源框架&#xff0c;开放之余&#xff0c;所要面临的就是安全问题&#xff0c;世间之事&#xff0c;有正就有邪&#xff0c;有攻就有守&#xff0c;作为开发者虽然不需要进入专业安全领域&#xff0c;但还是需要掌握基本的安全常识和原理。 二、加壳 加…

APK加固原理详解

一、前言 之前使用的360加固&#xff0c;挺好用的&#xff0c;从2021年底的时候限制每天每个账号仅上传2次apk&#xff08;免费的&#xff0c;不知道VIP的是不是这样&#xff09;。通过这个事情&#xff0c;感觉技术还是掌握在自己手里稳妥点&#xff0c;不用受制于人&#xf…