转自:http://www.xuebuyuan.com/1470645.html
最近在工作中遇到了EINTR错误,感到比较困惑,几番研究之后,颇有心得和收获,特记录如下,便于以后查询,也给有同样困惑的朋友们提供一点借鉴。
我们经常在网络编程中会看到这样,当执行一个可能会阻塞的系统调用后,在返回的时候需要检查下错误码(if errno == EINTR),如果是这样的错误,那我们一般会重新执行该系统调用。所以经常的写法是:
repeat:
if(read(fd, buff, size) < 0)
{
if(errno == EINTR)
goto repeat;
else
printf("read failed");
}
但一般我们在读/写磁盘文件的时候却不太会判断这个错误,那我们到底什么时候该判断而什么时候又不要去判断呢?这是个问题。针对这个问题我特意做了一些测试。首先是读磁盘文件,测试代码如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
void hup_handler(int sig)
{
printf(".");
fflush(stdout);
}
int main()
{
int i = 0;
struct sigaction act;
const int buffSize = 1 << 27;
int allocated = 0;
char* buf = NULL;
act.sa_handler = hup_handler;
act.sa_flags = SA_INTERRUPT;
sigemptyset(&act.sa_mask);
allocated = posix_memalign((void**)&buf, getpagesize(), buffSize);
if (0 != allocated)
{
perror("posix_memalign error");
exit(1);
}
sigaction(SIGHUP, &act, NULL);
int fd = open("testfile", O_RDWR | O_DIRECT);
//for (i = 0; i < 1; ++i)
for (;
{
if (lseek(fd, 0, SEEK_SET) == -1)
{
printf("lseek failed: %s\n", strerror(errno));
}
if (read(fd, buf, buffSize) != buffSize)
{
printf("read failed: %s\n", strerror(errno));
}
}
}
代码中注册了信号SIG_HUP的信号处理函数,收到信号的时候应该会进入该处理函数。使用了O_DIRECT方式直接从磁盘读,然后运行该程序。在另外一个终端发送信号
while true; do pkill -HUP read; done
观察read进程,发现read确实进入了信号处理函数(终端输出了"......")但是,程序并没有打印出“read failed”错误,这与我们的预期不太符合,测试发现调用write接口时,现象也一样。
为了进一步验证,我尝试去read终端设备,然后发信号,整个流程跟上面read测试基本相同,测试代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
void int_handler(int signum)
{
printf("....\n");
}
int main()
{
char buf[128];
ssize_t ret;
struct sigaction oldact;
struct sigaction act;
act.sa_handler = int_handler;
//act.sa_flags = SA_INTERRUPT;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
if(-1 == sigaction(SIGHUP, &act, &oldact))
{
exit(1);
}
bzero(buf, 100);
while(true)
{
ret = read(STDIN_FILENO, buf, 10);
if(-1 == ret)
{
perror("read terminal");
//printf("read error%s\n"), strerror(errno);
}
}
return 0;
}
编译运行该函数,然后在另外一个终端为其发送信号:
while true; do pkill -HUP read; done