1.epoll 是I/o多路複用的一種解決方案,對比select的優勢有:面試
a.支持打開最大的文件描述符(可高達百萬)數據庫
b.效率並不隨着描述符的增多而線性降低。select每次是輪詢,因此描述符越多效率越低。epoll的好處是利用事件觸發,內核經過回調函數幫他(這是親兒子)。服務器
c.採用了mmap內存映射,減小內核區到用戶區數據拷貝,又節省了很多時間。多線程
2.Epoll又分爲水平觸發和邊緣觸發。函數
Level_triggered(水平觸發):當被監控的文件描述符上有可讀寫事件發生時,epoll_wait()會通知處理程序去讀寫。若是此次沒有把數據一次性所有讀寫完(如讀寫緩衝區過小),那麼下次調用 epoll_wait()時,它還會通知你在上沒讀寫完的文件描述符上繼續讀寫,固然若是你一直不去讀寫,它會一直通知你!!!若是系統中有大量你不須要讀寫的就緒文件描述符,而它們每次都會返回,這樣會大大下降處理程序檢索本身關心的就緒文件描述符的效率!!!spa
Edge_triggered(邊緣觸發):當被監控的文件描述符上有可讀寫事件發生時,epoll_wait()會通知處理程序去讀寫。若是此次沒有把數據所有讀寫完(如讀寫緩衝區過小),那麼下次調用epoll_wait()時,它不會通知你,也就是它只會通知你一次,直到該文件描述符上出現第二次可讀寫事件纔會通知你!!!這種模式比水平觸發效率高,系統不會充斥大量你不關心的就緒文件描述符!!!線程
優缺點就很明顯了,如過訪問量過大,切每一個事件的內容又不少的話,推薦邊緣觸發,由於每一個事件的讀取時要花長時間的,用邊緣觸發能明顯減小事件的觸發次數。code
3.瓶頸。網上看的,不過卻是和咱們的遊戲服務器的限制想吻合。blog
單線程epoll,觸發量可達到15000,可是加上業務後,由於大多數業務都與數據庫打交道,因此就會存在阻塞的狀況,這個時候就必須用多線程來提速。遊戲
代碼實現:從服務端分析,何時該監聽讀,何時該監聽寫?(面試被問到過。。。)。從服務器的角度,客戶端發送的請求都是須要讀取的,因此是讀事件,而服務器的返回數據是寫事件。正常狀況下應該是先讀後寫,有請求才返回嘛。寫完以後這個描述符應該再切換回讀事件,監聽下次的請求。並且讀是個被動事件,寫是服務器的主動事件。因此網上大部分代碼的實現都是默認監聽讀,服務器返回數據時將描述符置爲寫,寫完後在還原讀。
if(events[i].data.fd==listenfd) { connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); if(connfd<0){ perror("connfd<0"); exit(1); } setnonblocking(connfd); char *str = inet_ntoa(clientaddr.sin_addr); //std::cout<<"connec_ from >>"< //設置用於讀操做的文件描述符 ev.data.fd=connfd; //設置用於注測的讀操做事件 ev.events=EPOLLIN|EPOLLET; //註冊ev epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); } else if(events[i].events&EPOLLIN) { //printf("reading!/n"); if ( (sockfd = events[i].data.fd) < 0) continue; new_task=new task(); new_task->fd=sockfd; new_task->next=NULL; //添加新的讀任務 pthread_mutex_lock(&mutex); if(readhead==NULL) { readhead=new_task; readtail=new_task; } else { readtail->next=new_task; readtail=new_task; } //喚醒全部等待cond1條件的線程 pthread_cond_broadcast(&cond1); pthread_mutex_unlock(&mutex); } else if(events[i].events&EPOLLOUT) {
rdata=(struct user_data *)events[i].data.ptr;
sockfd = rdata->fd;
write(sockfd, rdata->line, rdata->n_size);
delete rdata;
//設置用於讀操做的文件描述符
ev.data.fd=sockfd;
//設置用於注測的讀操做事件
ev.events=EPOLLIN|EPOLLET;
//修改sockfd上要處理的事件爲EPOLIN
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); }