linux c单线程I/O超时处理

本文介绍了在UNIX环境下处理单线程I/O超时的三种常见方法:终端I/O超时、信号与跳转I/O超时及多路复用I/O超时。通过示例代码展示了如何设置超时时间,并在超时或接收到输入时进行相应的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单线程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]$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值