Epoll多路I/O複用技術
Epoll多路I/O複用技術
通常學習一個新的linux技術,我們應該看看man手冊對其定義。
NAME
epoll - I/O event notification facility
SYNOPSIS
#include <sys/epoll.h>
DESCRIPTION
The epoll API performs a similar task to poll(2): monitoring multiple file descriptors to see if I/O is possible on any of them.
The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of
watched file descriptors.
那麼從man手冊這段文字我們可以看出,epoll它是由linux另一套的併發處理方案poll演變過來的,它與poll相類似:能夠監控多個檔案描述符的I/O變化。在Linux中,一切皆檔案(有部分不是)所以,任何一個連線,也有一個檔案描述符(一般為int型別)來存放
重點:epoll比poll的優點:支援水平觸發(level-triggered)和邊沿觸發(edge-triggered)兩種方案
Edge Triggered (ET) 邊緣觸發只有資料到來才觸發,不管快取區中是否還有資料。
Level Triggered (LT) 水平觸發只要有資料都會觸發。
epoll所需的API函式:
epoll_create(2) creates an epoll instance and returns a file descriptor referring to that instance. (The more recent epoll_create1(2) extends the functionality of epoll_create(2).)
Interest in particular file descriptors is then registered via epoll_ctl(2). The set of file descriptors currently registered on an epoll instance is sometimes called an epoll set.
epoll_wait(2) waits for I/O events, blocking the calling thread if no events are currently available.
1、建立epoll檔案描述符
建立一個epoll控制程式碼,引數size用來告訴核心監聽的檔案描述符的個數,跟記憶體大小有關。需要注意的是,當建立好epoll控制程式碼後,它就是會佔用一個fd值,在linux下如果檢視/proc/程式id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須呼叫close()關閉,否則可能導致fd被耗盡。
int epoll_create(int size); //size:監聽數目
2、管理epoll中的檔案描述符集合,增加、修改、刪除
epoll的事件註冊函式,它不同於select()是在監聽事件時告訴核心要監聽什麼型別的事件,而是在這裡先註冊要監聽的事件型別。
第一個引數是epoll_create()的返回值。
第二個參數列示動作,用三個巨集來表示:
EPOLL_CTL_ADD:註冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
第三個引數是需要監聽的fd。
第四個引數是告訴核心需要監聽什麼事
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
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 */
} __EPOLL_PACKED;
3、收集在epoll監控的事件中已經傳送的事件(預設阻塞等待)
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
/*
events:用來從核心得到事件的集合,
maxevents:告之核心這個events有多大,這個maxevents的值不能大於建立epoll_create()時的size,
timeout:是超時時間
-1:阻塞
0:立即返回,非阻塞
>0:指定微秒
返回值:成功返回有多少檔案描述符就緒,時間到時返回0,出錯返回-1
*/
epoll工作原理
1、epoll同樣只告知那些就緒的檔案描述符,而且當我們呼叫epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個陣列中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體對映(mmap)技術,這樣便徹底省掉了這些檔案描述符在系統呼叫時複製的開銷。
2、另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,程式只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程式呼叫epoll_wait()時便得到通知。
epoll 服務端例子
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main()
{
int sock_server = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(10099);
addr.sin_addr.s_addr = 0;
int ret = bind(sock_server,(struct sockaddr*)&addr,sizeof(addr));
listen(sock_server,5);
int epollfd = epoll_create(2);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sock_server;
//吧sock_server加入eopll集合中
epoll_ctl(epollfd,EPOLL_CTL_ADD,sock_server,&ev);
while(1)
{
struct epoll_event outev[8];
int ret = epoll_wait(epollfd,outev,8,1000);
if (ret < 0)
{
if (errno == EINTR)//若被訊號打斷,則重新迴圈
continue;
break;
}
if (ret > 0)//有被喚醒的檔案描述符
{
for(int i = 0 ; i<ret; i++)
{
int fd = outev[i].data.fd;
if (fd == sock_server)
{
//若為socket的檔案描述符,則用accept進行三次握手,建立連線
int newfd = accept(fd,NULL,NULL);
//EPOLLIN :表示對應的檔案描述符可以讀(包括對端SOCKET正常關閉)
ev.events = EPOLLIN;
ev.data.fd = newfd;
//把新的fd加到epollfd中,繼續等待下一次喚醒
epoll_ctl(epollfd,EPOLL_CTL_ADD,newfd,&ev);
}
else
{
//若不是socketfd,則為已經建立的連線,可以直接讀取資料
char buf[1024];
int readlen = read(fd,buf,sizeof(buf));
if (readlen<=0)
{
//read<=0,證明已經沒有資料,或者出錯,關閉fd
close(fd);
}
else
{
printf("read data is :%s\n",buf);
}
}
}
}
}
return 0;
}
測試epoll的客戶端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main()
{
int fd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(10099);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int ret = connect(fd , (struct sockaddr*)&addr, sizeof(addr));
write(fd,"hello server",sizeof("hello server"));
char buf[1024];
read(fd,buf,sizeof(buf));
printf("server:%s\n",buf);
close(fd);
return 0;
}
測試結果我就暫時不貼圖了,希望這次部落格能給大家帶來一點收穫!
相關文章
- Epoll程式設計-I/O多路複用程式設計
- I/O多路複用技術(multiplexing)
- Netty權威指南:I/O 多路複用技術Netty
- 一文搞懂I/O多路複用及其技術
- 【Linux網路程式設計】I/O 多路複用技術Linux程式設計
- 從網路I/O模型到Netty,先深入瞭解下I/O多路複用模型Netty
- LinuxI/O多路複用Linux
- 詳解Go語言I/O多路複用netpoller模型Go模型
- 伺服器程式設計——I/O複用(select、poll、epoll)伺服器程式設計
- IO多路複用技術總結
- IO多路複用與epoll機制淺析
- 細談 Linux 中的多路複用epollLinux
- 流?I/O 操作?阻塞?epoll?
- 【面試】I/O 複用面試
- I/O Mutiplexing poll 和 epoll
- 一篇文章幫你徹底搞清楚“I/O多路複用”和“非同步I/O”的前世今生非同步
- 一篇文章讀懂Reactor和Proactor兩種I/O多路複用模式React模式
- 網路程式設計學習——Linux epoll多路複用模型程式設計Linux模型
- Go netpoll I/O 多路複用構建原生網路模型之原始碼深度解析Go模型原始碼
- IO多路複用——深入淺出理解select、poll、epoll的實現
- 多路複用
- 網路程式設計-I/O複用程式設計
- Linux下的5種I/O模型與3組I/O複用Linux模型
- python網路程式設計——IO多路複用之epollPython程式設計
- Oracle 多路複用Oracle
- 【技術分享】Oracle控制檔案的備份、恢復以及多路複用Oracle
- 理解IO多路複用
- Linux下套接字詳解(十)---epoll模式下的IO多路複用伺服器Linux模式伺服器
- IO多路複用詳解
- Redis 和 IO 多路複用Redis
- IO多路複用原理剖析
- 計算機網路——多路複用與多路分解計算機網路
- IO多路複用原理&場景
- BIO、NIO、多路複用IO、AIOAI
- [Java併發]IO多路複用Java
- 玩轉 PHP 網路程式設計全套之 I/O 複用PHP程式設計
- 淺談 non-blocking I/O Multiplexing + poll/epoll 的正確使用BloC
- IO多路複用機制詳解