20190417_Epoll详解

1、Epoll和select差别

epollselect都是用来通知触发事件的,举个例子:

假如小明在房间写作业,此时外边可能发生的触发事件有    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);
	
}
发布了12 篇原创文章 · 获赞 1 · 访问量 1016

猜你喜欢

转载自blog.csdn.net/qq_37718322/article/details/89357660