linux c單線程I/O超時處理

單線程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]$
發佈了79 篇原創文章 · 獲贊 17 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章