91在线一级黄片|91视频在线观看18|成人夜间呦呦网站|91资源欧美日韩超碰|久久最新免费精品视频一区二区三区|国产探花视频在线观看|黄片真人免费三级片毛片|国产人无码视频在线|精品成人影视无码三区|久久视频爱久久免费精品

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
深度探究linuxio多路復(fù)用的實(shí)驗(yàn)過程與原理(linuxio多路復(fù)用實(shí)驗(yàn))

深度探究 Linux IO 多路復(fù)用的實(shí)驗(yàn)過程與原理

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

Linux 是一種自由、開放源代碼的操作系統(tǒng),能夠自由獲取、查看和修改源代碼。在 Linux,IO 多路復(fù)用是一個(gè)十分重要的概念,可以大大提高服務(wù)器的效率和性能。本文將深度探究 Linux IO 多路復(fù)用的實(shí)驗(yàn)過程和原理。

一、什么是 IO 多路復(fù)用

IO 多路復(fù)用是指在一個(gè)進(jìn)程中,同時(shí)監(jiān)視多個(gè)文件描述符,對(duì)于文件描述符中的任意一種 IO 準(zhǔn)備就緒,都能夠進(jìn)行相應(yīng)的操作。這意味著一個(gè)進(jìn)程可以同時(shí)處理多個(gè) I/O 請(qǐng)求,不僅避免了不必要的 I/O 輪詢,也可以將多個(gè) I/O 請(qǐng)求交給操作系統(tǒng)同時(shí)處理,達(dá)到更大的并發(fā)能力,提高了系統(tǒng)性能。在 Linux 中,常見的 IO 多路復(fù)用函數(shù)有 select,poll 和 epoll。

二、實(shí)驗(yàn)環(huán)境

為了深入了解 Linux IO 多路復(fù)用,我們需要先準(zhǔn)備好實(shí)驗(yàn)環(huán)境,本文使用的實(shí)驗(yàn)環(huán)境如下:

1. 操作系統(tǒng):Ubuntu 16.04.6 LTS

2. 編譯器:gcc 5.4.0

3. 多路復(fù)用函數(shù):epoll

三、實(shí)驗(yàn)過程

1. 了解 epoll 函數(shù)

在開始實(shí)驗(yàn)之前,我們需要先了解 epoll 函數(shù)的使用方法和參數(shù)。epoll 函數(shù)是 Linux 中一個(gè)高效的 IO 多路復(fù)用機(jī)制,可以監(jiān)控多個(gè)文件描述符上的 I/O 事件并且能夠?qū)@些 I/O 事件進(jìn)行快速的響應(yīng)。epoll 函數(shù)包括三個(gè)主要的 API:

(1)epoll_create() 函數(shù):創(chuàng)建 epoll 的文件描述符。

(2)epoll_ctl() 函數(shù):添加、修改、刪除需要監(jiān)控的文件描述符。

(3)epoll_wt() 函數(shù):等待事件的發(fā)生。

2. 創(chuàng)建 epoll 實(shí)例

在我們開始使用 epoll 函數(shù)進(jìn)行 IO 多路復(fù)用之前,需要先使用 epoll_create() 函數(shù)創(chuàng)建一個(gè) epoll 實(shí)例。代碼如下:

“`c

#include

int epoll_create(int size);

“`

其中 size 參數(shù)是 epoll 實(shí)例的大小,它很重要,因?yàn)樗绊懥?epoll 實(shí)例所使用的內(nèi)存大小。創(chuàng)建成功后,該函數(shù)的調(diào)用將返回一個(gè)大于0的整數(shù)表示 epoll 實(shí)例的文件描述符,否則返回-1。

3. 將文件描述符添加到 epoll 實(shí)例中

創(chuàng)建好了 epoll 實(shí)例之后,我們需要使用 epoll_ctl() 將需要監(jiān)控的文件描述符添加到 epoll 實(shí)例中。代碼如下:

“`c

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

“`

其中:

(1)epfd:指定需要操作的 epoll 實(shí)例的文件描述符。

(2)op:用于指定需要執(zhí)行的操作。此參數(shù)可以有以下三種值:

  - EPOLL_CTL_ADD:將 fd 加入監(jiān)聽的事件中。

  - EPOLL_CTL_DEL:將 fd 從監(jiān)聽的事件中刪除。

  - EPOLL_CTL_MOD:修改 fd 上的監(jiān)聽事件。

(3)fd:需要添加或修改監(jiān)聽事件的文件描述符。

(4)event:需要注冊(cè)的事件,它是一個(gè)結(jié)構(gòu)體類型,可以通過以下代碼進(jìn)行定義:

“`c

struct epoll_event {

__uint32_t events; // epoll 事件類型

epoll_data_t data; // 用戶數(shù)據(jù)

};

typedef union epoll_data {

void *ptr;

int fd;

__uint32_t u32;

__uint64_t u64;

} epoll_data_t;

“`

在實(shí)際使用中,最常用的是 EPOLLIN 和 EPOLLOUT 兩種事件。EPOLLIN 事件表明文件描述符可以對(duì)應(yīng)的文件被讀,EPOLLOUT 事件表示文件描述符可以對(duì)應(yīng)的文件可以被寫。在監(jiān)聽事件觸發(fā)時(shí),用戶可以通過 epoll_wt() 獲得相應(yīng)的文件描述符。

4. 等待事件發(fā)生

我們使用 epoll_wt() 等待事件的發(fā)生,代碼如下:

“`c

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

“`

其中:

(1)epfd:需要等待的 epoll 實(shí)例的文件描述符。

(2)events:存放滿足監(jiān)聽事件的數(shù)組,一般將該數(shù)組定義為 epoll_event 類型,其大小由參數(shù) maxevents 傳遞。

(3)maxevents:events 數(shù)組的大小,即最多可以同時(shí)等到多少個(gè)文件描述符。

(4)timeout:等待的超時(shí)時(shí)間,單位為毫秒。

epoll_wt() 函數(shù)返回滿足事件的文件描述符的數(shù)量。當(dāng)函數(shù)返回 0 時(shí),表示超時(shí);當(dāng)函數(shù)返回 -1 時(shí),表示 epoll_wt() 函數(shù)發(fā)生了錯(cuò)誤,這時(shí)可以通過 errno 來獲得錯(cuò)誤碼。

四、實(shí)驗(yàn)結(jié)果

以下是基于 epoll 的多路復(fù)用程序的實(shí)現(xiàn),并加入了測(cè)試代碼,此代碼的功能是從標(biāo)準(zhǔn)輸入中讀取輸入并將其寫到標(biāo)準(zhǔn)輸出中。通過多路復(fù)用機(jī)制,可以同時(shí)處理來自標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出的 I/O 請(qǐng)求,達(dá)到更大的并發(fā)能力和性能。

“`c

#include

#include

#include

#include

#include

#define MAX_EVENTS 10

#define MAX_BUF_SIZE 1024

int mn(int argc, char *argv[]) {

struct epoll_event ev, events[MAX_EVENTS];

int listen_fd, epoll_fd, n;

char buf[MAX_BUF_SIZE];

char *message = “Hello, I/O multiplexing!”;

int str_len;

// 創(chuàng)建 epoll 實(shí)例

epoll_fd = epoll_create(MAX_EVENTS);

// 添加標(biāo)準(zhǔn)輸入到 epoll 實(shí)例中

ev.events = EPOLLIN;

ev.data.fd = STDIN_FILENO;

epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);

// 添加標(biāo)準(zhǔn)輸出到 epoll 實(shí)例中

ev.events = EPOLLOUT;

ev.data.fd = STDOUT_FILENO;

epoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev);

// 等待事件的發(fā)生

while (1) {

n = epoll_wt(epoll_fd, events, MAX_EVENTS, 1000);

if (n

continue;

}

for (int i = 0; i

if (events[i].data.fd == STDIN_FILENO && events[i].events & EPOLLIN) {

// 從標(biāo)準(zhǔn)輸入中讀取輸入

fgets(buf, MAX_BUF_SIZE, stdin);

// 打印讀取到的輸入

printf(“Read from stdin: %s”, buf);

} else if (events[i].data.fd == STDOUT_FILENO && events[i].events & EPOLLOUT) {

// 將字符串輸出到標(biāo)準(zhǔn)輸出

write(STDOUT_FILENO, message, strlen(message));

}

}

}

return 0;

}

“`

運(yùn)行程序后,用戶輸入的內(nèi)容將被輸出。通過多路復(fù)用機(jī)制,可以同時(shí)處理來自標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出的 I/O 請(qǐng)求,達(dá)到更大的并發(fā)能力和性能。

五、結(jié)論

相關(guān)問題拓展閱讀:

  • 面試必問的epoll技術(shù),從內(nèi)核源碼出發(fā)徹底搞懂epoll

面試必問的epoll技術(shù),從內(nèi)核源碼出發(fā)徹底搞懂epoll

epoll是linux中IO多路復(fù)用的一種機(jī)制,I/O多路復(fù)用就是通過一種機(jī)制,一個(gè)進(jìn)程可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。當(dāng)然linux中IO多路復(fù)用不僅僅是epoll,其他多路復(fù)用機(jī)制還有select、poll,但是接下來介紹epoll的內(nèi)核實(shí)現(xiàn)。

events可以是以下幾個(gè)宏的:

epoll相比select/poll的優(yōu)勢(shì)

epoll相關(guān)的內(nèi)核代碼在fs/eventpoll.c文件中,下面分別分析epoll_create、epoll_ctl和epoll_wait三個(gè)函數(shù)在內(nèi)核中的實(shí)現(xiàn),分析所用linux內(nèi)核源碼為4.1.2版本。

epoll_create用于創(chuàng)建一個(gè)epoll的句柄,其在內(nèi)核的系統(tǒng)實(shí)現(xiàn)如下:

sys_epoll_create:

可見,我們?cè)谡{(diào)用epoll_create時(shí),傳入的size參數(shù),僅僅是用來判斷是否小于等于0,之后再也沒有其他用處。

整個(gè)函數(shù)就3行代碼,真正的工作還是放在sys_epoll_create1函數(shù)中。

sys_epoll_create -> sys_epoll_create1:

sys_epoll_create1 函數(shù)流程如下:

sys_epoll_create -> sys_epoll_create1 -> ep_alloc:

sys_epoll_create -> sys_epoll_create1 -> ep_alloc -> get_unused_fd_flags:

linux內(nèi)核中,current是個(gè)宏,返回的是一個(gè)task_struct結(jié)構(gòu)(我們稱之為進(jìn)程描述符)的變量,表示的是當(dāng)前進(jìn)程,進(jìn)程打開的文件資源保存在進(jìn)程描述符的files成員里面,所以current->files返回的當(dāng)前進(jìn)程打開的文件資源。rlimit(RLIMIT_NOFILE) 函數(shù)獲取的是當(dāng)前進(jìn)程可以打開的更大文件描述符數(shù),這個(gè)值可以設(shè)置,默認(rèn)是1024。

相關(guān)視頻推薦:

支撐億級(jí)io的底層基石 epoll實(shí)戰(zhàn)揭秘

網(wǎng)絡(luò)原理tcp/udp,網(wǎng)絡(luò)編程epoll/reactor,面試中正經(jīng)“八股文”

學(xué)習(xí)視頻教程-騰訊課堂

需要更多C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加群

獲?。ㄙY料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費(fèi)分享

__alloc_fd的工作是為進(jìn)程在

sys_epoll_create -> sys_epoll_create1 -> ep_alloc -> get_unused_fd_flags -> __alloc_fd:

然后,epoll_create1會(huì)調(diào)用anon_inode_getfile,創(chuàng)建一個(gè)file結(jié)構(gòu),如下:

sys_epoll_create -> sys_epoll_create1 -> anon_inode_getfile:

anon_inode_getfile函數(shù)中首先會(huì)alloc一個(gè)file結(jié)構(gòu)和一個(gè)dentry結(jié)構(gòu),然后將該file結(jié)構(gòu)與一個(gè)匿名inode節(jié)點(diǎn)anon_inode_inode掛鉤在一起,這里要注意的是,在調(diào)用anon_inode_getfile函數(shù)申請(qǐng)file結(jié)構(gòu)時(shí),傳入了前面申請(qǐng)的eventpoll結(jié)構(gòu)的ep變量,申請(qǐng)的file->private_data會(huì)指向這個(gè)ep變量,同時(shí),在anon_inode_getfile函數(shù)返回來后,ep->file會(huì)指向該函數(shù)申請(qǐng)的file結(jié)構(gòu)變量。

簡(jiǎn)要說一下file/dentry/inode,當(dāng)進(jìn)程打開一個(gè)文件時(shí),內(nèi)核就會(huì)為該進(jìn)程分配一個(gè)file結(jié)構(gòu),表示打開的文件在進(jìn)程的上下文,然后應(yīng)用程序會(huì)通過一個(gè)int類型的文件描述符來訪問這個(gè)結(jié)構(gòu),實(shí)際上內(nèi)核的進(jìn)程里面維護(hù)一個(gè)file結(jié)構(gòu)的數(shù)組,而文件描述符就是相應(yīng)的file結(jié)構(gòu)在數(shù)組中的下標(biāo)。

dentry結(jié)構(gòu)(稱之為“目錄項(xiàng)”)記錄著文件的各種屬性,比如文件名、訪問權(quán)限等,每個(gè)文件都只有一個(gè)dentry結(jié)構(gòu),然后一個(gè)進(jìn)程可以多次打開一個(gè)文件,多個(gè)進(jìn)程也可以打開同一個(gè)文件,這些情況,內(nèi)核都會(huì)申請(qǐng)多個(gè)file結(jié)構(gòu),建立多個(gè)文件上下文。但是,對(duì)同一個(gè)文件來說,無論打開多少次,內(nèi)核只會(huì)為該文件分配一個(gè)dentry。所以,file結(jié)構(gòu)與dentry結(jié)構(gòu)的關(guān)系是多對(duì)一的。

同時(shí),每個(gè)文件除了有一個(gè)dentry目錄項(xiàng)結(jié)構(gòu)外,還有一個(gè)索引節(jié)點(diǎn)inode結(jié)構(gòu),里面記錄文件在存儲(chǔ)介質(zhì)上的位置和分布等信息,每個(gè)文件在內(nèi)核中只分配一個(gè)inode。 dentry與inode描述的目標(biāo)是不同的,一個(gè)文件可能會(huì)有好幾個(gè)文件名(比如鏈接文件),通過不同文件名訪問同一個(gè)文件的權(quán)限也可能不同。dentry文件所代表的是邏輯意義上的文件,記錄的是其邏輯上的屬性,而inode結(jié)構(gòu)所代表的是其物理意義上的文件,記錄的是其物理上的屬性。dentry與inode結(jié)構(gòu)的關(guān)系是多對(duì)一的關(guān)系。

sys_epoll_create -> sys_epoll_create1 -> fd_install:

總結(jié)epoll_create函數(shù)所做的事:調(diào)用epoll_create后,在內(nèi)核中分配一個(gè)eventpoll結(jié)構(gòu)和代表epoll文件的file結(jié)構(gòu),并且將這兩個(gè)結(jié)構(gòu)關(guān)聯(lián)在一塊,同時(shí),返回一個(gè)也與file結(jié)構(gòu)相關(guān)聯(lián)的epoll文件描述符fd。當(dāng)應(yīng)用程序操作epoll時(shí),需要傳入一個(gè)epoll文件描述符fd,內(nèi)核根據(jù)這個(gè)fd,找到epoll的file結(jié)構(gòu),然后通過file,獲取之前epoll_create申請(qǐng)eventpoll結(jié)構(gòu)變量,epoll相關(guān)的重要信息都存儲(chǔ)在這個(gè)結(jié)構(gòu)里面。接下來,所有epoll接口函數(shù)的操作,都是在eventpoll結(jié)構(gòu)變量上進(jìn)行的。

所以,epoll_create的作用就是為進(jìn)程在內(nèi)核中建立一個(gè)從epoll文件描述符到eventpoll結(jié)構(gòu)變量的通道。

epoll_ctl接口的作用是添加/修改/刪除文件的監(jiān)聽事件,內(nèi)核代碼如下:

sys_epoll_ctl:

根據(jù)前面對(duì)epoll_ctl接口的介紹,op是對(duì)epoll操作的動(dòng)作(添加/修改/刪除事件),ep_op_has_event(op)判斷是否不是刪除操作,如果op != EPOLL_CTL_DEL為true,則需要調(diào)用copy_from_user函數(shù)將用戶空間傳過來的event事件拷貝到內(nèi)核的epds變量中。因?yàn)?,只有刪除操作,內(nèi)核不需要使用進(jìn)程傳入的event事件。

接著連續(xù)調(diào)用兩次fdget分別獲取epoll文件和被監(jiān)聽文件(以下稱為目標(biāo)文件)的file結(jié)構(gòu)變量(備注:該函數(shù)返回fd結(jié)構(gòu)變量,fd結(jié)構(gòu)包含file結(jié)構(gòu))。

接下來就是對(duì)參數(shù)的一些檢查,出現(xiàn)如下情況,就可以認(rèn)為傳入的參數(shù)有問題,直接返回出錯(cuò):

當(dāng)然下面還有一些關(guān)于操作動(dòng)作如果是添加操作的判斷,這里不做解釋,比較簡(jiǎn)單,自行閱讀。

在ep里面,維護(hù)著一個(gè)紅黑樹,每次添加注冊(cè)事件時(shí),都會(huì)申請(qǐng)一個(gè)epitem結(jié)構(gòu)的變量表示事件的監(jiān)聽項(xiàng),然后插入ep的紅黑樹里面。在epoll_ctl里面,會(huì)調(diào)用ep_find函數(shù)從ep的紅黑樹里面查找目標(biāo)文件表示的監(jiān)聽項(xiàng),返回的監(jiān)聽項(xiàng)可能為空。

接下來switch這塊區(qū)域的代碼就是整個(gè)epoll_ctl函數(shù)的核心,對(duì)op進(jìn)行switch出來的有添加(EPOLL_CTL_ADD)、刪除(EPOLL_CTL_DEL)和修改(EPOLL_CTL_MOD)三種情況,這里我以添加為例講解,其他兩種情況類似,知道了如何添加監(jiān)聽事件,其他刪除和修改監(jiān)聽事件都可以舉一反三。

為目標(biāo)文件添加監(jiān)控事件時(shí),首先要保證當(dāng)前ep里面還沒有對(duì)該目標(biāo)文件進(jìn)行監(jiān)聽,如果存在(epi不為空),就返回-EEXIST錯(cuò)誤。否則說明參數(shù)正常,然后先默認(rèn)設(shè)置對(duì)目標(biāo)文件的POLLERR和POLLHUP監(jiān)聽事件,然后調(diào)用ep_insert函數(shù),將對(duì)目標(biāo)文件的監(jiān)聽事件插入到ep維護(hù)的紅黑樹里面:

sys_epoll_ctl -> ep_insert:

前面說過,對(duì)目標(biāo)文件的監(jiān)聽是由一個(gè)epitem結(jié)構(gòu)的監(jiān)聽項(xiàng)變量維護(hù)的,所以在ep_insert函數(shù)里面,首先調(diào)用kmem_cache_alloc函數(shù),從slab分配器里面分配一個(gè)epitem結(jié)構(gòu)監(jiān)聽項(xiàng),然后對(duì)該結(jié)構(gòu)進(jìn)行初始化,這里也沒有什么好說的。我們接下來看ep_item_poll這個(gè)函數(shù)調(diào)用:

sys_epoll_ctl -> ep_insert -> ep_item_poll:

ep_item_poll函數(shù)里面,調(diào)用目標(biāo)文件的poll函數(shù),這個(gè)函數(shù)針對(duì)不同的目標(biāo)文件而指向不同的函數(shù),如果目標(biāo)文件為套接字的話,這個(gè)poll就指向sock_poll,而如果目標(biāo)文件為tcp套接字來說,這個(gè)poll就是tcp_poll函數(shù)。雖然poll指向的函數(shù)可能會(huì)不同,但是其作用都是一樣的,就是獲取目標(biāo)文件當(dāng)前產(chǎn)生的事件位,并且將監(jiān)聽項(xiàng)綁定到目標(biāo)文件的poll鉤子里面(最重要的是注冊(cè)ep_ptable_queue_proc這個(gè)poll callback回調(diào)函數(shù)),這步操作完成后,以后目標(biāo)文件產(chǎn)生事件就會(huì)調(diào)用ep_ptable_queue_proc回調(diào)函數(shù)。

接下來,調(diào)用list_add_tail_rcu將當(dāng)前監(jiān)聽項(xiàng)添加到目標(biāo)文件的f_ep_links鏈表里面,該鏈表是目標(biāo)文件的epoll鉤子鏈表,所有對(duì)該目標(biāo)文件進(jìn)行監(jiān)聽的監(jiān)聽項(xiàng)都會(huì)加入到該鏈表里面。

然后就是調(diào)用ep_rbtree_insert,將epi監(jiān)聽項(xiàng)添加到ep維護(hù)的紅黑樹里面,這里不做解釋,代碼如下:

sys_epoll_ctl -> ep_insert -> ep_rbtree_insert:

前面提到,ep_insert有調(diào)用ep_item_poll去獲取目標(biāo)文件產(chǎn)生的事件位,在調(diào)用epoll_ctl前這段時(shí)間,可能會(huì)產(chǎn)生相關(guān)進(jìn)程需要監(jiān)聽的事件,如果有監(jiān)聽的事件產(chǎn)生,(revents & event->events 為 true),并且目標(biāo)文件相關(guān)的監(jiān)聽項(xiàng)沒有鏈接到ep的準(zhǔn)備鏈表rdlist里面的話,就將該監(jiān)聽項(xiàng)添加到ep的rdlist準(zhǔn)備鏈表里面,rdlist鏈接的是該epoll描述符監(jiān)聽的所有已經(jīng)就緒的目標(biāo)文件的監(jiān)聽項(xiàng)。并且,如果有任務(wù)在等待產(chǎn)生事件時(shí),就調(diào)用wake_up_locked函數(shù)喚醒所有正在等待的任務(wù),處理相應(yīng)的事件。當(dāng)進(jìn)程調(diào)用epoll_wait時(shí),該進(jìn)程就出現(xiàn)在ep的wq等待隊(duì)列里面。接下來講解epoll_wait函數(shù)。

總結(jié)epoll_ctl函數(shù):該函數(shù)根據(jù)監(jiān)聽的事件,為目標(biāo)文件申請(qǐng)一個(gè)監(jiān)聽項(xiàng),并將該監(jiān)聽項(xiàng)掛人到eventpoll結(jié)構(gòu)的紅黑樹里面。

epoll_wait等待事件的產(chǎn)生,內(nèi)核代碼如下:

sys_epoll_wait:

首先是對(duì)進(jìn)程傳進(jìn)來的一些參數(shù)的檢查:

參數(shù)全部檢查合格后,接下來就調(diào)用ep_poll函數(shù)進(jìn)行真正的處理:

sys_epoll_wait -> ep_poll:

ep_poll中首先是對(duì)等待時(shí)間的處理,timeout超時(shí)時(shí)間以ms為單位,timeout大于0,說明等待timeout時(shí)間后超時(shí),如果timeout等于0,函數(shù)不阻塞,直接返回,小于0的情況,是永久阻塞,直到有事件產(chǎn)生才返回。

當(dāng)沒有事件產(chǎn)生時(shí)((!ep_events_available(ep))為true),調(diào)用__add_wait_queue_exclusive函數(shù)將當(dāng)前進(jìn)程加入到ep->wq等待隊(duì)列里面,然后在一個(gè)無限for循環(huán)里面,首先調(diào)用set_current_state(TASK_INTERRUPTIBLE),將當(dāng)前進(jìn)程設(shè)置為可中斷的睡眠狀態(tài),然后當(dāng)前進(jìn)程就讓出cpu,進(jìn)入睡眠,直到有其他進(jìn)程調(diào)用wake_up或者有中斷信號(hào)進(jìn)來喚醒本進(jìn)程,它才會(huì)去執(zhí)行接下來的代碼。

如果進(jìn)程被喚醒后,首先檢查是否有事件產(chǎn)生,或者是否出現(xiàn)超時(shí)還是被其他信號(hào)喚醒的。如果出現(xiàn)這些情況,就跳出循環(huán),將當(dāng)前進(jìn)程從ep->wp的等待隊(duì)列里面移除,并且將當(dāng)前進(jìn)程設(shè)置為TASK_RUNNING就緒狀態(tài)。

如果真的有事件產(chǎn)生,就調(diào)用ep_send_events函數(shù),將events事件轉(zhuǎn)移到用戶空間里面。

sys_epoll_wait -> ep_poll -> ep_send_events:

ep_send_events沒有什么工作,真正的工作是在ep_scan_ready_list函數(shù)里面:

sys_epoll_wait -> ep_poll -> ep_send_events -> ep_scan_ready_list:

ep_scan_ready_list首先將ep就緒鏈表里面的數(shù)據(jù)鏈接到一個(gè)全局的txlist里面,然后清空ep的就緒鏈表,同時(shí)還將ep的ovflist鏈表設(shè)置為NULL,ovflist是用單鏈表,是一個(gè)接受就緒事件的備份鏈表,當(dāng)內(nèi)核進(jìn)程將事件從內(nèi)核拷貝到用戶空間時(shí),這段時(shí)間目標(biāo)文件可能會(huì)產(chǎn)生新的事件,這個(gè)時(shí)候,就需要將新的時(shí)間鏈入到ovlist里面。

僅接著,調(diào)用sproc回調(diào)函數(shù)(這里將調(diào)用ep_send_events_proc函數(shù))將事件數(shù)據(jù)從內(nèi)核拷貝到用戶空間。

sys_epoll_wait -> ep_poll -> ep_send_events -> ep_scan_ready_list -> ep_send_events_proc:

ep_send_events_proc回調(diào)函數(shù)循環(huán)獲取監(jiān)聽項(xiàng)的事件數(shù)據(jù),對(duì)每個(gè)監(jiān)聽項(xiàng),調(diào)用ep_item_poll獲取監(jiān)聽到的目標(biāo)文件的事件,如果獲取到事件,就調(diào)用__put_user函數(shù)將數(shù)據(jù)拷貝到用戶空間。

回到ep_scan_ready_list函數(shù),上面說到,在sproc回調(diào)函數(shù)執(zhí)行期間,目標(biāo)文件可能會(huì)產(chǎn)生新的事件鏈入ovlist鏈表里面,所以,在回調(diào)結(jié)束后,需要重新將ovlist鏈表里面的事件添加到rdllist就緒事件鏈表里面。

同時(shí)在最后,如果rdlist不為空(表示是否有就緒事件),并且由進(jìn)程等待該事件,就調(diào)用wake_up_locked再一次喚醒內(nèi)核進(jìn)程處理事件的到達(dá)(流程跟前面一樣,也就是將事件拷貝到用戶空間)。

到這,epoll_wait的流程是結(jié)束了,但是有一個(gè)問題,就是前面提到的進(jìn)程調(diào)用epoll_wait后會(huì)睡眠,但是這個(gè)進(jìn)程什么時(shí)候被喚醒呢?在調(diào)用epoll_ctl為目標(biāo)文件注冊(cè)監(jiān)聽項(xiàng)時(shí),對(duì)目標(biāo)文件的監(jiān)聽項(xiàng)注冊(cè)一個(gè)ep_ptable_queue_proc回調(diào)函數(shù),ep_ptable_queue_proc回調(diào)函數(shù)將進(jìn)程添加到目標(biāo)文件的wakeup鏈表里面,并且注冊(cè)ep_poll_callbak回調(diào),當(dāng)目標(biāo)文件產(chǎn)生事件時(shí),ep_poll_callbak回調(diào)就去喚醒等待隊(duì)列里面的進(jìn)程。

總結(jié)一下epoll該函數(shù): epoll_wait函數(shù)會(huì)使調(diào)用它的進(jìn)程進(jìn)入睡眠(timeout為0時(shí)除外),如果有監(jiān)聽的事件產(chǎn)生,該進(jìn)程就被喚醒,同時(shí)將事件從內(nèi)核里面拷貝到用戶空間返回給該進(jìn)程。

關(guān)于linuxio多路復(fù)用實(shí)驗(yàn)的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。

香港服務(wù)器選創(chuàng)新互聯(lián),2H2G首月10元開通。
創(chuàng)新互聯(lián)(www.cdcxhl.com)互聯(lián)網(wǎng)服務(wù)提供商,擁有超過10年的服務(wù)器租用、服務(wù)器托管、云服務(wù)器、虛擬主機(jī)、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗(yàn)。專業(yè)提供云主機(jī)、虛擬主機(jī)、域名注冊(cè)、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。


文章標(biāo)題:深度探究linuxio多路復(fù)用的實(shí)驗(yàn)過程與原理(linuxio多路復(fù)用實(shí)驗(yàn))
分享網(wǎng)址:http://m.jiaoqi3.com/article/dphecgo.html