常見IO模型和epoll O(1)實現原理
同步和阻塞
同步:傳送方傳送請求後,需要收到響應後才能接著傳送下一步請求;
阻塞;通常針對網路套接字socket,呼叫結果返回前,當前執行緒一直掛起等待;
同步針對呼叫方,阻塞針對接受方;
同步非阻塞:傳送方發出請求後一直等待,接受方處理請求時立即返回,不用等待執行結果;
非同步阻塞:傳送方發出請求後馬上返回,接受方處理請求期間一直等待,直到返回執行結果;
nginx工作程式採用了非同步非阻塞,即執行請求時,工作程式和客戶端都無需等待響應;
那麼問題來了,當IO請求完成時,如何通知工作程式?
有兩種方式:
1 worker定期檢查IO執行狀態
2 IO在完成後主動通知worker,即採用事件驅動,允許一個程式同時處理多個請求;
事件驅動
包含事件收集器,事件傳送器和事件處理器,重點是事件處理器,通常有3種模式;
基於程式的,基於執行緒的,基於非阻塞IO的(每接受一個請求,將其放入待處理事件的列表,使用非阻塞IO方式呼叫事件處理器來處理請求,形成事件驅動處理庫)
基於非阻塞IO常見的有select()和epoll(),然而兩者皆有不足,尤其在高併發連線時。
select():描述符總數有限制(1024個);讀/寫/異常各對應一個描述符;O(n)
poll():讀/寫/異常集合到一個描述符中;描述符總數沒有限制;O(n)
而epoll()很大程度上解決了上述問題。
epoll優勢
為了避免每次掃描所有fd,epoll引入兩個資料結構,
紅黑樹--儲存socket fd
ready list--當socket上有事件發生時,將其加入此列表
epoll每次只掃描ready list,直接跳過那些不符合條件socketfd,演算法複雜度為O(1);
epoll在初始化時呼叫kmem_cache_create申請核心記憶體(採用slab機制),用於建立epitem和eppoll_entry;
epoll對外只提供epoll_create/ctl/wait 3個API,原型如下:
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);
epoll_create(): 在核心cache裡建立上述紅黑樹和ready list;
epoll_ctl(): 如果增加socket控制程式碼,則檢查在紅黑樹中是否存在,存在立即返回,不存在則新增到樹幹上;然後向核心註冊回撥函式,告訴核心,如果這個控制程式碼的中斷到了,就把它放到準備就緒list連結串列裡;
epoll_wait(): 與select()等效,即返回ready list中的資料,將fd從核心態複製到使用者態;
客戶端只需呼叫epoll_ctl(),負責把socket fd加入epoll監控;
sockfd-1
|
|
sockfd-2 e
| p -----------> wait_queue(process)
| -----------> o
sockfd-3 o
| -----------> l
| f -----------> ready_queue(ready events)
..... d
sockfd-N-2
|
|
sockfd-N-1
|
|
sockfd-N
虛擬碼
while true {
active_stream[] = epoll_wait(epollfd)
for i in active_stream[] {
read or write till
}
}
再詳細一點
for( ; ; )
{
nfds = epoll_wait(epfd,events,20,500);
for(i=0;i
{
if(events[i].data.fd==listenfd) //有新的連線
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept這個連線
ev.data.fd=connfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //將新的fd新增到epoll的監聽佇列中
}
else if( events[i].events&EPOLLIN ) //接收到資料,讀socket
{
n = read(sockfd, line, MAXLINE)) < 0 //讀
ev.data.ptr = md; //md為自定義型別,新增資料
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改識別符號,等待下一個迴圈時傳送資料,非同步處理的精髓
}
else if(events[i].events&EPOLLOUT) //有資料待傳送,寫socket
{
struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取資料
sockfd = md->fd;
send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //傳送資料
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改識別符號,等待下一個迴圈時接收資料
}
else
{
//其他的處理
}
}
}
epoll_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 */
};
注:events有多種型別,包括EPOLLIN/EPOLLOUT/EPOLLERR/EPOLLET
參考資料
http://blog.csdn.net/russell_tao/article/details/7160071
同步:傳送方傳送請求後,需要收到響應後才能接著傳送下一步請求;
阻塞;通常針對網路套接字socket,呼叫結果返回前,當前執行緒一直掛起等待;
同步針對呼叫方,阻塞針對接受方;
同步非阻塞:傳送方發出請求後一直等待,接受方處理請求時立即返回,不用等待執行結果;
非同步阻塞:傳送方發出請求後馬上返回,接受方處理請求期間一直等待,直到返回執行結果;
nginx工作程式採用了非同步非阻塞,即執行請求時,工作程式和客戶端都無需等待響應;
那麼問題來了,當IO請求完成時,如何通知工作程式?
有兩種方式:
1 worker定期檢查IO執行狀態
2 IO在完成後主動通知worker,即採用事件驅動,允許一個程式同時處理多個請求;
事件驅動
包含事件收集器,事件傳送器和事件處理器,重點是事件處理器,通常有3種模式;
基於程式的,基於執行緒的,基於非阻塞IO的(每接受一個請求,將其放入待處理事件的列表,使用非阻塞IO方式呼叫事件處理器來處理請求,形成事件驅動處理庫)
基於非阻塞IO常見的有select()和epoll(),然而兩者皆有不足,尤其在高併發連線時。
select():描述符總數有限制(1024個);讀/寫/異常各對應一個描述符;O(n)
poll():讀/寫/異常集合到一個描述符中;描述符總數沒有限制;O(n)
而epoll()很大程度上解決了上述問題。
epoll優勢
為了避免每次掃描所有fd,epoll引入兩個資料結構,
紅黑樹--儲存socket fd
ready list--當socket上有事件發生時,將其加入此列表
epoll每次只掃描ready list,直接跳過那些不符合條件socketfd,演算法複雜度為O(1);
epoll在初始化時呼叫kmem_cache_create申請核心記憶體(採用slab機制),用於建立epitem和eppoll_entry;
epoll對外只提供epoll_create/ctl/wait 3個API,原型如下:
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);
epoll_create(): 在核心cache裡建立上述紅黑樹和ready list;
epoll_ctl(): 如果增加socket控制程式碼,則檢查在紅黑樹中是否存在,存在立即返回,不存在則新增到樹幹上;然後向核心註冊回撥函式,告訴核心,如果這個控制程式碼的中斷到了,就把它放到準備就緒list連結串列裡;
epoll_wait(): 與select()等效,即返回ready list中的資料,將fd從核心態複製到使用者態;
客戶端只需呼叫epoll_ctl(),負責把socket fd加入epoll監控;
sockfd-1
|
|
sockfd-2 e
| p -----------> wait_queue(process)
| -----------> o
sockfd-3 o
| -----------> l
| f -----------> ready_queue(ready events)
..... d
sockfd-N-2
|
|
sockfd-N-1
|
|
sockfd-N
虛擬碼
while true {
active_stream[] = epoll_wait(epollfd)
for i in active_stream[] {
read or write till
}
}
再詳細一點
for( ; ; )
{
nfds = epoll_wait(epfd,events,20,500);
for(i=0;i
{
if(events[i].data.fd==listenfd) //有新的連線
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept這個連線
ev.data.fd=connfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //將新的fd新增到epoll的監聽佇列中
}
else if( events[i].events&EPOLLIN ) //接收到資料,讀socket
{
n = read(sockfd, line, MAXLINE)) < 0 //讀
ev.data.ptr = md; //md為自定義型別,新增資料
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改識別符號,等待下一個迴圈時傳送資料,非同步處理的精髓
}
else if(events[i].events&EPOLLOUT) //有資料待傳送,寫socket
{
struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取資料
sockfd = md->fd;
send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //傳送資料
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改識別符號,等待下一個迴圈時接收資料
}
else
{
//其他的處理
}
}
}
epoll_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 */
};
注:events有多種型別,包括EPOLLIN/EPOLLOUT/EPOLLERR/EPOLLET
參考資料
http://blog.csdn.net/russell_tao/article/details/7160071
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15480802/viewspace-1340824/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 高階IO模型之kqueue和epoll模型
- 常見排序原理及 python 實現排序Python
- 今天我們來聊Java IO模型,BIO、NIO、AIO三種常見IO模型Java模型AI
- I/O Mutiplexing poll 和 epoll
- 利用AutoGpt將任何模型支援o1模型的推理實現GPT模型
- IO多路複用——深入淺出理解select、poll、epoll的實現
- 常見排序演算法原理及JS程式碼實現排序演算法JS
- 流?I/O 操作?阻塞?epoll?
- 堆的原理以及實現O(lgn)
- IO模式 select、poll、epoll模式
- epoll程式設計,單epoll+執行緒池?執行緒池+epoll?nginx實現高併發的原理?程式設計執行緒Nginx
- epoll核心實現分析
- 常見的安全模型、攻擊模型和隱私需求模型
- 12 個常見 CNN 模型論文集錦與 PyTorch 實現CNN模型PyTorch
- [作業系統]阻塞io 非阻塞io Epoll作業系統
- Laravel 模型事件實現原理Laravel模型事件
- Epoll模型詳解模型
- Epoll 模型簡介模型
- epoll使用與原理
- 【Java面試】Java常見IO面試題!Java面試題
- epoll實現快速ping
- nginx實現常見場景Nginx
- Java I/O模型及其底層原理Java模型
- Java網路程式設計和NIO詳解6:Linux epoll實現原理詳解Java程式設計Linux
- Netty原始碼(三):I/O模型和JavaNIO底層原理Netty原始碼模型Java
- Epoll多路I/O複用技術
- 談談對不同I/O模型的理解 (阻塞/非阻塞IO,同步/非同步IO)模型非同步
- Java IO1:IO和FileJava
- 常見的 emit 實現 AOP demoMIT
- 模型融合——stacking原理與實現模型
- Epoll程式設計-I/O多路複用程式設計
- Java NIO1:I/O模型概述Java模型
- 逆向工程 O1模型架構模型架構
- epoll 非阻塞IO 邊沿觸發模式模式
- 常見的索引模型淺析索引模型
- 夯實Java基礎系列16:一文讀懂Java IO流和常見面試題Java面試題
- 常見快取演算法和LRU的c++實現快取演算法C++
- HIDS 常見檢測原理