久久婷婷香蕉热狠狠综合,精品无码国产自产拍在线观看蜜,寡妇房东在做爰3,中文字幕日本人妻久久久免费,国产成人精品三上悠亚久久


Linux下多路復用I/O接口

分享到:
           

    多路復用

    1.函數說明

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

    總的(de)來說,I/O處理的(de)模(mo)型有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()函數,就是屬于這種模型。
    ● 信(xin)號(hao)驅動(dong)(dong)I/O模型(xing):在(zai)這種模型(xing)下(xia),進程要定義一個信(xin)號(hao)處理程序,系統可(ke)以自動(dong)(dong)捕(bu)獲特定信(xin)號(hao)的到來,從而(er)啟(qi)(qi)動(dong)(dong)I/O。這是由內核通(tong)知用戶何時可(ke)以啟(qi)(qi)動(dong)(dong)一個I/O操(cao)作決定的。

    它是(shi)(shi)非阻塞(sai)的。當有就緒的數(shu)據(ju)(ju)時(shi),內核就向該進程(cheng)發送SIGIO信號。 無論我們如何(he)處理SIGIO信號,這種模型的好處是(shi)(shi)當等待數(shu)據(ju)(ju)到(dao)(dao)達時(shi),可(ke)以(yi)不(bu)阻塞(sai)。主程(cheng)序繼(ji)續(xu)執行,只(zhi)有收到(dao)(dao)SIGIO信號時(shi)才(cai)去處理數(shu)據(ju)(ju)即(ji)可(ke)。

    ● 異步(bu)I/O模型(xing):在(zai)這種模型(xing)下,進程(cheng)先讓(rang)內(nei)核(he)啟動I/O操(cao)(cao)作(zuo),并在(zai)整個(ge)操(cao)(cao)作(zuo)完成后通(tong)知該進程(cheng)。這種模型(xing)與信(xin)號(hao)驅動模型(xing)的主要區別在(zai)于:信(xin)號(hao)驅動I/O是由內(nei)核(he)通(tong)知我(wo)們何(he)時可以啟動一個(ge)I/O操(cao)(cao)作(zuo),而異步(bu)I/O模型(xing)是由內(nei)核(he)通(tong)知進程(cheng)I/O操(cao)(cao)作(zuo)何(he)時完成的。現在(zai),并不是所有(you)的系統(tong)都支持(chi)這種模型(xing)。

    可(ke)以看到(dao)(dao),select()和(he)poll()的(de)I/O多路(lu)轉接模型是處理(li)I/O復用的(de)一(yi)個(ge)(ge)高效(xiao)的(de)方法。它(ta)可(ke)以具體設置程序中每一(yi)個(ge)(ge)所關心的(de)文件(jian)(jian)描述(shu)符的(de)條件(jian)(jian)、希望等(deng)待(dai)的(de)時(shi)間等(deng),從select()和(he)poll()函(han)數返回時(shi),內(nei)核會(hui)通(tong)知用戶(hu)已(yi)準備好的(de)文件(jian)(jian)描述(shu)符的(de)數量、已(yi)準備好的(de)條件(jian)(jian)(或事件(jian)(jian))等(deng)。通(tong)過(guo)使用select()和(he)poll()函(han)數的(de)返回結(jie)果(可(ke)能是檢測(ce)到(dao)(dao)某(mou)個(ge)(ge)文件(jian)(jian)描述(shu)符的(de)注冊事件(jian)(jian)或是超時(shi),或是調用出錯),就可(ke)以調用相應(ying)的(de)I/O處理(li)函(han)數了(le)。

    2.函(han)數格(ge)式

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

表2.8 select()函(han)數語(yu)法要點(dian)

所需頭文件 #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:出錯

   &nbsp;可(ke)以看到(dao),select()函數根據希望進行的(de)文件(jian)(jian)操作對文件(jian)(jian)描述符進行了分類處理,這里對文件(jian)(jian)描述符的(de)處理主要涉(she)及4個宏函數,如表(biao)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()后測試文件描述符集中的哪個文件描述符是否有變化

    一般(ban)來(lai)(lai)說(shuo),在(zai)每(mei)次(ci)使(shi)用(yong)(yong)select()函(han)數之前,首先使(shi)用(yong)(yong)FD_ZERO()和(he)FD_SET()來(lai)(lai)初始化(hua)文件描(miao)(miao)述(shu)(shu)符(fu)(fu)(fu)集(ji)(ji)(在(zai)需要重(zhong)復調用(yong)(yong)select()函(han)數時(shi),先把一次(ci)初始化(hua)好的文件描(miao)(miao)述(shu)(shu)符(fu)(fu)(fu)集(ji)(ji)備份(fen)下來(lai)(lai),每(mei)次(ci)讀取它即可)。在(zai)select()函(han)數返(fan)回后(hou),可循環使(shi)用(yong)(yong)FD_ISSET()來(lai)(lai)測試描(miao)(miao)述(shu)(shu)符(fu)(fu)(fu)集(ji)(ji),在(zai)執(zhi)行完對(dui)相關文件描(miao)(miao)述(shu)(shu)符(fu)(fu)(fu)的操作后(hou),使(shi)用(yong)(yong)FD_CLR()來(lai)(lai)清(qing)除描(miao)(miao)述(shu)(shu)符(fu)(fu)(fu)集(ji)(ji)。

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

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

    可以看到,這個時間結構體的精確度可以設置到微秒級,這對于大多數的應用而言已經足夠了。
 ;   poll()函數(shu)的語(yu)法要點如表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.使(shi)用實例(li)

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

    本實例中(zhong)主(zhu)要(yao)實現通過(guo)調用poll()函數來監(jian)聽三(san)個終端的(de)(de)(de)輸(shu)入(分別重定向到(dao)兩(liang)個管(guan)道文(wen)(wen)(wen)件的(de)(de)(de)虛(xu)擬終端及(ji)主(zhu)程(cheng)序(xu)所運行的(de)(de)(de)虛(xu)擬終端)并分別進(jin)行相應的(de)(de)(de)處理(li)。在這里我(wo)們建立了一個poll()函數監(jian)視(shi)的(de)(de)(de)讀(du)文(wen)(wen)(wen)件描(miao)(miao)述(shu)符(fu)(fu)(fu)集,其(qi)中(zhong)包含三(san)個文(wen)(wen)(wen)件描(miao)(miao)述(shu)符(fu)(fu)(fu),分別為標準輸(shu)入文(wen)(wen)(wen)件描(miao)(miao)述(shu)符(fu)(fu)(fu)和兩(liang)個管(guan)道文(wen)(wen)(wen)件描(miao)(miao)述(shu)符(fu)(fu)(fu)。通過(guo)監(jian)視(shi)主(zhu)程(cheng)序(xu)的(de)(de)(de)虛(xu)擬終端標準輸(shu)入來實現程(cheng)序(xu)的(de)(de)(de)控制(如程(cheng)序(xu)結束);以兩(liang)個管(guan)道作為數據輸(shu)入,主(zhu)程(cheng)序(xu)將(jiang)從兩(liang)個管(guan)道讀(du)取的(de)(de)(de)輸(shu)入字符(fu)(fu)(fu)串寫入到(dao)標準輸(shu)出文(wen)(wen)(wen)件(屏幕)。

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

    在(zai)程序運行后,如果(guo)在(zai)兩個管(guan)道終(zhong)端(duan)上輸入字(zi)符串,則可(ke)以觀察(cha)到同樣的(de)內容將在(zai)主(zhu)程序的(de)虛擬終(zhong)端(duan)上逐行顯示。

    如(ru)(ru)果(guo)(guo)想結(jie)束主(zhu)程(cheng)(cheng)(cheng)序(xu),只要在(zai)主(zhu)程(cheng)(cheng)(cheng)序(xu)的(de)虛擬終端下(xia)輸入以“q”或“Q”字(zi)符開頭的(de)字(zi)符串即可(ke)。如(ru)(ru)果(guo)(guo)三個文件(jian)一(yi)直在(zai)無輸入狀(zhuang)態(tai)中,則主(zhu)程(cheng)(cheng)(cheng)序(xu)一(yi)直處于阻(zu)塞狀(zhuang)態(tai)。為(wei)了防(fang)止無限期的(de)阻(zu)塞,在(zai)程(cheng)(cheng)(cheng)序(xu)中設(she)置超(chao)時(shi)值(本(ben)實(shi)例中設(she)置為(wei)60s),當無輸入狀(zhuang)態(tai)持續到超(chao)時(shi)值時(shi),主(zhu)程(cheng)(cheng)(cheng)序(xu)主(zhu)動(dong)結(jie)束運行并退出(chu)。該程(cheng)(cheng)(cheng)序(xu)的(de)流程(cheng)(cheng)(cheng)圖如(ru)(ru)圖2.3所示(shi)。


圖(tu)2.3 多路復用實例流程圖(tu)

    /* 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);
    }

    ;讀者(zhe)可以將以上(shang)程序交叉編譯,并下(xia)載(zai)到開發(fa)板上(shang)運(yun)行,以下(xia)是運(yun)行結果:

    $ 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”則立(li)刻結束程序運行(xing) */ 

    程序的超時(shi)結束結果如(ru)下:

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

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

   熱(re)點鏈接:

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

更多新聞>>