Linux进程/线程协作之eventfd

本文介绍Linux系统调用eventfd的基本原理及其应用场景,包括线程间和进程间的通信,并附带示例代码展示如何使用eventfd进行同步。

eventfd是linux的一个系统调用,为事件通知创建文件描述符。
eventfd()创建一个“eventfd对象”,这个对象能被用户空间应用用作一个事件等待/响应机制,靠内核去响应用户空间应用事件。这个对象包含一个由内核保持的无符号64位整型计数器。这个计数器由参数initval说明的值来初始化。
以下例子展示了在线程之间及fork()产生的进程之间的通信。

/* 创建线程之后主线程和子线程谁先运行是不确定的。
 * 通过一个eventfd在线程之间传递数据的好处是多个线程无需上锁就可以实现同步。
 * eventfd支持的最低内核版本为Linux 2.6.27,在2.6.26及之前的版本也可以使用eventfd,但是flags必须设置为0。
 * 函数原型:
 *     #include <sys/eventfd.h>
 *     int eventfd(unsigned int initval, int flags);
 * 参数说明:
 *      initval,初始化计数器的值。
 *      flags, EFD_NONBLOCK,设置socket为非阻塞。
 *             EFD_CLOEXEC,执行fork的时候,在父进程中的描述符会自动关闭,子进程中的描述符保留。
 * 场景:
 *     eventfd可以用于同一个进程之中的线程之间的通信。
 *     eventfd还可以用于同亲缘关系的进程之间的通信。
 *     eventfd用于不同亲缘关系的进程之间通信的话需要把eventfd放在几个进程共享的共享内存中(没有测试过)。
 */
#include <stdlib.h>
#include <stdio.h>
#include <sys/eventfd.h>
#include <pthread.h>
#include <memory.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>

int efd = -1;
//循环接收线程。
void * recv(void * pars) {
    int len = -1;
    uint64_t msg = 0;

    printf("Thread created!\n");
    while(1) {
        len = read(efd, &msg, sizeof(uint64_t)); //如果计数器为0则阻塞。
        printf("Current msg is %d\n", msg);
    }
}

int main(int argc, char ** argv) {
    pthread_t tid;
    pid_t fpid;
    int len = -1;
    uint64_t item = 1;
    int i = 0;

    efd = eventfd(0, 0);
    if(efd == -1) {
        printf("Error! Fail to create event fd.\n");
    } else {
        printf("OK! Create event fd is %d\n", efd);
    }
    fpid = fork();
    if(fpid > 0) {  //父进程接收消息(父进程同时也发送消息。)
        pthread_create(&tid, NULL, recv, NULL);
    }

    //循环发送。
    while(1) {
        sleep(1);
        len = write(efd, &item, sizeof(uint64_t)); //如果写sizeof(unsigned int)则参数错误。必须传8。
                                    //如果计数器达到0xfffffffe,则阻塞。
        if(len != sizeof(uint64_t)) {
            printf("Error! Fail to write event fd, errno: %d,errmsg: %s\n"
                    ,errno, strerror(errno));
        }else {
            printf("Success to write.\n");
        }
    }
    pthread_exit(0);
    return 0;
}
<think>好的,我现在要解决的问题是:当进程创建线程时,eventfd类型的句柄数量是否会增加。我需要结合提供的引用资料和系统知识来分析这个问题。 首先,回顾一下eventfd的基本知识。根据引用[4],eventfd是一个内核维护的计数器,通过文件描述符(fd)来操作。每个eventfd调用会创建一个新的eventfd对象,并返回对应的fd。例如,在muduo库的代码中,createEventfd函数调用eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)来创建这样的fd,并存储在wakeupFd_中。引用[2]提到eventfd可以加入epoll监听,说明它作为文件描述符的特性。 接下来,考虑进程线程之间的关系。在Linux中,线程进程的区别在于资源共享。进程拥有独立的地址空间,而同一进程下的线程共享地址空间,包括文件描述符表。当一个线程打开一个文件或者创建eventfd时,得到的文件描述符会被添加到进程的文件描述符表中,所有线程都可以访问这个fd。 现在问题转化为:当创建新线程时,是否会复制或增加eventfd的句柄数量?根据Linux的文件描述符管理机制,文件描述符属于进程级别,而不是线程级别。所以,无论创建多少个线程,只要它们属于同一个进程,它们共享同一个文件描述符表。例如,如果主线程创建了一个eventfd,其对应的fd是3,那么新线程可以直接使用这个fd,而不会创建新的eventfd实例,也不会增加该进程eventfd句柄数量。 引用中的代码示例也支持这一点。比如在EventLoop的构造函数中,wakeupFd_在进程初始化时创建,所有线程共享这个eventfd,用于线程间的事件通知。即使创建多个线程,它们可能使用同一个wakeupFd_来通信,而不会每个线程都创建自己的eventfd,除非代码中显式地在新线程中调用eventfd创建新的实例。 另外,引用[3]中的例子展示了一个进程内创建eventfd并进行读写操作,没有涉及多线程的情况,但原理相同。如果进程中的多个线程都调用eventfd创建新的实例,那么每个调用都会生成新的fd,从而增加进程的总句柄数。但线程的创建本身并不会自动导致eventfd的创建,除非在线程函数中有这样的代码。 总结:线程创建本身不会增加eventfd句柄数量,关键在于是否在新线程中显式调用eventfd()函数。如果没有显式创建,那么eventfd句柄数量不变;如果有,则每次调用都会新增句柄。进程内的所有线程共享相同的文件描述符表,因此eventfd句柄属于进程级别,而非线程级别。</think>在进程创建线程时,**eventfd类型的句柄数量不会自动增加**。具体分析如下: --- ### **关键结论** 1. **句柄归属层级** eventfd句柄属于**进程级别**,而非线程级别。所有线程共享进程的文件描述符表[^2][^4]。 2. **线程创建与句柄关系** - **默认行为**:创建线程不会自动生成新的eventfd句柄。若进程中已存在eventfd(例如主线程初始化时创建的`wakeupFd_`),所有线程均可直接复用该句柄[^1][^4]。 - **显式创建场景**:若在新线程中显式调用`eventfd()`创建新的实例,则会新增句柄(需注意资源管理)。 --- ### **原理分析** 1. **eventfd的工作机制** - eventfd通过系统调用`eventfd()`创建一个内核维护的64位计数器,并返回对应的文件描述符(fd)。该fd与其他文件描述符(如套接字、文件)统一由进程管理[^4]。 - 示例代码中的`wakeupFd_`在`EventLoop`构造函数中初始化,进程内所有线程共享此fd进行事件通知[^1]。 2. **线程资源共享特性** - Linux线程(轻量级进程)共享进程的地址空间、文件描述符表等资源。新线程不会复制父线程的fd表,而是直接访问同一份表[^2][^4]。 - 若多个线程操作同一eventfd,需注意同步问题(如`eventfd_read`和`eventfd_write`的原子性)[^3]。 --- ### **验证实验** 若在代码中创建线程**但不显式调用`eventfd()`**: ```cpp // 主线程创建eventfd int main() { int efd = eventfd(0, EFD_NONBLOCK); // 进程级句柄 std::thread t([&efd]() { eventfd_write(efd, 1); // 子线程直接使用主线程的efd }); t.join(); close(efd); // 所有线程均不可再使用此efd } ``` **结果**:整个进程仅有一个eventfd句柄,线程创建未导致数量增加。 --- ### **注意事项** - **显式创建场景**:若每个线程独立调用`eventfd()`,句柄数量会随调用次数增加,需及时`close()`避免泄漏。 - **多线程安全**:共享eventfd时,需确保读写操作的原子性(例如通过`eventfd_write`和`eventfd_read`的原子特性)[^3][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值