IT培训机构|91免费精品视频|专注编程培训|91免费精品|软件开发培训_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)

更多新聞>> 

主站蜘蛛池模板: 无线对讲机系统-中继台-山区隧道信号覆盖-贝亚特 | 潍坊沃林机械设备有限公司-牵引式风送果园打药机,悬挂式风送果园喷雾机,自走式果树喷药机,车载式风送远程喷雾机-潍坊沃林机械设备有限公司-牵引式风送果园打药机,悬挂式风送果园喷雾机,自走式果树喷药机,车载式风送远程喷雾机 潍坊网络推广,临沂360推广,东营360推广,枣庄360推广,潍坊网站建设,潍坊网络公司,潍坊360搜索,潍坊APP开发,潍坊360推广,潍坊360代理,潍坊点睛网络科技有限公司 | 上海塑料链条生产厂家-塑料网带批发-柔性齿形链价格-上海瀚幽传动机械有限公司 | 潍坊网络推广,临沂360推广,东营360推广,枣庄360推广,潍坊网站建设,潍坊网络公司,潍坊360搜索,潍坊APP开发,潍坊360推广,潍坊360代理,潍坊点睛网络科技有限公司 | 清洁度检测_手动颗粒萃取设备_自动颗粒萃取设备 - 厦门迈纳光学技术有限公司 | 全球环保研究网_生态环境绿色产业咨询部_生态文明建设行业智库 ? | 猪粪烘干机|小型鸡粪烘干机|猪粪烘干机价格|小型鸡粪烘干机价格 - 河南宏科重工干燥机设备生产厂家 | 山东货架厂家,重型货架,阁楼货架,钢平台,板材货架-山东智造仓储设备有限公司 | 山东日兴新材料股份有限公司 | 碳纤维棒_碳纤维管_碳纤维缠绕管_嘉兴恒隆复合材料有限公司 | 实木全屋定制|整木定制|整木家装|实木护墙板-浩冠家具官网 | 直流屏|青岛直流屏|直流屏电池-世界500强艾默生直流屏标准生产厂家赛里斯能源科技有限公司 | 京建鹏达_商用无烟烧烤设备多少钱|开店商用自助旋转烧烤炉价格|无烟电烧烤炉批发厂家|无烟烧烤桌定做厂商-京建鹏达烧烤设备网 | 万博瑞升(天津)科技有限公司-管道应力|管道振动|脉动|CAE,CFD 弯箍机_钢筋弯箍机_全自动钢筋弯箍机_数控弯箍机-建科智能装备制造(天津)股份有限公司 | 珠海市甜菊科技发展有限公司| 兰舍硅藻泥 -- 深圳硅藻泥|深圳兰舍硅藻泥|深圳硅藻泥品牌|深圳硅藻泥价格|深圳硅藻泥厂家|深圳硅藻泥施工| | 太阳能杀虫灯-农业用杀虫灯-果园灭虫灯厂家「尚科杀虫灯| | 合金锤头_破碎机锤头_耐磨锤头_巩义市东辰铸造 高耐磨合金锤头厂家 | 金属剪切机,金属打包机,废钢剪切机,废铁压块机,金属压块机,废纸打包机,重废龙门剪,废钢龙门剪,箱式剪,液压剪切机-瑞顿机械装备制造江阴有限公司 | 微库仑硫氯分析仪-化学发光定氮仪-X荧光硫测定仪-泰州江河仪器有限公司 | 数据交易导航,数据资产入表,数据交易平台,数据要素市场 | 青岛大倾角输送带厂家_橡胶挡边输送带_波纹状挡边输送带_大倾角输送带型号-青岛朗森橡胶有限公司 | 人工草坪_施工围挡_体育场围网-人工草坪厂家菲斯福 | 叶片泵,液压电机,油泵电机,变量叶片泵,油压电机-广东俊泰液压科技有限公司 | 立式/卧式/暖气/管道泵,管道离心泵选型,管道增压泵型号 - 安平鼎千泵业制造厂 | 助乐邦轮椅,北京轮椅置换,电动轮椅置换,轮椅回收北京,轮椅送货上门服务 | 实木中药柜,实木中药斗,木制中药柜,木制中药柜的价格,实木中草药柜,安国美佳中药柜厂家 | 河南郑州纯净水设备_厂家_价格_河南江宇环保科技有限公司水处理设备工厂 | 柚墨yomoer官网_PPT模板_工作总结PPT模板下载_个人简历模板 | 湖南实验台-防静电工作台-实验设备厂家-长沙实验室设备有限公司-湖南贝塔实验室设备有限公司 | 性激素6项实验检测,放射免疫实验检测,明胶酶谱试剂盒,科研用人血清,质控标品,试剂盒-南京信帆生物技术有限公司 | 交通标志牌-交通标牌-铝圆牌-铝三角片-铝滑槽-公路警示指示牌-方牌-高速道路反光牌毛坯-交通设施安全警示标识牌-路名指示限速限高牌-厂家加工交通标牌铝板半成品毛坯-上海吕盟铝业有限公司 | 泰安铭德机械有限公司,有机肥设备,山东有机肥设备厂家,铭德机械 泰安华特玻璃钢有限公司|泰安玻璃钢|泰安华特玻璃钢 | 免费的动态思维导图软件,脑图大师,思维导图制作软件 - 万彩脑图大师官网 | 自恢复保险丝_贴片保险丝_插件保险丝_力特保险丝_力特经销商_SMD贴片保险丝_一次性保险丝厂家—深圳市集电通实业有限公司 | 室内模拟高尔夫,射箭馆-北京鹰搏蓝天科技有限公司 | 智能化解决方案 智能家居 家庭影院 灯光音箱 会议室报告厅 剧场剧院 指挥中心 大数据中心_沈阳天哲科技有限公司 | 清河县隆鑫密封件有限公司,汽车用密封条,配电箱柜、集装箱密封胶条,建筑门窗、家具用密封条,无毒环保医用密封条,船舶、农机用密封条,异型产品 | 郑州空气能热水器,郑州空气能热水器经销商,郑州空气能热水器维修,郑州空气能热水器安装 | 罗湖人才网_罗湖招聘网_求职找工作平台| 企业网站模板_公司模板网站_网站整站源码 - 亿搜云全网营销平台 企业头条—优府网企业自媒体 |