真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

C/C++ I/O多路復(fù)用(IO multiplexing)--select與epoll

I/O多路復(fù)用(IO multiplexing)

? I/O多路復(fù)用是通過(guò)一種機(jī)制,可以監(jiān)視多個(gè)文件描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫(xiě)就緒,還有異常就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫(xiě)操作。比較常用的有select/epoll,有些地方也稱(chēng)這種IO方式為事件驅(qū)動(dòng) IO(event driven IO)。

10年的文水網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)營(yíng)銷(xiāo)推廣的優(yōu)勢(shì)是能夠根據(jù)用戶(hù)設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整文水建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。成都創(chuàng)新互聯(lián)公司從事“文水網(wǎng)站設(shè)計(jì)”,“文水網(wǎng)站推廣”以來(lái),每個(gè)客戶(hù)項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

select

  • 原理:客戶(hù)端操作服務(wù)器時(shí)就會(huì)產(chǎn)生這三種文件描述符(簡(jiǎn)稱(chēng)fd):writefds(寫(xiě))、readfds(讀)、和exceptfds(異常)。select會(huì)阻塞住監(jiān)視3類(lèi)文件描述符,等有數(shù)據(jù)、可讀、可寫(xiě)、出異常 或超時(shí)、就會(huì)返回;返回后通過(guò)遍歷fdset整個(gè)數(shù)組來(lái)找到就緒的描述符fd,然后進(jìn)行對(duì)應(yīng)的IO操作。

  • 優(yōu)點(diǎn):幾乎在所有的平臺(tái)上支持,跨平臺(tái)支持性好

  • 缺點(diǎn):

  1. 由于是采用輪詢(xún)方式全盤(pán)掃描,會(huì)隨著文件描述符FD數(shù)量增多而性能下降。
  2. 每次調(diào)用 select(),需要把 fd 集合從用戶(hù)態(tài)拷貝到內(nèi)核態(tài),并進(jìn)行遍歷(消息傳遞都是從內(nèi)核到用戶(hù)空間)
  3. 默認(rèn)單個(gè)進(jìn)程打開(kāi)的FD有限制是1024個(gè),可修改宏定義,但是效率仍然慢。

select 接口的原型:

FD_ZERO(int fd, fd_set* fds)//將指定的文件描述符集清空,在對(duì)文件描述符集合進(jìn)行設(shè)置前,必須對(duì)其進(jìn)行初始化,如果不清空,由于在系統(tǒng)分配內(nèi)存空間后,通常并不作清空處理,所以結(jié)果是不可知的。即清空set中所有的位,全置為0
FD_SET(int fd, fd_set* fds)//用于在文件描述符集合中增加一個(gè)新的文件描述符。即設(shè)置set中相關(guān)fd的位,將其置1
FD_ISSET(int fd, fd_set* fds)//用于判斷指定的文件描述符是否在該集合中。即判斷set中相關(guān)fd是否存在,該位是否被置1
FD_CLR(int fd, fd_set* fds)//用于在文件描述符集合中刪除一個(gè)文件描述符。即清除set中相關(guān)fd的位,將其置為0
int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set*exceptfds,struct timeval *timeout)

  1. maxfd:是需要監(jiān)視的最大的文件描述符值+1
  2. readfds:需要檢測(cè)的可讀文件描述符的集合
  3. writefds:需要檢測(cè)的可寫(xiě)文件描述符的集合
  4. exceptfds:需要檢測(cè)的異常文件描述符的集合
  5. timeout:指向timeval結(jié)構(gòu)體的指針,通過(guò)傳入的這個(gè)timeout參數(shù)來(lái)決定select()函數(shù)的三種執(zhí)行方式。

  • 傳入的timeout為NULL,則表示將select()函數(shù)置為阻塞狀態(tài),直到我們所監(jiān)視的文件描述符集合中某個(gè)文件描述符發(fā)生變化是,才會(huì)返回結(jié)果。
  • 傳入的timeout為0秒0毫秒,則表示將select()函數(shù)置為非阻塞狀態(tài),不管文件描述符是否發(fā)生變化均立刻返回繼續(xù)執(zhí)行。
  • 傳入的timeout為一個(gè)大于0的值,則表示這個(gè)值為select()函數(shù)的超時(shí)時(shí)間,在timeout時(shí)間內(nèi)一直阻塞,超過(guò)時(shí)間即返回結(jié)果 。

簡(jiǎn)單使用

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAXLNE  4096

int main(int argc, char **argv)
{
    int listenfd, connfd, n;
    struct sockaddr_in servaddr;
    char buff[MAXLNE];

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    
    fd_set rfds, rset, wfds, wset;

	FD_ZERO(&rfds);
	FD_SET(listenfd, &rfds);

	FD_ZERO(&wfds);

	int max_fd = listenfd;

	while (1) {

		rset = rfds;
		wset = wfds;

		int nready = select(max_fd+1, &rset, &wset, NULL, NULL);

		if (FD_ISSET(listenfd, &rset)) { //

			struct sockaddr_in client;
		    socklen_t len = sizeof(client);
		    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
		        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
		        return 0;
		    }

			FD_SET(connfd, &rfds);

			if (connfd > max_fd) max_fd = connfd;

			if (--nready == 0) continue;

		}

		int i = 0;
		for (i = listenfd+1;i <= max_fd;i ++) {

			if (FD_ISSET(i, &rset)) { //

				n = recv(i, buff, MAXLNE, 0);
		        if (n > 0) {
		            buff[n] = '\0';
		            printf("recv msg from client: %s\n", buff);

					FD_SET(i, &wfds);

					//send(i, buff, n, 0);
		        } else if (n == 0) { //

					FD_CLR(i, &rfds);
					//printf("disconnect\n");
		            close(i);

		        }
				if (--nready == 0) break;
			} else if (FD_ISSET(i, &wset)) {

				send(i, buff, n, 0);
				FD_SET(i, &rfds);

			}
		}
	}
    
    close(listenfd);
    return 0;
}

epoll

原理:epoll使用一個(gè)文件描述符管理多個(gè)描述符,將用戶(hù)關(guān)系的文件描述符的事件存放到內(nèi)核的一個(gè)事件表中,這樣在用戶(hù)空間和內(nèi)核空間的copy只需一次。

epoll之所以高性能是得益于它的三個(gè)函數(shù):

  1. epoll_create()系統(tǒng)啟動(dòng)時(shí),在Linux內(nèi)核里面申請(qǐng)一個(gè)B+樹(shù)結(jié)構(gòu)文件系統(tǒng),返回epoll對(duì)象,也是一個(gè)fd
  2. epoll_ctl() 每新建一個(gè)連接,都通過(guò)該函數(shù)操作epoll對(duì)象,在這個(gè)對(duì)象里面修改添加刪除對(duì)應(yīng)的鏈接fd, 綁定一個(gè)callback函數(shù)
  3. epoll_wait() 輪訓(xùn)所有的callback集合,并完成對(duì)應(yīng)的IO操作

優(yōu)點(diǎn):

  • 沒(méi)fd這個(gè)限制,所支持的FD上限是操作系統(tǒng)的最大文件句柄數(shù),1G內(nèi)存大概支持10萬(wàn)個(gè)句柄
  • 效率提高,使用回調(diào)通知而不是輪詢(xún)的方式,不會(huì)隨著FD數(shù)目的增加效率下降
  • 內(nèi)核和用戶(hù)空間mmap同一塊內(nèi)存實(shí)現(xiàn)(mmap是一種內(nèi)存映射文件的方法,即將一個(gè)文件或者其它對(duì)象映射到進(jìn)程的地址空間)
      

epoll接口

 epoll操作過(guò)程需要三個(gè)接口,分別如下:

#include 
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

(1) int epoll_create(int size);

 函數(shù)是一個(gè)系統(tǒng)函數(shù),函數(shù)將在內(nèi)核空間內(nèi)開(kāi)辟一塊新的空間,可以理解為epoll結(jié)構(gòu)空間,返回值為epoll的文件描述符編號(hào),方便后續(xù)操作使用。參數(shù)size從Linux 2.6.8以后就不再使用了,但是必須為它設(shè)置一個(gè)大于0的值。若epoll_create函數(shù)調(diào)用成功,則返回一個(gè)非負(fù)值的epollfd,否則返回-1。

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

? poll的事件注冊(cè)函數(shù),epoll與select不同,select函數(shù)是調(diào)用時(shí)指定需要監(jiān)聽(tīng)的描述符和事件,epoll先將用戶(hù)感興趣的描述符事件注冊(cè)到epoll空間內(nèi),此函數(shù)是非阻塞函數(shù),作用僅僅是增刪改epoll空間內(nèi)的描述符信息。第一個(gè)參數(shù)是epoll_create()的返回值,第二個(gè)參數(shù)表示動(dòng)作,用三個(gè)宏來(lái)表示:
EPOLL_CTL_ADD:注冊(cè)新的fd到epfd中;
EPOLL_CTL_MOD:修改已經(jīng)注冊(cè)的fd的監(jiān)聽(tīng)事件;
EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;
第三個(gè)參數(shù)是需要監(jiān)聽(tīng)的fd(一般指socket_fd),第四個(gè)參數(shù)是告訴內(nèi)核需要監(jiān)聽(tīng)什么事,struct epoll_event結(jié)構(gòu)如下:

struct epoll_event {
  __uint32_t events;  /* Epoll events */
  epoll_data_t data;  /* User data variable */
};

typedef union epoll_data {
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;

events可以是以下幾個(gè)宏的集合:
EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉);
EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫(xiě);
EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái));
EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤;
EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的。
EPOLLONESHOT:只監(jiān)聽(tīng)一次事件,當(dāng)監(jiān)聽(tīng)完這次事件之后,如果還需要繼續(xù)監(jiān)聽(tīng)這個(gè)socket的話(huà),需要再次把這個(gè)socket加入到EPOLL隊(duì)列里

(3) int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

  等待事件的產(chǎn)生,類(lèi)似于select()調(diào)用。參數(shù)epfd是epoll的文件描述符,events用來(lái)從內(nèi)核得到事件的集合,maxevents告之內(nèi)核這個(gè)events有多大,這個(gè)maxevents的值不能大于創(chuàng)建epoll_create()時(shí)的size,參數(shù)timeout是超時(shí)時(shí)間(毫秒,0非阻塞會(huì)立即返回,-1將不確定,也有說(shuō)法說(shuō)是永久阻塞)。該函數(shù)返回需要處理的事件數(shù)目,如返回0表示已超時(shí),小于0表示出錯(cuò)

工作模式

  epoll對(duì)文件描述符的操作有兩種模式:LT(level trigger)和ET(edge trigger)。LT模式是默認(rèn)模式,LT模式與ET模式的區(qū)別如下:

  LT(水平觸發(fā))模式:當(dāng)epoll_wait檢測(cè)到描述符事件發(fā)生并將此事件通知應(yīng)用程序,應(yīng)用程序可以不立即處理該事件。下次調(diào)用epoll_wait時(shí),會(huì)再次響應(yīng)應(yīng)用程序并通知此事件。簡(jiǎn)單點(diǎn)說(shuō),就是一個(gè)事件只要有,就會(huì)一直觸發(fā)。

  ET(邊緣觸發(fā))模式:當(dāng)epoll_wait檢測(cè)到描述符事件發(fā)生并將此事件通知應(yīng)用程序,應(yīng)用程序必須立即處理該事件。如果不處理,下次調(diào)用epoll_wait時(shí),不會(huì)再次響應(yīng)應(yīng)用程序并通知此事件。簡(jiǎn)單點(diǎn)說(shuō),就是在一個(gè)事件從無(wú)到有時(shí),才會(huì)觸發(fā)。

  ET模式在很大程度上減少了epoll事件被重復(fù)觸發(fā)的次數(shù),因此效率要比LT模式高。epoll工作在ET模式的時(shí)候,必須使用非阻塞套接口,以避免由于一個(gè)文件句柄的阻塞讀/阻塞寫(xiě)操作把處理多個(gè)文件描述符的任務(wù)餓死。

編碼流程

  1. 創(chuàng)建epoll描述符
  2. 注冊(cè)epoll事件
  3. 等待epoll事件
  4. 判斷觸發(fā)epoll事件的描述符和事件
  5. 關(guān)閉epoll描述符

簡(jiǎn)單使用

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define MAXLNE  4096
#define POLL_SIZE	1024

int main(int argc, char **argv)
{
    int listenfd, connfd, n;
    struct sockaddr_in servaddr;
    char buff[MAXLNE];

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9999);

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    
    int epfd = epoll_create(1); //int size

    struct epoll_event events[POLL_SIZE] = {0};
    struct epoll_event ev;

    ev.events = EPOLLIN;
    ev.data.fd = listenfd;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    while (1) {

        int nready = epoll_wait(epfd, events, POLL_SIZE, 5);
        if (nready == -1) {
            continue;
        }

        int i = 0;
        for (i = 0;i < nready;i ++) {

            int clientfd =  events[i].data.fd;
            if (clientfd == listenfd) {

                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
                    printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                    return 0;
                }

                printf("accept\n");
                ev.events = EPOLLIN;
                ev.data.fd = connfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);

            } else if (events[i].events & EPOLLIN) {

                n = recv(clientfd, buff, MAXLNE, 0);
                if (n > 0) {
                    buff[n] = '\0';
                    printf("recv msg from client: %s\n", buff);

                    send(clientfd, buff, n, 0);
                } else if (n == 0) { //


                    ev.events = EPOLLIN;
                    ev.data.fd = clientfd;

                    epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);

                    close(clientfd);

                }

            }

        }

    }
    
    close(listenfd);
    return 0;
}

分享題目:C/C++ I/O多路復(fù)用(IO multiplexing)--select與epoll
當(dāng)前路徑:http://weahome.cn/article/dsoihpp.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部