单线程I/O超时处理
UNIX下的I/O超时处理是一个很常见的问题, 它的通常做法是接收输入(或发送输出)后立刻返回, 如果无输入(或输出)则n秒后定时返回.
一般情况下, 处理UNIX中I/O超时的方式有终端方式, 信号跳转方式和多路复用方式等三种. 本节设计一个定时I/O的例子, 它从文件描述符0中读取一个字符, 当有输入时继续, 或者3秒钟后超时退出,并打印超时信息.
(1) 终端I/O超时方式
利用ioctl函数, 设置文件描述符对应的标准输入文件属性为”接收输入后立刻返回, 如无输入则3秒后定时返回.
[bill@billstone Unix_study]$ cat timeout1.c
#include <unistd.h>
#include <termio.h>
#include <fcntl.h>
int main()
{
struct termio old, new;
char c = 0;
ioctl(0, TCGETA, &old);
new = old;
new.c_lflag &= ~ICANON;
new.c_cc[VMIN] = 0;
new.c_cc[VTIME] = 30; // 设置文件的超时时间为3秒
ioctl(0, TCSETA, &new);
if((read(0, &c, 1)) != 1)
printf("timeout\n");
else
printf("\n%d\n", c);
ioctl(0, TCSETA, &old);
return 0;
}
[bill@billstone Unix_study]$ make timeout1
cc timeout1.c -o timeout1
[bill@billstone Unix_study]$ ./timeout1
x
120
[bill@billstone Unix_study]$ ./timeout1
timeout
(2) 信号与跳转I/O超时方式
在read函数前调用setjmp保存堆栈数据并使用alarm设定3秒定时.
[bill@billstone Unix_study]$ cat timeout2.c
#include <setjmp.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int timeout = 0;
jmp_buf env;
void timefunc(int sig){
timeout = 1;
longjmp(env, 1);
}
int main()
{
char c;
signal(SIGALRM, timefunc);
setjmp(env);
if(timeout == 0){
alarm(3);
read(0, &c, 1);
alarm(0);
printf("%d\n", c);
}
else
printf("timeout\n");
return 0;
}
[bill@billstone Unix_study]$ make timeout2
cc timeout2.c -o timeout2
[bill@billstone Unix_study]$ ./timeout2
v // 需要按Enter健激活输入
118
[bill@billstone Unix_study]$ ./timeout2
timeout
[bill@billstone Unix_study]$
(3) 多路复用I/O超时方式
一个进程可能同时打开多个文件, UNIX中函数select可以同时监控多个文件描述符的输入输出, 进程将一直阻塞, 直到超时或产生I/O为止, 此时函数返回, 通知进程读取或发送数据.
函数select的原型如下:
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
FD_CLR(int fd, fd_set *fdset); // 从fdset中删去文件描述符fd
FD_ISSET(int fd, fd_set *fdset); // 查询文件描述符是否在fdset中
FD_SET(int fd, fd_set *fdset); // 在fdset中插入文件描述符fd
FD_ZERO(fd_set *fdset); // 清空fdset
参数nfds是select监控的文件描述符的时间, 一般为监控的最大描述符编号加1.
类型fd_set是文件描述符集合, 其元素为监控的文件描述符.
参数timeout是描述精确时间的timeval结构,它确定了函数的超时时间,有三种取值情况:
a) NULL. 函数永远等待, 直到文件描述符就绪.
b) 0. 函数不等待, 检查文件描述符状态后立即返回.
c) 其他值. 函数等待文件描述符就绪, 或者定时完成时返回.
函数select将返回文件描述符集合中已准备好的文件总个数. 函数select返回就绪文件描述符数量后, 必须执行read等函数, 否则函数继续返回就绪文件数.
[bill@billstone Unix_study]$ cat timeout3.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int main()
{
struct timeval timeout;
fd_set readfds;
int i;
char c;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(0, &readfds);
i = select (1, &readfds, NULL, NULL, &timeout);
if(i > 0){
read(0, &c, 1);
printf("%d\n", c);
}
else if(i == 0)
printf("timeout\n");
else
printf("error\n");
return 0;
}
[bill@billstone Unix_study]$ make timeout3
cc timeout3.c -o timeout3
[bill@billstone Unix_study]$ ./timeout3
x
120
[bill@billstone Unix_study]$
[bill@billstone Unix_study]$ ./timeout3
timeout
[bill@billstone Unix_study]$
UNIX下的I/O超时处理是一个很常见的问题, 它的通常做法是接收输入(或发送输出)后立刻返回, 如果无输入(或输出)则n秒后定时返回.
一般情况下, 处理UNIX中I/O超时的方式有终端方式, 信号跳转方式和多路复用方式等三种. 本节设计一个定时I/O的例子, 它从文件描述符0中读取一个字符, 当有输入时继续, 或者3秒钟后超时退出,并打印超时信息.
(1) 终端I/O超时方式
利用ioctl函数, 设置文件描述符对应的标准输入文件属性为”接收输入后立刻返回, 如无输入则3秒后定时返回.
[bill@billstone Unix_study]$ cat timeout1.c
#include <unistd.h>
#include <termio.h>
#include <fcntl.h>
int main()
{
struct termio old, new;
char c = 0;
ioctl(0, TCGETA, &old);
new = old;
new.c_lflag &= ~ICANON;
new.c_cc[VMIN] = 0;
new.c_cc[VTIME] = 30; // 设置文件的超时时间为3秒
ioctl(0, TCSETA, &new);
if((read(0, &c, 1)) != 1)
printf("timeout\n");
else
printf("\n%d\n", c);
ioctl(0, TCSETA, &old);
return 0;
}
[bill@billstone Unix_study]$ make timeout1
cc timeout1.c -o timeout1
[bill@billstone Unix_study]$ ./timeout1
x
120
[bill@billstone Unix_study]$ ./timeout1
timeout
(2) 信号与跳转I/O超时方式
在read函数前调用setjmp保存堆栈数据并使用alarm设定3秒定时.
[bill@billstone Unix_study]$ cat timeout2.c
#include <setjmp.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int timeout = 0;
jmp_buf env;
void timefunc(int sig){
timeout = 1;
longjmp(env, 1);
}
int main()
{
char c;
signal(SIGALRM, timefunc);
setjmp(env);
if(timeout == 0){
alarm(3);
read(0, &c, 1);
alarm(0);
printf("%d\n", c);
}
else
printf("timeout\n");
return 0;
}
[bill@billstone Unix_study]$ make timeout2
cc timeout2.c -o timeout2
[bill@billstone Unix_study]$ ./timeout2
v // 需要按Enter健激活输入
118
[bill@billstone Unix_study]$ ./timeout2
timeout
[bill@billstone Unix_study]$
(3) 多路复用I/O超时方式
一个进程可能同时打开多个文件, UNIX中函数select可以同时监控多个文件描述符的输入输出, 进程将一直阻塞, 直到超时或产生I/O为止, 此时函数返回, 通知进程读取或发送数据.
函数select的原型如下:
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
FD_CLR(int fd, fd_set *fdset); // 从fdset中删去文件描述符fd
FD_ISSET(int fd, fd_set *fdset); // 查询文件描述符是否在fdset中
FD_SET(int fd, fd_set *fdset); // 在fdset中插入文件描述符fd
FD_ZERO(fd_set *fdset); // 清空fdset
参数nfds是select监控的文件描述符的时间, 一般为监控的最大描述符编号加1.
类型fd_set是文件描述符集合, 其元素为监控的文件描述符.
参数timeout是描述精确时间的timeval结构,它确定了函数的超时时间,有三种取值情况:
a) NULL. 函数永远等待, 直到文件描述符就绪.
b) 0. 函数不等待, 检查文件描述符状态后立即返回.
c) 其他值. 函数等待文件描述符就绪, 或者定时完成时返回.
函数select将返回文件描述符集合中已准备好的文件总个数. 函数select返回就绪文件描述符数量后, 必须执行read等函数, 否则函数继续返回就绪文件数.
[bill@billstone Unix_study]$ cat timeout3.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int main()
{
struct timeval timeout;
fd_set readfds;
int i;
char c;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(0, &readfds);
i = select (1, &readfds, NULL, NULL, &timeout);
if(i > 0){
read(0, &c, 1);
printf("%d\n", c);
}
else if(i == 0)
printf("timeout\n");
else
printf("error\n");
return 0;
}
[bill@billstone Unix_study]$ make timeout3
cc timeout3.c -o timeout3
[bill@billstone Unix_study]$ ./timeout3
x
120
[bill@billstone Unix_study]$
[bill@billstone Unix_study]$ ./timeout3
timeout
[bill@billstone Unix_study]$