epoll的一些总结

epoll介绍

epoll是一种当文件描述符的内核缓冲区非空的时候,发出可读信号进行通知,当写缓冲区不满的时候,发出可写信号通知的机制。

使用DEMO

#define MAX_EVENTS 10
struct epoll_event  ev, events[MAX_EVENTS];
int   listen_sock, conn_sock, nfds, epollfd;

epollfd = epoll_create( 1024 );//需要判断

ev.events   = EPOLLIN;          //
ev.data.fd  = listen_sock;
if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, listen_sock, &ev ) == -1 )
{
    perror( "epoll_ctl: listen_sock" );
    exit( EXIT_FAILURE );
}

while(1)
{
    nfds = epoll_wait( epollfd, events, MAX_EVENTS, -1 );
    if ( nfds == -1 )
    {
        perror( "epoll_wait" );
        exit( EXIT_FAILURE );
    }

    for ( n = 0; n < nfds; ++n )
    {
        if ( events[n].data.fd == listen_sock )
        {
            conn_sock = accept( listen_sock,
                        (struct sockaddr *) &local, &addrlen );
            if ( conn_sock == -1 )
            {
                perror( "accept" );
                exit( EXIT_FAILURE );
            }
            setnonblocking( conn_sock );
            ev.events   = EPOLLIN | EPOLLET;
            ev.data.fd  = conn_sock;
            if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, conn_sock,
                    &ev ) == -1 )
            {
                perror( "epoll_ctl: conn_sock" );
                exit( EXIT_FAILURE );
            }
        } 
        else 
        {
            do_use_fd( events[n].data.fd );
        }
    }
}

特点:有自己独立的文件系统

启动:
1>.在linux系统启动时候,就会初始化一个eventpolls文件系统:
1.eventpoll_init():创建两块缓存==>红黑树and双链表
2.register_filesystem():在文件系统中注册一个eventpolls文件系统
3.kern_mount():挂载至linux文件系统中

API:
1.epoll_creat(size_t):size_t已经没有作用,只是兼容老版本
  创建一个struct eventpoll对象:
            struct eventpoll *ep = NULL;
            ep_alloc(&ep);
  然后分配一个未使用的文件描述符:
            fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
  struct eventpoll
  {
      ...
      struct rb_root rbr;      //红黑树节点
      struct list_head rdlist; //epoll_wait()返回的事件,双链表
  };
  @@每一个epoll对象都有一个独立的eventpoll结构体,通过epoll_ctl添加事件,都挂在红黑树上(红黑树插入效率高);所有添加在epoll中的事件都会与设备驱动程序建立回调关系==>ep_poll_callback,把将发生的事件添加到rdlist中;
  @@每一个添加到epoll的事件都会建立一个结构体==>
  struct epitem
  {
      struct rb_node rbn;          //红黑树节点
      struct list_head rdlist;     //双链表节点
      struct epoll_filed ffd;      //事件句柄信息
      struct eventpoll *ep;        //所属eventpool对象
      struct epoll_event event;    //期待发生的事件类型
  }

  2.epoll_ctl()
 @ epoll_ctl()首先判断op是不是删除操作,如果不是则将event参数从用户空间拷贝到内核中.
 @接下来判断用户是否设置了EPOLLEXCLUSIVE标志,这个标志是4.5版本内核才有的,主要是为了解决同一个文件描述符同时被添加到多个epoll实例中造成的“惊群”问题,详细描述可以看这里。 这个标志的设置有一些限制条件,比如只能是在EPOLL_CTL_ADD操作中设置,而且对应的文件描述符本身不能是一个epoll实例.

 3.epoll_wait()
 1>sys_epoll_wait():参数检查,查看epfd是否为eventpoll类型,获取eventpoll结构体
 2>ep_poll():定义一个局部变量wait(等待队列节点),判断eventpoll中rdlist是否为空,为空的话将wait设为current,并加入等待队列中,将当前进程设为INTERRUPTIBLE(可中断的)之后进入sleep,一段时间唤醒后再检查等待队列空否,直到不为空或者有回调唤醒。
 3>ep_insert()
 往红黑树中添加节点:
 向fd的等待队列上挂载一个节点,注册回调函数ep_poll_callback(),设备就绪就调用回调==>获取fd 和 epfd,将epiteam挂到对应的epfd和eventpoll中的rdlist上 

rdlist上的fd ==> events[](用户传的)

epoll高效原因:
epoll的高效就在于,当我们调用epoll_ctl往里塞入百万个句柄时,epoll_wait仍然可以飞快的返回,并有效的将发生事件的句柄给我们用户。这是由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。所以,epoll_wait非常高效。

epoll的LT和ET::

ET高效是因为将rdlist上的fd拷给用户后就清空,而LT模式下还会检查这些fd,如果有未完成的还会拷回rdlist中。

猜你喜欢

转载自blog.csdn.net/m18706819671/article/details/80624713