eventfd

eventfd 在内核版本,2.6.22以后有效。查看内核版本可以用命令 uname -r 。

  • 原型
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
 /**
  * initval: 创建eventfd时它所对应的64位计数器的初始值,可以写0
  * flags: 是2.6.26或之前版本的内核,flags 必须设置为0
  *      EFD_CLOEXEC表示返回的eventfd文件描述符在fork后exec其他程序时会自动关闭这个文件描述符;
  *      EFD_NONBLOCK设置返回的eventfd非阻塞;
  *      EFD_SEMAPHORE表示将eventfd作为一个信号量来使用。
  *
  *eventfd设置了EFD_SEMAPHORE,那么每次read就会返回1,并且让eventfd对应的计数器减一;
  *如果eventfd没有设置EFD_SEMAPHORE,那么每次read就会直接返回计数器中的数值,read之后计数器就会置0。不管是哪一种,当计数器为0时,如果继续read,那么read就会阻塞(如果eventfd没有设置EFD_NONBLOCK)或者返回EAGAIN错误(如果eventfd设置了EFD_NONBLOCK)。
  */

efd = eventfd(0, 0);
if (efd == -1)
    handle_error("eventfd");
  • 作用

    • eventfd顾名思义就是事件fd类型,就是专门用于事件同志的文件描述符(fd)
    • eventfd 是一个计数相关的fd,计数不为零是有可读事件发生 write 仅仅是加计数,read 是读计数,并且清零
  • eventfd 结合epoll
    eventfd 是专门用来传递事件的 fd ,而 epoll 池则是专门用来管理事件的池子,它们两结合就妙了
    eventfd 可以和 libaio & epoll 一起,实现 Linux 下的纯异步 IO
    ext4 这种文件 fd 一直可读可写,所以实现 poll 毫无意义。eventfd 一直可写,所以监听可写毫无意义;
    eventfd 可以结合业务,做一个事件通知的通信机制,非常巧妙;

  • 子线程多次写入多个值,主线程一次读出所有值的和

  #include <iostream>                                                                                                                
  #include <sys/eventfd.h>
  #include <sys/wait.h>
  #include <errno.h>
  #include <unistd.h>

  using namespace std;
  
  int main(int argc,char *argv[])
  {
      int efd,j;
      uint64_t u;
      ssize_t s;
      if(argc < 2)
      {
          printf("number of argc is wrong!\n");
          return 0;
      }
  
      efd = eventfd(0,0);
      if( -1 == efd )
          printf("failed to create eventfd\n");
  
      switch (fork()) {
          case 0:
              for(j=1;j<argc;j++)
              {
                  printf("child writing %s to efd\n",argv[j]);
                  u = strtoul(argv[j],NULL,0);
                  s = write(efd,&u,sizeof(uint64_t));
                  if(s!=sizeof(uint64_t))
                      printf("write efd failed\n");
              }
              printf("Child completed write loop\n");
              exit(0);
              break;
          default:
              sleep(2);
              printf("Parents about to read\n");
              s = read(efd,&u,sizeof(uint64_t));
              if(s != sizeof(uint64_t))
                  printf("read efd failed\n");
             printf("Parents first read %llu (0x%llu) from efd\n",u,u);
              exit(0);
          case -1:
              printf("fork error\n");
      }
      return 0;
  }

如果没有写入操作,但是并没有导致初始值变化,则主线程会一直挂在read操作上

  • eventfd可以被epoll监控, 一旦有状态变化,可以触发通知
#include <iostream>
  #include <sys/eventfd.h>
  #include <sys/epoll.h>
  #include <string.h>
  #include <unistd.h>
  #include <pthread.h>

  int g_iEvtfd = -1;

 void * pthread_cback(void *arg)
  {
      uint64_t uiWrite = 1;
      while(1)
      {
          sleep(2);
          if(0 != eventfd_write(g_iEvtfd,uiWrite))
          {
              printf("child write iEvtfd failed\n");                                                         
          }
      }
      pthread_exit(NULL);
  }

  int main()
  {
     int iEvtfd,j;
     uint64_t uiWrite = 1;
      uint64_t uiRead;
      ssize_t s;
      int iEpfd;

      struct epoll_event stEvent;
      int iRet = 0;
      struct epoll_event stEpEvent;
      pthread_t stWthread;

      iEpfd = epoll_create(1);
      if( -1 == iEpfd )
      {
          printf("Create epoll failed.\n");
          return 0;
      }

      iEvtfd = eventfd(0,0);
      if (-1 == iEvtfd)
      {
          printf("failed to create eventfd\n");
          return 0;
      }
  
      g_iEvtfd = iEvtfd;
  
      memset(&stEvent,0,sizeof(struct epoll_event));
      stEvent.events = (unsigned long) EPOLLIN;
      stEvent.data.fd = iEvtfd;
      iRet = epoll_ctl(iEpfd,EPOLL_CTL_ADD,g_iEvtfd,&stEvent);
      if(0 != iRet)
      {
          printf("failed to add iEvtfd to epoll\n");
          close(g_iEvtfd);
          close(iEpfd);
          return 0;
      }
  
      iRet = pthread_create(&stWthread,NULL,pthread_cback,NULL);                                             
  
      if(0 != iRet)
      {
          close(g_iEvtfd);
          close(iEpfd);
          return 0;
      }
  
      for(;;)
      {
          iRet = epoll_wait(iEpfd,&stEpEvent,1,-1);
          if(iRet > 0)
          {
              s = eventfd_read(iEvtfd,&uiRead);
              if(s != 0)
              {
                  printf("read iEvtfd failed\n");
                  break;
              }
             printf("Read %llu (0x%llx) from iEvtfd\n",uiRead,uiRead);
          }
      }
  
      close(g_iEvtfd);
      close(iEpfd);
      return 0;
  }

posted on 2021-07-29 07:25  lodger47  阅读(230)  评论(0)    收藏  举报

导航