文件描述符處理與回調(diào)函數(shù)
一、主要概念
- 反應(yīng)堆模型:一種處理系統(tǒng)事件或網(wǎng)絡(luò)事件的模型,當(dāng)文件描述符被激活時,可以檢測到
- 文件描述符:在操作系統(tǒng)中,用于標(biāo)識打開的文件、套接字等的一種數(shù)據(jù)類型?
- 處理激活的文件描述符的函數(shù):當(dāng)文件描述符被激活時,需要有一個函數(shù)來處理這些事件
- dispatch函數(shù):用于分發(fā)或處理不同類型事件的函數(shù)
- channel結(jié)構(gòu)體:存儲與文件描述符相關(guān)的事件處理動作的結(jié)構(gòu)體
- 回調(diào)函數(shù):在初始化channel對象時指定的讀回調(diào)和寫回調(diào),用于處理不同類型的事件
- select函數(shù):用于檢測多個文件描述符的狀態(tài),看是否有數(shù)據(jù)可讀或可寫
- fd_set集合:用于存儲文件描述符的集合,通過宏函數(shù)FD_ISSET判斷文件描述符是否被觸發(fā)
二、處理流程
- 當(dāng)反應(yīng)堆模型啟動,檢測到被激活的文件描述符
- 調(diào)用dispatch函數(shù),得到文件描述符fd
- 根據(jù)fd從channelMap中取出對應(yīng)的channel,判斷是讀事件還是寫事件
- 調(diào)用對應(yīng)的回調(diào)函數(shù),處理事件
- 在select函數(shù)中,通過fd_set集合判斷是否有數(shù)據(jù)可讀或可寫,調(diào)用eventActivate函數(shù)處理事件
- 在epoll函數(shù)中,通過epoll_wait進(jìn)行檢測,遍歷返回的events數(shù)組,調(diào)用eventActivate函數(shù)處理事件
- 在poll函數(shù)中,通過poll進(jìn)行檢測,遍歷返回的事件列表,調(diào)用eventActivate函數(shù)處理事件
三、注意事項
- 在調(diào)用回調(diào)函數(shù)時,需要傳入注冊時指定的參數(shù)
- 在使用回調(diào)函數(shù)時,需要注意處理函數(shù)的參數(shù)和返回值
四、概括
- 本文主要介紹了在EventLoop中處理被激活的文件描述符的事件和回調(diào)機(jī)制
- 當(dāng)反應(yīng)堆模型啟動時,可以檢測到被激活的文件描述符,并使用dispatch函數(shù)獲取文件描述符。(EventLoop初始化和啟動)
- 根據(jù)文件描述符從ChannelMap中取出對應(yīng)的channel,判斷是讀事件還是寫事件,并調(diào)用相應(yīng)的回調(diào)函數(shù)處理
核心觀點:
- 反應(yīng)堆模型啟動后,可以檢測到被激活的文件描述符
- 使用dispatch函數(shù)獲取文件描述符,并根據(jù)文件描述符從ChannelMap中取出對應(yīng)的channel
- 根據(jù)channel判斷是讀事件還是寫事件,并調(diào)用相應(yīng)的回調(diào)函數(shù)處理
- 在select函數(shù)中,通過fd_set集合判斷是否有數(shù)據(jù)可讀或可寫,調(diào)用eventActivate函數(shù)處理事件
- 在epoll函數(shù)中,通過epoll_wait進(jìn)行檢測,遍歷返回的events數(shù)組,調(diào)用eventActivate函數(shù)處理事件
- 在poll函數(shù)中,通過poll進(jìn)行檢測,遍歷返回的事件列表,調(diào)用eventActivate函數(shù)處理事件
>>回顧ChannelMap 模塊的實現(xiàn)和Channel 模塊的實現(xiàn)
- Channel.h
// 定義函數(shù)指針
typedef int(*handleFunc)(void* arg);
// 定義文件描述符的讀寫事件
enum FDEvent {
TimeOut = 0x01;
ReadEvent = 0x02;
WriteEvent = 0x04;
};
struct Channel {
// 文件描述符
int fd;
// 事件
int events;
// 回調(diào)函數(shù)
handleFunc readCallback;// 讀回調(diào)
handleFunc writeCallback;// 寫回調(diào)
// 回調(diào)函數(shù)的參數(shù)
void* arg;
};
?>>在EventLoop中處理被激活的文件描述符的事件
- EpollLoop.h?
// 處理被激活的文件描述符fd
int eventActivate(struct EventLoop* evLoop,int fd,int event);
- ?EpollLoop.c??
// 處理被激活的文件描述符fd
int eventActivate(struct EventLoop* evLoop,int fd,int event) {
if(fd < 0 || evLoop == NULL) {+
return -1;
}
// 取出channel
struct Channel* channel = evLoop->channelMap->list[fd];
assert(channel->fd == fd);
if(event & ReadEvent && channel->readCallback) {
channel->readCallback(channel->arg);
}
if(event & WriteEvent && channel->writeCallback) {
channel->writeCallback(channel->arg);
}
return 0;
}
>>回顧Dispatcher模塊,Dispatcher模塊的實現(xiàn)思路和定義?,補(bǔ)充代碼
- SelectDispatcher.c??
static int selectDispatch(struct EventLoop* evLoop,int timeout) {
struct SelectData* data = (struct SelectData*)evLoop->dispatcherData;
struct timeval val;
val.tv_sec = timeout;
val.tv_usec = 0;
fd_set rdtmp = data->readSet;
fd_set wrtmp = data->writeSet;
int count = select(Max,&rdtmp,&wrtmp,NULL,&val);
if(count == -1) {
perror("select");
exit(0);
}
for(int i=0;i<Max;++i) {
if(FD_ISSET(i,&rdtmp)) {
// 已續(xù)寫...
eventActivate(evLoop,i,ReadEvent);
}
if(FD_ISSET(i,&wrtmp)) {
// 已續(xù)寫...
eventActivate(evLoop,i,WriteEvent);
}
}
return 0;
}
- PollDispatcher.c??
static int pollDispatch(struct EventLoop* evLoop,int timeout) {
struct PollData* data = (struct PollData*)evLoop->dispatcherData;
int count = poll(data->fds,data->maxfd + 1,timeout * 1000);
if(count == -1) {
perror("poll");
exit(0);
}
for(int i=0;i<=data->maxfd;++i) {
if(data->fds[i].fd == -1) {
continue;
}
if(data->fds[i].revents & POLLIN) {
// 已續(xù)寫...
eventActivate(evLoop,data->fds[i].fd,ReadEvent);
}
if(data->fds[i].revents & POLLOUT) {
// 已續(xù)寫...
eventActivate(evLoop,data->fds[i].fd,WriteEvent);
}
}
return 0;
}
- EpollDispatcher.c??
static int epollDispatch(struct EventLoop* evLoop,int timeout) {
struct EpollData* data = (struct EpollData*)evLoop->dispatcherData;
int count = epoll_wait(data->epfd,data->events,Max,timeout * 1000);
for(int i=0;i<count;++i) {
int events = data->events[i].events;
int fd = data->events[i].data.fd;
if(events & EPOLLERR || events & EPOLLHUP) {
// 對方斷開了連接,刪除 fd
// epollRemove(&evLoop->channels[fd],evLoop);
continue;
}
if(events & EPOLLIN) {
// 已續(xù)寫...
eventActivate(evLoop,fd,ReadEvent);
}
if(events & EPOLLOUT) {
// 已續(xù)寫...
eventActivate(evLoop,fd,WriteEvent);
}
}
return 0;
}
五、回顧select函數(shù)、poll函數(shù)、epoll_wait函數(shù)(CodeGeex生成)
- select函數(shù)
Linux 中的 select 函數(shù)主要用于等待多個文件描述符(fd)就緒,從而可以一次性處理多個文件描述符。select 函數(shù)會檢查指定的文件描述符是否就緒,如果其中任何一個是就緒的,則 select 函數(shù)返回。select 函數(shù)的原型為:
#include <sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,const struct timeval *timeout);
其中,nfds 表示要檢查的文件描述符數(shù)量,readfds、writefds 和 exceptfds 分別表示要檢查的讀、寫和異常文件描述符集合,timeout 表示等待的時間限制。
例如,下面代碼等待兩個文件描述符(fd)就緒,最多等待 5 秒鐘:
int fd1 = open("file1", O_RDONLY);
int fd2 = open("file2", O_RDONLY);
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd1, &readfds);
FD_SET(fd2, &readfds);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int ret = select(2, &readfds, NULL, NULL, &timeout);
if (ret > 0) {
if (FD_ISSET(fd1, &readfds)) {
// fd1 is ready to read
}
if (FD_ISSET(fd2, &readfds)) {
// fd2 is ready to read
}
}
- poll函數(shù)
Linux 中的 poll 函數(shù)與 select 函數(shù)類似,用于等待多個文件描述符就緒,但是 poll 函數(shù)返回的是一個事件列表,而不是像 select 函數(shù)那樣返回就緒的文件描述符數(shù)量。poll 函數(shù)的原型為:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
其中,fds 是一個 pollfd 結(jié)構(gòu)數(shù)組,每個結(jié)構(gòu)中包含一個文件描述符和一個事件掩碼,timeout 表示等待的時間限制。
例如,下面代碼使用 poll 函數(shù)等待兩個文件描述符(FD)就緒,最多等待 5 秒鐘:
int fd1 = open("file1", O_RDONLY);
int fd2 = open("file2", O_RDONLY);
struct pollfd fds[2];
fds[0].fd = fd1;
fds[0].events = POLLIN;
fds[1].fd = fd2;
fds[1].events = POLLIN;
int timeout = 5000; // 5 秒
int ret = poll(fds, 2, timeout);
if (ret > 0) {
if (fds[0].revents & POLLIN) {
// fd1 is ready to read
}
if (fds[1].revents & POLLIN) {
// fd2 is ready to read
}
}
如果 poll 函數(shù)返回 0,則表示超時,如果返回值大于 0,則表示有文件描述符就緒。與 select 函數(shù)不同的是,poll 函數(shù)返回的是一個事件列表,而不是具體的文件描述符。
- epoll函數(shù)
Linux 中的 epoll 函數(shù)是 Linux 2.6.8 引入的一個新的 I/O 模型,它提供了高效的 I/O 事件通知機(jī)制,能夠處理大量文件描述符,并且具有低延遲和高吞吐量的優(yōu)點。
epoll 函數(shù)的原型為:
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epoll_fd, int op, int fd, int event);
int epoll_wait(int epoll_fd, struct epoll_event *events, int maxevents, int timeout);
其中,epoll_create 函數(shù)用于創(chuàng)建一個 epoll 實例,size 參數(shù)指定要創(chuàng)建的 epoll 實例可以容納的最大文件描述符數(shù)量。epoll_ctl 函數(shù)用于控制 epoll 實例,op 參數(shù)指定要進(jìn)行的操作(例如,添加、修改或刪除文件描述符),fd 參數(shù)指定要操作的文件描述符,event 參數(shù)指定要設(shè)置的事件掩碼。epoll_wait 函數(shù)用于等待 epoll 實例中的文件描述符就緒,events 參數(shù)指向一個 epoll_event 結(jié)構(gòu)數(shù)組,用于存儲就緒的文件描述符和事件信息,maxevents 參數(shù)指定最多等待的文件描述符數(shù)量,timeout 參數(shù)指定等待的時間限制。
例如,下面代碼使用 epoll 函數(shù)等待兩個文件描述符(fd)就緒,最多等待 5 秒鐘:文章來源:http://www.zghlxwxcb.cn/news/detail-777311.html
int fd1 = open("file1", O_RDONLY);
int fd2 = open("file2", O_RDONLY);
int epoll_fd = epoll_create(10);
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd1;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd1, &event);
event.data.fd = fd2;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd2, &event);
struct epoll_event events[2];
int timeout = 5000; // 5 秒
int num = epoll_wait(epoll_fd, events, 2, timeout);
if (num > 0) {
if (events[0].data.fd == fd1 && events[0].events & EPOLLIN) {
// fd1 is ready to read
}
if (events[1].data.fd == fd2 && events[1].events & EPOLLIN) {
// fd2 is ready to read
}
}
如果 epoll 函數(shù)返回 0,則表示超時,如果返回值大于 0,則表示有文件描述符就緒。與 select 和 poll 函數(shù)不同的是,epoll 函數(shù)能夠處理大量的文件描述符,并且具有低延遲和高吞吐量的優(yōu)點文章來源地址http://www.zghlxwxcb.cn/news/detail-777311.html
到了這里,關(guān)于基于多反應(yīng)堆的高并發(fā)服務(wù)器【C/C++/Reactor】(中)在EventLoop中處理被激活的文件描述符的事件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!