linux-線程/進程通信eventfd


eventfd 在內核版本,2.6.22以後有效。查看內核版本可以用命令 uname -r
在看muduo源碼時,項目中使用eventfd機制實現線程間的喚醒(通知)。覺得效率比較高,所以拿來學習下。其也可以實現父子進程間的通信。
首先使用: man eventfd
name:eventfd–創建一個用於事件通知的描述符

SYNOPSIS:

#include<sys/eventfd.h>  
int eventfd(unsigned int initval,int flags); 
  • 1
  • 2

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文件描述符的開銷遠小於一個管道,並且只需要一個文件描述符。**
 多線程例程:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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";
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

父子進程之間通知:

#include <sys/eventfd.h>
       #include <unistd.h>
       #include <stdlib.h>
       #include <stdio.h>
       #include <stdint.h>             /* 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 <num>...\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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章