linux-muduo线程通信eventfd

eventfd 在内核版本,2.6.22以后有效。查看内核版本可以用命令 uname -r
在看muduo源码时,项目中使用eventfd机制实现线程间的唤醒(通知)。觉得效率比较高,所以拿来学习下。其也可以实现父子进程间的通信。
首先使用: man eventfd
name:eventfd–创建一个用于事件通知的描述符

SYNOPSIS:

#include  
int eventfd(unsigned int initval,int flags); 

DESCRIPTION:
eventfd是用于创建一个“eventfd object”,它可以用于用户态-用户态和内核态-用户态之间实现事件通知的机制,这个object包括一个64位计数器(initval,内核维护)。
flags–可以改变eventfd的行为,
EFD_CLOEXEC(从linux 2.6.27开始支持)–类似于close-on-exec(FD_CLOEXEC)在open函数的作用,即这个句柄我在fork子进程后执行exec时就关闭。
EFD_NONBLOCK(从linux 2.6.27开始支持)–当使用eventfd创建一个新的描述符设置了该状态时,将该描述符设为非阻塞状态。
EFD_SEMAPHORE(从linux 2.6.30开始支持)–对于read该文件描述符是提供类似信号的语义,具体含义如下会介绍。
return values 返回值:eventfd()返回一个新的文件描述符(efd)可以用于引用eventfd对象,可以对efd执行以下操作:
read(2):如果读成功,则返回一个8-byte的整数,如果读入的数小于8-byte,则会导致读错误,并设置error 为EIVAL。返回的整数是主机序的。read(2)返回值的语义是由当前的initval非零和是否设置EFD_SEMAPHORE flag决定的。
* 如果EFD_SEMAPHORE flag没有被设置,并且eventfd 的counter是一个非零值,则read(2)会返回该counter 的数值,并且将该counter置零。
* 如果设置了EFD_SEMAPHORE flag并且counter的值是非零的,则read(2)会返回一个8-byte 的为1的整数,并且将counter的值减1.
* 如果在read(2)的时候,counter的值为0,则将会阻塞直到counter大于0,如果设置为 EFD_NONBLOCK,则会立刻返回,并将error设置为EAGAIN。
write(2):改操作会将当前的一个8-byte的数字累加到counter的缓存中,counter的最大值为2^64-1。如果write导致counter值超过最大值,则会阻塞直到对该描述符执行read(2),否则会导致写失败,返回错误 EAGAIN。如果写入的数值字节小于8-byte,则会返回错误,EINVAL。
poll(2),select(2),epoll(2)都可以监听该描述符。
close(2) – 当这个文件描述符不需要的时候,我们需要关闭它。当所有的关联的文件描述符都被关闭时,内核中的eventfd对象才会被释放。

当fork()时,这个文件描述符是会被子进程继承,重复的文件描述符被关联到相同的eventfd对象。如果创建eventfd时没有设置EFD_CLOEXEC,则子进程调用execve(2)后,该文件描述符将会被保留。
返回值:成功?efd:(-1);
**注意: 和pipe的比较:
 在使用pipe作为简单的信号通信功能的所有情况都可以使用eventfd替代。一个eventfd文件描述符的开销远小于一个管道,并且只需要一个文件描述符。**
 多线程例程:

muduo源码中,用于唤醒一个线程

EventLoop::EventLoop()
  : looping_(false),
    quit_(false),
    eventHandling_(false),
    callingPendingFunctors_(false),
    iteration_(0),
    threadId_(CurrentThread::tid()),
    poller_(Poller::newDefaultPoller(this)),
    timerQueue_(new TimerQueue(this)),
    wakeupFd_(createEventfd()),
    wakeupChannel_(new Channel(this, wakeupFd_)),
    currentActiveChannel_(NULL)
int createEventfd()
{
  int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
  if (evtfd < 0)
  {
    LOG_SYSERR << "Failed in eventfd";
    abort();
  }
  return evtfd;
}

void EventLoop::wakeup()
{
  uint64_t one = 1;
  ssize_t n = sockets::write(wakeupFd_, &one, sizeof one);
  if (n != sizeof one)
  {
    LOG_ERROR << "EventLoop::wakeup() writes " << n << " bytes instead of 8";
  }
}

父子进程之间通知:

#include 
       #include 
       #include 
       #include 
       #include              /* Definition of uint64_t */

       #define handle_error(msg) \
           do { perror(msg); exit(EXIT_FAILURE); } while (0)

       int
       main(int argc, char *argv[])
       {
           int efd, j;
           uint64_t u;
           ssize_t s;

           if (argc < 2) {
               fprintf(stderr, "Usage: %s ...\n", argv[0]);
               exit(EXIT_FAILURE);
           }
 efd = eventfd(0, 0);
           if (efd == -1)
               handle_error("eventfd");

           switch (fork()) {
           case 0:
               for (j = 1; j < argc; j++) {
                   printf("Child writing %s to efd\n", argv[j]);
                   u = strtoull(argv[j], NULL, 0);
                           /* strtoull() allows various bases */
                   s = write(efd, &u, sizeof(uint64_t));
                   if (s != sizeof(uint64_t))
                       handle_error("write");
               }
               printf("Child completed write loop\n");

               exit(EXIT_SUCCESS);

           default:
               sleep(2);
printf("Parent about to read\n");
               s = read(efd, &u, sizeof(uint64_t));
               if (s != sizeof(uint64_t))
                   handle_error("read");
               printf("Parent read %llu (0x%llx) from efd\n",
                       (unsigned long long) u, (unsigned long long) u);
               exit(EXIT_SUCCESS);

           case -1:
               handle_error("fork");
           }
       }

gcc main.c -o main
./main  1 2 4 7 14 
child writing 1 to efd
child writing 2 to efd
child writing 4 to efd
child writing 7 to efd
child writing 14 to efd
child completed write loop
parent about to read
parent read 28 (0x1c) from efd

你可能感兴趣的:(linux,muduo源码分析)