国产成人精品三级麻豆,色综合天天综合高清网,亚洲精品夜夜夜,国产成人综合在线女婷五月99播放,色婷婷色综合激情国产日韩


Linux下多路復用I/O接口

分享到:
           

    多路復用

    1.函數(shù)說明

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

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

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

    它是非阻塞的。當有就緒的數(shù)據(jù)時,內(nèi)核就向該進程發(fā)送SIGIO信號。 無論我們?nèi)绾翁幚鞸IGIO信號,這種模型的好處是當?shù)却龜?shù)據(jù)到達時,可以不阻塞。主程序繼續(xù)執(zhí)行,只有收到SIGIO信號時才去處理數(shù)據(jù)即可。

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

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

    2.函數(shù)格式

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

表2.8 select()函數(shù)語法要點

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

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

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

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集中的一個元素,則返回非零值,可以用于調(diào)用select()后測試文件描述符集中的哪個文件描述符是否有變化

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

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

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

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

表2.10 poll()函數(shù)語法要點

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

    3.使用實例

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

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

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

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

    如果想結(jié)束主程序,只要在主程序的虛擬終端下輸入以“q”或“Q”字符開頭的字符串即可。如果三個文件一直在無輸入狀態(tài)中,則主程序一直處于阻塞狀態(tài)。為了防止無限期的阻塞,在程序中設(shè)置超時值(本實例中設(shè)置為60s),當無輸入狀態(tài)持續(xù)到超時值時,主程序主動結(jié)束運行并退出。該程序的流程圖如圖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 /* 緩沖區(qū)大小 */
    #define IN_FILES 3 /* 多路復用輸入文件數(shù)目 */
    #define TIME_DELAY 60000 /* 超時時間秒數(shù):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;

        /* 首先按一定的權(quán)限打開兩個源文件 */
        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;
        }

        /* 循環(huán)測試是否存在正在監(jiān)聽的文件描述符 */
        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) /* 判斷在哪個文件上發(fā)生了事件 */
                {
                    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; /* 系統(tǒng)錯誤,結(jié)束運行 */
                        }
                    }
                    else if (!real_read)
                    {
                        close(fds[i].fd);
                        fds[i].events = 0; /* 取消對該文件的監(jiān)聽 */
                    }
                    else
                    {
                        if (i == 0) /* 如果在標準輸入上有數(shù)據(jù)輸入時 */
                        {
                            if ((buf[0] == 'q') || (buf[0] == 'Q'))
                            {
                                return 1; /* 輸入“q”或“Q”則會退出 */
                            }
                        }
                        else
                        { /* 將讀取的數(shù)據(jù)先傳送到終端上 */
                            buf[real_read] = '\0';
                            printf("%s", buf);
                        }
                    } /* end of if real_read*/
                } /* end of if revents */
            } /* end of for */
        } /*end of while */
        exit(0);
    }

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

    $ 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的輸入數(shù)據(jù) */
    select call            /* 管道2的輸入數(shù)據(jù) */
    TEST PROGRAMME         /* 管道1的輸入數(shù)據(jù) */
    test programme         /* 管道2的輸入數(shù)據(jù) */
    END                    /* 管道1的輸入數(shù)據(jù) */
    end                    /* 管道2的輸入數(shù)據(jù) */
    q                      /* 在第三個終端上輸入“q”或“Q”則立刻結(jié)束程序運行 */

    程序的超時結(jié)束結(jié)果如下:

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

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

   熱點鏈接:

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

更多新聞>>