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

當前位置:首頁 > 學習資源 > 講師博文 > Linux網絡編程IO復用和模式

Linux網絡編程IO復用和模式 時間:2023-10-12      來源:華清遠見

IO多路復用概念

      IO多路復用是指通過一種機制,使得單個進程可以監控多個文件描述符的可讀、可寫和異常等事件。常見的IO多路復用技術包括:select、epoll等。在實際應用中,IO多路復用可以提高程序的運行效率和性能,減少系統開銷,降低系統資源的使用率。它廣泛應用于網絡編程、服務器開發、操作系統等領域,可以幫助開發人員更好地處理大量的網絡連接和數據請求。以下主要以select和epoll展開敘述。

 

IO多路復用之select

select實現原理

      IO多路復用select函數是一種實現并行IO的機制,可以同時處理多個socket連接,提高系統的性能和效率。在Linux中,select是一個阻塞函數,把需要管理的文件描述符添加到fd_set集合中,由select統一管理。如果所有的文件描述符都沒有數據準備好,那么select會一直阻塞等待,如果有文件描述符的數據準備好,select函數解除阻塞,但是要通過輪循的方式把有響應的文件描述符找出來。處理完后,繼續循環到select的位置監聽。

 

select相關知識

   1、函數原型

    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

    

參數說明

nfds:需要監控的最大文件描述符加1。

readfds:讀文件描述符集合。

writefds:寫文件描述符集合。

exceptfds:異常文件描述符集合。

timeout:select函數的超時時間設置,如果設置為NULL,則一直等待直到事件發生

 

         返回值:

           >0 :  表示準備好的文件描述符個數

          =0: 超時解除阻塞

            -1:  函數出錯

 

 

2、輔助函數 

 void FD_CLR(int fd, fd_set *set);  //將fd從set集合中移除

 int  FD_ISSET(int fd, fd_set *set); //判斷fd是否在set集合中,如果在返回真,否則返回假

 void FD_SET(int fd, fd_set *set); // 將fd加入set集合中

 void FD_ZERO(fd_set *set);  //  清空set集合中的內容

 

select應用案例

   #include <stdio.h>

 #include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <pthread.h>

 

//成功返回監聽套接字, 失敗返回NULL 

int sock_init()

{

    int sockfd;

    int ret;

 

     sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if(sockfd<0)

    {

    perror("socket");

    return -1;

    }

 

    //設置套接字端口復用

    int opt = 1;

     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

 

     struct sockaddr_in  seraddr;

     int addrlen = sizeof(struct sockaddr_in);

    seraddr.sin_family = AF_INET;

    seraddr.sin_port = htons(8001);

     inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);

     ret = bind(sockfd, (struct sockaddr*)&seraddr, addrlen);

    if(ret<0)

    {

    perror("bind");

    return -1;

    }

 

 

    ret = listen(sockfd, 10);

    if(ret<0)

    {

    perror("bind");

    return -1;

    }

    return sockfd;

}

 

int main()

{

    char buff[1024];

    int sockfd;

    int ret;

    int cfd;

 

    sockfd = sock_init();

    if(sockfd < 0)

    {

    return -1;

    }

 

    int maxfd;

    fd_set  set, rset;

    FD_ZERO(&set);  //清空集合

 

     FD_SET(sockfd, &set); //文件描述符加入集合

     maxfd = sockfd;   //設置最大文件描述符

 

    while(1)

    {

     rset = set;//由于select每次都會修改集合中的數據,所有每次都會將set的值拷貝給rset

    printf("select..\n");

     ret = select(maxfd+1, &rset, NULL, NULL, NULL);//調用select監聽rset集合

     printf("select over..\n");

    if(ret<0)

    {

    perror("select");

    break;

    }

 

     if(FD_ISSET(sockfd, &rset))   //有客戶端請求連接

    {

    //1、接受客戶端

printf("accept...\n");

     cfd = accept(sockfd, NULL, NULL);

printf("accept  over...\n");

    if(cfd<0)

    {

    perror("accept");

    continue;

    }

    //2、將cfd加入set集合

    FD_SET(cfd, &set);

 

    //3、判斷最大值

    if(maxfd<cfd)

    {

    maxfd = cfd;

    }

    }

 

    for(int i=0; i<=maxfd; i++)

    {

    if(i == sockfd)

    {

    continue;

    }

    if(!FD_ISSET(i, &rset))

    {

    continue;

    }

 

    printf("read...\n");

    ret = read(i, buff, 1024 );

     printf("read  over...\n");

    if(ret<0)

    {

    perror("read");

    //1、關閉文件描述符

    close(i);

    //2、從set集合中移除

    FD_CLR(i, &set);

    continue;

    }

    else if(0 == ret) 

    {

     printf("tcp  broken...\n");

    //1、關閉文件描述符

    close(i);

    //2、從set集合中移除

    FD_CLR(i, &set);

    continue;

    }

 

    buff[ret] = '\0';

     printf("buff: %s\n", buff);

    }

    }

    

    return 0;

}

 

select優缺點

     Select通過串行模擬并行可以實現服務端的多任務, 但是如果某個任務處理的時間很長,就會影響后面任務的處理。除此之外,select能夠監聽的文件描述符的個數是有限的,默認是1024。 而且select每次解除阻塞僅僅只是說明有套接字數據準備好了,具體是哪個套接字有數據要通過輪循遍歷的方式給找出來。

 

 

IO多路復用之epoll

epoll實現原理

   Epoll是一種高效的I/O多路復用機制,它可以同時監控多個文件描述符,當其中任意一個文件描述符就緒時,就會通知應用程序進行相應的操作。相比于傳統的select和poll機制,epoll具有更高的性能和更好的擴展性。

 Epoll的實現原理主要包括三個部分:epoll_create、epoll_ctl和epoll_wait。應用程序需要調用epoll_create函數創建一個epoll實例,該函數返回一個文件描述符,用于標識該實例。在Linux內核中,會為該實例創建一個紅黑樹和一個雙向鏈表,用于存儲待監控的文件描述符和相關的事件信息。接下來,應用程序可以通過epoll_ctl函數向epoll實例中添加、修改或刪除待監控的文件描述符和事件。該函數的第一個參數是epoll實例的文件描述符,第二個參數是操作類型(EPOLL_CTL_ADD、EPOLL_CTL_MOD或EPOLL_CTL_DEL),第三個參數是待監控的文件描述符,第四個參數是一個epoll_event結構體,用于指定待監控的事件類型和相關的數據。應用程序需要調用epoll_wait函數等待文件描述符就緒。該函數的第一個參數是epoll實例的文件描述符,第二個參數是一個epoll_event數組,用于存儲就緒的文件描述符和相關的事件信息,第三個參數是數組的大小,第四個參數是超時時間(單位為毫秒)。當有文件描述符就緒時,該函數會返回就緒的文件描述符數量,并將相關的事件信息存儲在epoll_event數組中。

Epoll的高效性和擴展性主要體現在以下幾個方面:

 (1)Epoll使用紅黑樹和雙向鏈表存儲待監控的文件描述符和事件信息,可以快速地進行查找和插入操作,而不需要遍歷整個文件描述符集合。

 (2)Epoll支持邊緣觸發和水平觸發兩種模式。邊緣觸發只在文件描述符狀態發生變化時通知應用程序,而水平觸發則會在文件描述符處于就緒狀態時不斷通知應用程序。邊緣觸發可以減少不必要的通知,提高效率。

 (3)Epoll支持EPOLLONESHOT事件,可以確保每個文件描述符只被一個線程處理,避免了多個線程同時處理同一個文件描述符的情況。

 (4)Epoll支持EPOLLEXCLUSIVE事件,可以確保每個文件描述符只被一個進程處理,避免了多個進程同時處理同一個文件描述符的情況。

 Epoll是一種高效的I/O多路復用機制,可以大大提高應用程序的性能和可擴展性。在實際應用中,應該根據具體的需求選擇合適的觸發模式和事件類型,以達到最佳的性能和可靠性。

 

epoll相關知識

    1、創建集合空間

        int epoll_create(int size);

參數: 

size  : 指定文件描述符的個數

 

返回值:

成功:返回與集合關聯的文件描述符

失敗:  -1

 

 

 

2、管理集合空間中的文件描述符

     int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

 

    參數說明:

epfd:和集合關聯的文件描述符

op :  命令參數

       EPOLL_CTL_ADD:  往集合中添加文件描述符

       EPOLL_CTL_MOD: 修改集合中文件描述符的信息

       EPOLL_CTL_DEL:  刪除集合中指定的文件描述符

fd:  操作的文件描述符

event:  如果是刪除操作,該參數忽略,直接傳NULL

              如果是添加操作, 通過該參數告訴內核監聽指定文件描述符的指定時間

 

    typedef union epoll_data {

                void        *ptr;

                int          fd;

                uint32_t     u32;

                uint64_t     u64;

            } epoll_data_t;

 

      struct epoll_event {

                uint32_t     events;      /* Epoll events */

                epoll_data_t data;        /* User data variable */

           };

          

            events:        EPOLLIN(讀事件)

 

3、監聽集合中的文件描述符

  int epoll_wait(int epfd, struct epoll_event *events,

                       int maxevents, int timeout);

參數說明:

epfd:和集合關聯的文件描述符

events:存放準備好文件描述符信息數組的起始地址

maxevents:  數組的最大元素個數

timeout  :設置超時時間   (以毫秒為單位的)

           >0  : 指定超時時間

           =0  : 非阻塞函數

           -1  : 永久阻塞

 

 

返回值:

>0 : 準備好的文件描述符個數

=0 :超時時間到了

-1 :出錯

 

 

epoll應用案例

 #include <stdio.h>

 #include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <pthread.h>

#include <stdlib.h>

#include <assert.h>

#include <sys/epoll.h>

 

//成功返回監聽套接字, 失敗返回NULL 

int sock_init()

{

    int sockfd;

    int ret;

 

     sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if(sockfd<0)

    {

    perror("socket");

    return -1;

    }

 

    //設置套接字端口復用

    int opt = 1;

     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

 

     struct sockaddr_in  seraddr;

     int addrlen = sizeof(struct sockaddr_in);

    seraddr.sin_family = AF_INET;

    seraddr.sin_port = htons(8001);

     inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);

     ret = bind(sockfd, (struct sockaddr*)&seraddr, addrlen);

    if(ret<0)

    {

    perror("bind");

    return -1;

    }

 

 

    ret = listen(sockfd, 10);

    if(ret<0)

    {

    perror("bind");

    return -1;

    }

    return sockfd;

}

 

int main()

{

    char buff[1024];

    int sockfd;

    int ret, count;

    int cfd, efd;

 

    efd = epoll_create(100);

    if(efd<0)

    {

     perror("epoll_create");

    return -1;

    }

 

    sockfd = sock_init();

    if(sockfd < 0)

    {

    return -1;

    }

    

     struct  epoll_event  ev;

     struct  epoll_event  evs[10];

    ev.events = EPOLLIN;

    ev.data.fd = sockfd;

     epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &ev);

 

    while(1)

    {

    printf("wait...\n");

     count = epoll_wait(efd, evs, 10, -1);

     printf("wait over...\n");

    if(count < 0)

    {

    perror("epoll_wait");

    break;

    }

 

    for(int i=0; i<count; i++)

    {

     int  temp =  evs[i].data.fd;

    if(temp == sockfd)// 有客戶端請求連接

    {

    //1、接收客戶端

    printf("accept...\n");

     cfd = accept(sockfd, NULL, NULL);

     printf("accept  over...\n");

    if(cfd<0)

    {

    perror("accept");

    continue;

    }

    //2、cfd 加入efd關聯的集合中

                 ev.data.fd = cfd;

     epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &ev);

    }

     else   //已經連接過來的客戶端發來數據

    {

    printf("read..\n");

    ret = read(temp, buff, 1024);

     printf("read  over..\n");

    if(ret<0)

    {

    perror("read");

    //1、關閉文件描述符

    close(temp);

    //2、從集合中移除

         epoll_ctl(efd, EPOLL_CTL_DEL, temp, NULL);

    }

    else if(ret == 0)

    {

     printf("tcp  broken...\n");

    //1、關閉文件描述符

    close(temp);

    //2、從集合中移除

         epoll_ctl(efd, EPOLL_CTL_DEL, temp, NULL);

    }

    buff[ret] = '\0';

     printf("buff: %s\n", buff);

    }

    }

    }    

    return 0;

}

 

 

epoll優缺點

   缺點:通過串行模擬并行, 如果處理一個請求的時間過長,會影響后面任務的處理。這是所有多路IO轉接的通病。

優點:(1)監聽文件描述符的個數由用戶指定

         (2)如果有套接字的數據準備好, 內核直接告訴你哪個套接字的數據準備就緒,不需要采用輪循的方式去查找

 

 

 

 

上一篇:嵌入式開發初學者必看:bootloader移植步驟

下一篇:Linux系統

戳我查看嵌入式每月就業風云榜

點我了解華清遠見高校學霸學習秘籍

猜你關心企業是如何評價華清學員的

干貨分享
相關新聞
前臺專線:010-82525158 企業培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2024 北京華清遠見科技發展有限公司 版權所有 ,京ICP備16055225號-5京公海網安備11010802025203號

回到頂部