色yeye在线视频观看_亚洲人亚洲精品成人网站_一级毛片免费播放_91精品一区二区中文字幕_一区二区三区日本视频_成人性生交大免费看


Linux下多路復用I/O接口

分享到:
           

    多路復用

    1.函數說明

    前面的fcntl()函數解決了文件的共享問題,接下來該處理I/O復用的情況了。

    總的來說,I/O處理的模型有5種。

    ● 阻塞I/O模型:在這種模型下,若所調用的I/O函數沒有完成相關的功能,則會使進程掛起,直到相關數據到達才會返回。如常見對管道設備、終端設備和網絡設備進行讀寫時經常會出現這種情況。
    ● 非阻塞I/O模型:在這種模型下,當請求的I/O操作不能完成時,則不讓進程睡眠,而且立即返回。非阻塞I/O使用戶可以調用不會阻塞的I/O操作,如open()、write()和read()。如果該操作不能完成,則會立即返回出錯(如打不開文件)或者返回0(如在緩沖區中沒有數據可以讀取或者沒空間可以寫入數據)。
    ● I/O多路轉接模型:在這種模型下,如果請求的I/O操作阻塞,且它不是真正阻塞I/O,而是讓其中的一個函數等待,在此期間,I/O還能進行其他操作。如本小節要介紹的select()和poll()函數,就是屬于這種模型。
    ● 信號驅動I/O模型:在這種模型下,進程要定義一個信號處理程序,系統可以自動捕獲特定信號的到來,從而啟動I/O。這是由內核通知用戶何時可以啟動一個I/O操作決定的。

    它是非阻塞的。當有就緒的數據時,內核就向該進程發送SIGIO信號。 無論我們如何處理SIGIO信號,這種模型的好處是當等待數據到達時,可以不阻塞。主程序繼續執行,只有收到SIGIO信號時才去處理數據即可。

    ● 異步I/O模型:在這種模型下,進程先讓內核啟動I/O操作,并在整個操作完成后通知該進程。這種模型與信號驅動模型的主要區別在于:信號驅動I/O是由內核通知我們何時可以啟動一個I/O操作,而異步I/O模型是由內核通知進程I/O操作何時完成的。現在,并不是所有的系統都支持這種模型。

    可以看到,select()和poll()的I/O多路轉接模型是處理I/O復用的一個高效的方法。它可以具體設置程序中每一個所關心的文件描述符的條件、希望等待的時間等,從select()和poll()函數返回時,內核會通知用戶已準備好的文件描述符的數量、已準備好的條件(或事件)等。通過使用select()和poll()函數的返回結果(可能是檢測到某個文件描述符的注冊事件或是超時,或是調用出錯),就可以調用相應的I/O處理函數了。

    2.函數格式

    select()函數的語法要點如表2.8所示。

表2.8 select()函數語法要點

所需頭文件 #include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
函數原型 int select(int numfds, fd_set *readfds, fd_set *writefds,
fd_set *exeptfds, struct timeval *timeout)
函數傳入值 numfds:該參數值為需要監視的文件描述符的大值加1
readfds:由select()監視的讀文件描述符集合
writefds:由select()監視的寫文件描述符集合
exeptfds:由select()監視的異常處理文件描述符集合
timeout NULL:永遠等待,直到捕捉到信號或文件描述符已準備好為止
具體值:struct timeval類型的指針,若等待了timeout時間還沒有檢測到任何文件描符準備好,就立即返回
0:從不等待,測試所有指定的描述符并立即返回
函數返回值 成功:準備好的文件描述符
0:超時; 1:出錯

    可以看到,select()函數根據希望進行的文件操作對文件描述符進行了分類處理,這里對文件描述符的處理主要涉及4個宏函數,如表2.9所示。

表2.9 select()文件描述符處理函數

FD_ZERO(fd_set *set) 清除一個文件描述符集
FD_SET(int fd, fd_set *set) 將一個文件描述符加入文件描述符集中
FD_CLR(int fd, fd_set *set) 將一個文件描述符從文件描述符集中清除
FD_ISSET(int fd, fd_set *set) 如果文件描述符fd為fd_set集中的一個元素,則返回非零值,可以用于調用select()后測試文件描述符集中的哪個文件描述符是否有變化

    一般來說,在每次使用select()函數之前,首先使用FD_ZERO()和FD_SET()來初始化文件描述符集(在需要重復調用select()函數時,先把一次初始化好的文件描述符集備份下來,每次讀取它即可)。在select()函數返回后,可循環使用FD_ISSET()來測試描述符集,在執行完對相關文件描述符的操作后,使用FD_CLR()來清除描述符集。

    另外,select()函數中的timeout是一個struct timeval類型的指針,該結構體如下所示:

    struct timeval
    {
        long tv_sec; /* 秒 */
        long tv_unsec; /* 微秒 */
    }

    可以看到,這個時間結構體的精確度可以設置到微秒級,這對于大多數的應用而言已經足夠了。
    poll()函數的語法要點如表2.10所示。

表2.10 poll()函數語法要點

所需頭文件     #include <sys/types.h>
    #include <poll.h>
函數原型 int poll(struct pollfd *fds, int numfds, int timeout)
函數傳入值 fds:struct pollfd結構的指針,用于描述需要對哪些文件的哪種類型的操作進行監控
struct pollfd
{
  int fd; /* 需要監聽的文件描述符 */
  short events; /* 需要監聽的事件 */
  short revents; /* 已發生的事件 */
}
events成員描述需要監聽哪些類型的事件,可以用以下幾種標志來描述。
POLLIN:文件中有數據可讀,下面實例中使用到了這個標志
POLLPRI::文件中有緊急數據可讀
POLLOUT:可以向文件寫入數據
POLLERR:文件中出現錯誤,只限于輸出
POLLHUP:與文件的連接被斷開,只限于輸出
POLLNVAL:文件描述符是不合法的,即它并沒有指向一個成功打開的文件
numfds:需要監聽的文件個數,即第一個參數所指向的數組中的元素數目
timeout:表示poll阻塞的超時時間(毫秒)。如果該值小于等于0,則表示無限等待
函數返回值 成功:返回大于0的值,表示事件發生的pollfd結構的個數
0:超時; 1:出錯

    3.使用實例

    當使用select()函數時,存在一系列的問題,例如,內核必須檢查多余的文件描述符,每次調用select()之后必須重置被監聽的文件描述符集,而且可監聽的文件個數受限制(使用FD_SETSIZE宏來表示fd_set結構能夠容納的文件描述符的大數目)等。實際上,poll機制與select機制相比效率更高,使用范圍更廣。下面以poll()函數為例實現某種功能。

    本實例中主要實現通過調用poll()函數來監聽三個終端的輸入(分別重定向到兩個管道文件的虛擬終端及主程序所運行的虛擬終端)并分別進行相應的處理。在這里我們建立了一個poll()函數監視的讀文件描述符集,其中包含三個文件描述符,分別為標準輸入文件描述符和兩個管道文件描述符。通過監視主程序的虛擬終端標準輸入來實現程序的控制(如程序結束);以兩個管道作為數據輸入,主程序將從兩個管道讀取的輸入字符串寫入到標準輸出文件(屏幕)。

    為了充分表現poll()函數的功能,在運行主程序時,需要打開3個虛擬終端:首先用mknod命令創建兩個管道in1和in2。接下來,在兩個虛擬終端上分別運行cat>in1和cat>in2。同時在第三個虛擬終端上運行主程序。

    在程序運行后,如果在兩個管道終端上輸入字符串,則可以觀察到同樣的內容將在主程序的虛擬終端上逐行顯示。

    如果想結束主程序,只要在主程序的虛擬終端下輸入以“q”或“Q”字符開頭的字符串即可。如果三個文件一直在無輸入狀態中,則主程序一直處于阻塞狀態。為了防止無限期的阻塞,在程序中設置超時值(本實例中設置為60s),當無輸入狀態持續到超時值時,主程序主動結束運行并退出。該程序的流程圖如圖2.3所示。


圖2.3 多路復用實例流程圖

    /* multiplex_poll.c */
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <errno.h>
    #include <poll.h>
    #define MAX_BUFFER_SIZE 1024 /* 緩沖區大小 */
    #define IN_FILES 3 /* 多路復用輸入文件數目 */
    #define TIME_DELAY 60000 /* 超時時間秒數:60s */
    #define MAX(a, b) ((a > b)?(a):(b))

    int main(void)
    {
        struct pollfd fds[IN_FILES];
        char buf[MAX_BUFFER_SIZE];
        int i, res, real_read, maxfd;

        /* 首先按一定的權限打開兩個源文件 */
        fds[0].fd = 0;
        if((fds[1].fd = open ("in1", O_RDONLY|O_NONBLOCK)) < 0)
        {
            printf("Open in1 error\n");
            return 1;
        }
        if((fds[2].fd = open ("in2", O_RDONLY|O_NONBLOCK)) < 0)
        {
            printf("Open in2 error\n");
            return 1;
        }
        /* 取出兩個文件描述符中的較大者 */
        for (i = 0; i < IN_FILES; i++)
        {
            fds[i].events = POLLIN;
        }

        /* 循環測試是否存在正在監聽的文件描述符 */
        while(fds[0].events || fds[1].events || fds[2].events)
        {
            if (poll(fds, IN_FILES, 0) < 0)
            {
                printf("Poll error or Time out\n");
                return 1;
            }
            for (i = 0; i< IN_FILES; i++)
            {
                if (fds[i].revents) /* 判斷在哪個文件上發生了事件 */
                {
                    memset(buf, 0, MAX_BUFFER_SIZE);
                    real_read = read(fds[i].fd, buf, MAX_BUFFER_SIZE);
                    if (real_read < 0)
                    {
                        if (errno != EAGAIN)
                        {
                            return 1; /* 系統錯誤,結束運行 */
                        }
                    }
                    else if (!real_read)
                    {
                        close(fds[i].fd);
                        fds[i].events = 0; /* 取消對該文件的監聽 */
                    }
                    else
                    {
                        if (i == 0) /* 如果在標準輸入上有數據輸入時 */
                        {
                            if ((buf[0] == 'q') || (buf[0] == 'Q'))
                            {
                                return 1; /* 輸入“q”或“Q”則會退出 */
                            }
                        }
                        else
                        { /* 將讀取的數據先傳送到終端上 */
                            buf[real_read] = '\0';
                            printf("%s", buf);
                        }
                    } /* end of if real_read*/
                } /* end of if revents */
            } /* end of for */
        } /*end of while */
        exit(0);
    }

    讀者可以將以上程序交叉編譯,并下載到開發板上運行,以下是運行結果:

    $ mknod in1 p
    $ mknod in2 p
    $ cat > in1            /* 在第一個虛擬終端 */
    SELECT CALL
    TEST PROGRAMME
    END
    $ cat > in2            /* 在第二個虛擬終端 */
    select call
    test programme
    end
    $ ./multiplex_select   /* 在第三個虛擬終端 */
    SELECT CALL            /* 管道1的輸入數據 */
    select call            /* 管道2的輸入數據 */
    TEST PROGRAMME         /* 管道1的輸入數據 */
    test programme         /* 管道2的輸入數據 */
    END                    /* 管道1的輸入數據 */
    end                    /* 管道2的輸入數據 */
    q                      /* 在第三個終端上輸入“q”或“Q”則立刻結束程序運行 */

    程序的超時結束結果如下:

    $ ./multiplex_select
    …(在60s之內沒有任何監聽文件的輸入)
    Poll error or Time out

    本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》

   熱點鏈接:

   1、linux 文件鎖的實現及其應用
   2、底層文件I/O操作的系統調用
   3、Linux中的文件及文件描述符
   4、Linux文件系統之虛擬文件系統(VFS)
   5、Linux系統調用及用戶編程接口(API)

更多新聞>> 

主站蜘蛛池模板: 欧美乱妇无码免费视频 | 亚洲多毛女人厕所小便 | 国产色情老熟女NBA控卫之神 | 性一区| 未满十八勿入AV网免费 | 亚洲成A人片在线观看无码 久久久久久无码精品无码国产人妻丝瓜 | 国产乱子伦视频在线播放 | 亚洲AV最新在线观看网址 | 成人A级毛片免费观看AV | 粉嫩极品国产在线观看 | 亚洲欧洲无码一区二区三区 | 人妻人人添人妻人人爱 | 久久久国产成人一区二区 | 在线观看亚洲专区 | 日韩美女乱婬AAA高清视频 | 日本视频免费在线观看 | 成人无遮挡裸免费视频在线观看 | 久久99国产精品久久99软件 | 麻豆国产成人av一区二区三区 | 日本高清一区免费中文视频 | 被夫の上司に犯中文字幕 | AV网址有哪些 | 伊人久久大香线焦av综合影院 | 性中国少妇熟妇XXXX农村 | 中文字幕日韩精品一区 | 少妇88久久中文字幕 | 免费国产在线精品一区 | 最新国产免费AV片在线观看 | 青青草国产成人a∨ | 中文乱码免费一区二区 | 欧洲精品视频在线 | 日韩精品无码中文字幕一区二区 | 国产伦子系列沙发午睡 | 人妻被粗大猛进猛出69国产 | 揉捏奶头高潮呻吟视频 | CHINESE青年大学生GAY18 | 亚洲色偷偷综合亚洲av78 | 韩国三级中文字幕HD | 久久亚洲AV午夜福利精品一区 | 成年男女免费视频网站不卡 | 日产精品99久久久久久 |