最高效的进(线)程间通信机制--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 是阻塞的。



猜你喜欢

转载自blog.csdn.net/a_tu_/article/details/80325645