1、Epoll和select差别
epoll和select都是用来通知触发事件的,举个例子:
假如小明在房间写作业,此时外边可能发生的触发事件有 A :有人按门铃,B:饭烧好了,C:小狗饿了。
select的通知原理:
如果事件C发生了,select会通知小明外面有事件发生了,但是不会告诉小明发生了什么事,所以小明要依次去查看到底发生了什么事。他会先去门口看是否有人按门铃,再去厨房看饭有没有烧好,再去看小狗有没有饿,直到找到发生的事件,再去做对应处理。
epoll的通知原理 :
如果事件C发生了,epoll会通知小明外面有事件发生了,且会明确告诉小明是事件C,所以小明不需要去查询其他可能发生的事件,直接去处理C事件。
epoll和select的差别
总结:
有事件发生select只会通报一个总的消息,具体事件需要自己去轮训查找,效率比较低,不适合处理大量事件爆发的场合;epoll的话会直接报道具体是哪个事件发生,效率高,适合处理大量事件爆发的场合。
2、Epoll涉及的函数
Epoll创建函数:int epoll_create(int size)
int epoll_create(int size)
size 输入传参,表示这个EpollFd能关注的最大事件数目
return Epoll_Fd专用描述符号
Epoll控制行数:int epoll_ctl(int Epoll_Fd, int operation, int file_fd, struct epoll_event *EventMode)
int epoll_ctl(int Epoll_Fd, int operation, int file_fd, struct epoll_event *EventMode)
Epoll_Fd epoll_create()的返回值,即epoll专用描述符。
operation 操作方式,由三个宏控制,注册 EPOLL_CTL_ADD 、修改 EPOLL_CTL_MOD 、删除 EPOLL_CTL_DEL
比如向这个Epoll_Fd添加一个要监控的对象文件,只要把受控对象文件描述符file_fd添加即可。
file_fd 受控对象文件描述符file_fd
EventMode 监听方式等
struct epoll_event
{
__uint32_t events; events可以是以下几个宏的集合:
EPOLLIN: 对应文件描述符上有可读数据。;
EPOLLOUT: 对应的文件描述符上可以写数据;
EPOLLPRI: 表示对应的文件描述符有紧急的数据可读;
EPOLLERR: 表示对应的文件描述符发生错误;
EPOLLHUP: 表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT: 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
epoll_data_t data; 这个事件对应的一些参数,自己赋值。
}
data里面的参数使我们自己定义的,在向Epoll_Fd添加受控文件描述符file_Fd时,可以填充。
这样在Epoll_Waite到file_Fd有事件触发时,会把data里面的填充值返回出来。
typedef union epoll_data
{
void *ptr; 这个也可以填充,事件处理回调函数地址,当waite到这个事件时,就调用相应处理函数
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
Epoll等待行数:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
int epoll_wait(int Epoll_Fd,struct epoll_event * events,int max_events,int timeout)
Epoll_Fd epoll_create()的返回值,即epoll专用描述符。
events 触发理事件对应的数组
max_events 每次能处理的事件数
timeout 一般-1即可,-1表示永远阻塞,等于N的话表示,N时间后还没有事件发生就不阻塞
return 返回触发事件的个数,一次waite可能里面有多个事件发生
3、Epoll应用
#define FILE_FD "/dev/xx"
typedef VOID (* EventProcFunc)(VOID *);
定义一个结构体,这个结构体要我们自己填充的。
typedef struct Epoll_Info_S;
{
int Epoll_Fd; epoll_create()的返回值,即epoll专用描述符
int size; 最大能监听个数
int file_fd 被监控对象描述符
struct epoll_event *pstEvent_array 事件数组首地址
EventProcFunc Event_Proc_func 触发事件回调函数
}Epoll_Info_S;
My_EpollCreate(int size,Epoll_Info_S *Epoll_Info,EventProcFunc Event_Proc_func)
{
long Epoll_Fd = -1;
struct epoll_event *pstEvent_array 事件数组首指针
创建epoll
Epoll_Fd=epoll_create(size);
epoll事件分配内存,根据最大支持数来分配
pstEvent_array = (struct epoll_event *) BA_Malloc(sizeof(struct epoll_event) * ulSize);
Epoll_Info->Epoll_Fd = Epoll_Fd;
Epoll_Info->Max_Size = size;
Epoll_Info->pstEvent_array = pstEvent_array;
Epoll_Info->Event_Proc_func = Event_Proc_func;
}
My_EpollAdd(int Epoll_Fd,int file_fd,Epoll_Info_S *Epoll_Info )
{
struct epoll_event stEvent; 创建这个stEvent只是为了设置属性用,退出这个函数就没用了,跟My_EpollCreate里面的pstEvent_array没有任何关系。
memset(&stEvent, 0, sizeof(stEvent));
stEvent.events = EPOLLIN|EPOLLOUT; 有可读数据或者能写数据时都触发
stEvent.data.ptr = (void *)Epoll_Info; 本例子填充了传参地址,以后这个最好填写对应事件回调函数的地址。
epoll_ctl(Epoll_Fd, EPOLL_CTL_ADD, file_fd, &stEvent);
}
My_EpollWaite(Epoll_Info_S *Epoll_Info)
{
LONG WaiteEvenNum = 0; 一个waite到多少个触发事件
int i = 0 ;
VOID *pData = NULL;
//waite到事件之后,会把在添加Epoll时,Event中填充的数据返回到Epoll_Info->pstEvent_array对应的数组中
WaiteEvenNum = epoll_wait(Epoll_Info->Epoll_Fd, Epoll_Info->pstEvent_array, Epoll_Info->Max_Size,-1);
for (i = 0; i < WaiteEvenNum; i++)
{
pData = Epoll_Info->pstEvent_array[i].data.ptr;
Epoll_Info->Event_Proc_func(pData);
}
}
Epoll_Waite到事件后会执行这个行数
VOID my_func(void * Param)
{
int file_fd;
Epoll_Info_S *Epoll_Info = NULL ;
Epoll_Info = (Epoll_Info_S *)Param;
file_fd = Epoll_Info->file_fd;
……
lRet = ioctl(file_fd, XX, XX); 事件回调函数,从受控对象FD中读取数据。
这个file_fd是通过event中填充的地址传下来的的。
……
}
int main(int argc,char **argv )
{
int file_fd = 0 ; //受控文件描述符
Epoll_Info_S Epoll_Info = {0}; //数据结构体
file_fd = open(FILE_FD, O_RDWR); //打开节点,返回受控文件描述符
//创建epoll 把Epoll_Fd和回调函数地址填充到Epoll_Info数据结构体
My_EpollCreate(128,&Epoll_Info,my_func);
//把受控文件file_fd添加到Epoll_Fd,并且把数据结构体Epoll_Info地址填充到file_fd对应的事件参数中。
My_EpollAdd(Epoll_Info->Epoll_Fd,file_fd,&Epoll_Info)
//创建监听线程,线程执行函数My_EpollWaite,参数为Epoll_Info
pthread_create(XX, XX ,My_EpollWaite, &Epoll_Info)
while(1);
}