EINTR error
总结:本文介绍了EINTR错误产生的原因,以及如何解决,并在最后给出一个实例,通过该实例可以解决调用ioctl产生的EINTR错误。
一. EINTR
1.EINTR错误产生的原因;
当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能会返回一个EINTR错误。例如:在socket服务器端,设置了信号捕获机制,有子进程当在父进程阻塞于慢系统调用时由父进程捕获到了一个有效信号时,内核会致使accept返回一个EINTR错误(被中断的系统调用)。
2.如何解决:
当碰到EINTR错误的时候,可以采取有一些可以重启的系统调用进行重启,而对于有一些系统调用是不能够重启的。例如:accept、read、wiret、seletc、ioctl和open之类的函数来说是可以进行重启的。不过对于套接字编程中的connect函数是不能进行重启的。若connetct函数返回一个EINTR错误的时候,则不能再次调用它,否则将返回一个错误。针对connect不能重启的特性,必须调用select函数来等待连接完成。
补充:慢系统调用(slow system call)
该术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用永远无法返回,多数网络支持函数都属于这一类。
二实例
该实例解决了调用ioctl时,产生EINTR的解决方法。
- #include <stdio.h>
- #include <errno.h>
- /**
- *function name: xioctl
- *description:
- *Do ioctl and retry if error was EINTR(“A signal was caught during the ioctl() operation”)
- *Note:
- *ENTR:被信号中断函数
- */
- static int xioctl(int fd,int requrest, void *argp)
- {
- int ret;
- do {
- ret = ioctl(fd,request,argp);
- }while(-1 == ret && ENTR==errno);
- return ret;
- }
- }
对于socket接口(指connect/send/recv/accept..等等后面不重复,不包括不能设置非阻塞的如select),在阻塞模式下有可能因为发生信号,返回EINTR错误,由用户做重试或终止。
但是,在非阻塞模式下,是否出现这种错误呢?
对此,重温了系统调用、信号、socket相关知识,得出结论是:不会出现。
首先,
1.信号的处理是在用户态下进行的,也就是必须等待一个系统调用执行完了才会执行进程的信号函数,所以就有了信号队列保存未执行的信号
2.用户态下被信号中断时,内核会记录中断地址,信号处理完后,如果进程没有退出则重回这个地址继续执行
socket接口是一个系统调用,也就是即使发生了信号也不会中断,必须等socket接口返回了,进程才能处理信号。
也就是,EINTR错误是socket接口主动抛出来的,不是内核抛的。socket接口也可以选择不返回,自己内部重试之类的..
那阻塞的时候socket接口是怎么处理发生信号的?
举例
socket接口,例如recv接口会做2件事情,
1.检查buffer是否有数据,有则复制清除返回
2.没有数据,则进入睡眠模式,当超时、数据到达、发生错误则唤醒进程处理
socket接口的实现都差不了太多,抽象说
1.资源是否立即可用,有则返回
2.没有,就等...
对于
1.这个时候不管有没信号,也不返回EINTR,只管执行自己的就可以了
2.采用睡眠来等待,发生信号的时候进程会被唤醒,socket接口唤醒后检查有无未处理的信号(signal_pending)会返回EINTR错误。
所以
socket接口并不是被信号中断,只是调用了睡眠,发生信号睡眠会被唤醒通知进程,然后socket接口选择主动退出,这样做可以避免一直阻塞在那里,有退出的机会。非阻塞时不会调用睡眠。
我们看看linux内核里的实现
linux kernel 3.5.5