linux中对errno是EINTR的处理
慢系统调用(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的处理的更多相关文章
- linux中errno使用(转)
当linux中的C api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因,在实际编程中用这一招解决了不少 ...
- Linux中errno使用 - [Linux]
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明http://www.blogbus.com/wzgyantai-logs/24470871.html 当linux中的C api函数发 ...
- linux中errno及perror的应用
1 perror 定义在头文件<stdlib.h>中 void perror(const char *s);函数说明 perror ( )用 来 将 上 一 个 函 数 发 生 错 误 的 ...
- Linux编程下EAGAIN和EINTR宏的含义及处理
Linux中的EAGAIN含义 在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中). linux下使用write\send ...
- LINUX 内核代码 errno 错误代码提示 /include/asm/errno.h
首先在自己的程序中#include<errno.h> 添加打印errno的语句 printf("errno is: %d\n",errno); 根据errno的值查错. ...
- Linux中的IO复用接口简介(文件监视?)
I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...
- Linux中线程使用详解
线程与进程为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题. 使用多线程的理由之一是和进程相比,它是一种非常"节俭&qu ...
- linux中脚本扑捉(trap)信号问题
扑捉ctrl+c信号: #!/bin/bash trap ; function trap() { echo "You press Ctrl+C."; echo "Exit ...
- 浅谈Linux中的信号处理机制(二)
首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...
随机推荐
- Python触发异常
我们可以使用raise语句自己触发异常,raise语法格式如下: raise [Exception [, args [, traceback]]] 语句中 Exception 是异常的类型(例如,Na ...
- Linux Ubuntu stty 使用
stty(set tty)命令用于显示和修改当前注册的终端的属性. 该命令是一个用来改变并打印终端行设置的常用命令. stty -a #将所有选项设置的当前状态写到标准输出中 old_stty_set ...
- (转)刚来的大神彻底干掉了代码中的if else...
一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性. 首先可读性,不言而喻,过多的 if-else 代码和嵌套,会使阅读代码的人很难理解到底是什么意思.尤其是那些没有注释的代码. 其次 ...
- Jenkins MultiJob
前提:项目有十几个服务每次发版/更新服务需要一个个去编译 目的:希望能够建立一个任务一次构建可以批量编译很多服务,并且需要输入一个参数指定编译的分支 需要插件: MultiJob 安装插件 1.在Je ...
- centos redhat 安装g++
正确安装命令 : yum install gcc-c++ libstdc++-devel 安装后可以在/bin/找到
- Java学习笔记:GUI基础
一:我们使用到的java GUI的API可以分为3种类: 组件类(component class) 容器类(container class) 辅助类(helper class) 1:组件类:组件类是用 ...
- Discovery直播 | 3D“模”术师,还原立体世界——探秘3D建模服务
通过多张普通的照片重建一个立体逼真的3D物体模型,曾经靠想象实现的事情,现在, 使用HMS Core 3D建模服务即可实现! 3D模型作为物品在数字世界中的孪生体,用户可以自己拍摄.建模并在终端直观感 ...
- etcd安装常用操作
etcd安装 etcd 是基于 Raft 的分布式 key-value 存储系统,由 CoreOS 开发,常用于服务发现.共享配置以及并发控制(如 leader 选举.分布式锁等).kubernete ...
- Qt Creator 常用快捷键 详细总结
下面是我总结的一些Qt Creator 常用快捷键 ,可以大大提高我们使用Qt开发项目的效率!! Qt Creator 常用快捷键 快捷键 介绍 F1 查看帮助文档 Shift + F2 函数的声明和 ...
- leetcode 剪绳子系列
### 剪绳子一 利用动态规划 状态转移方程 为啥是这个样子?首先 代表 长度为i的绳子被剪去j,且继续剪(子问题) 表示长度为i的绳子被剪去j,不剪了的乘积 注意初始化: n<2 f=0 ...