1. 程式人生 > >最高效的進(線)程間通訊機制--eventfd

最高效的進(線)程間通訊機制--eventfd



我們常用的程序(執行緒)間通訊機制有管道,訊號,訊息佇列,訊號量,共享記憶體,socket等等,其中主要作為程序(執行緒)間通知/等待的有管道pipe和socketpair。執行緒還有特別的condition。

今天來看一個liunx較新的系統呼叫,它是從LINUX 2.6.27版本開始增加的,主要用於程序或者執行緒間的通訊(如通知/等待機制的實現)。

標頭檔案

編輯 #include <sys/eventfd.h>

eventfd函式原型

編輯 int eventfd(unsigned int initval, int flags);

eventfd說明

編輯 eventfd()建立一個“eventfd物件”,這個物件能被使用者空間應用用作一個事件等待/響應機制,靠核心去響應使用者空間應用事件。這個物件包含一個由核心保持的無符號64位整型計數器。這個計數器由引數initval說明的值來初始化。

它的標記可以有以下屬性:

EFD_CLOECEX,EFD_NONBLOCK,EFD_SEMAPHORE。

在linux直到版本2.6.26,這個flags引數是沒用的,必須指定為0。


它返回了一個引用eventfd object的描述符。這個描述符可以支援以下操作:

read:如果計數值counter的值不為0,讀取成功,獲得到該值。如果counter的值為0,非阻塞模式,會直接返回失敗,並把errno的值指紋EINVAL。如果為阻塞模式,一直會阻塞到counter為非0位置。

write:會增加8位元組的整數在計數器counter上,如果counter的值達到0xfffffffffffffffe時,就會阻塞。直到counter的值被read。阻塞和非阻塞情況同上面read一樣。

close:這個操作不用說了。

eventfd實現

編輯 程式碼選自linux-3.18。 核心資料結構:struct eventfd_ctx { struct kref kref; wait_queue_head_t wqh; __u64 count; unsigned int flags; }; eventfd系統呼叫建立一個匿名檔案,關聯一個eventfd_ctx的結構體,裡面的count用來計數,wqh用來喚醒等待eventfd事件的task。看來只能在父子程序中做簡單的訊息通知,效能上比pipe好一些。

例項

#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");
    }
}

結果如下:


如果設定EFD_NONBLOCK 則為非阻塞,父程序讀不到資料會直接返回,0 是阻塞的。