eventfd和timerfd学习笔记

一、eventfd

1.1简介:

eventfd是进程间通信(IPC)的一种手段,主要用来做事件通知。IPC通常方式:

1)unix socket

2)pipe

3)共享内存

前两种都是同步调用,但是传递大量数据时存在效率问题,而使用共享内存效率高,但是缺乏同步通知机制,消费者轮询共享内存修改开销太大。这里可以使用eventfd用来做同步。

1.2 使用

eventfd每次只能发送8个字节的内容(flag),一般用于每个bit表示一个消息类型。

  • API

1)创建文件句柄:

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

2)消息发送

// 写 eventfd,内部 buffer 必须是 8 字节大小;
n = write(efd, &u, sizeof(uint64_t));

3)消息接收

// 读 eventfd
n = read(efd, &u, sizeof(uint64_t));

写数据时会累加flag,读数据后flag清零。所以这里发送消息时可以同时发送多个消息。

实例:

// 写 3 次,发3次消息
write(efd, &u /* u = 0x01 */ , 8)
write(efd, &u /* u = 0x02 */ , 8)
write(efd, &u /* u = 0x04 */ , 8)

读数据时,会读到累加的数据

read(ebd, &x, 8) /*读到x = 0x7*/
  • 使用场景2

eventfd可以用于进程间信号量的作用,生产者每次写入1,消费者每次取出1然后执行。不过需要打开选项:

EFD_SEMAPHORE (since Linux 2.6.30)
      Provide  semaphore-like semantics for reads from the new file descriptor.  See
      below.

查看fd信息

zfj@ubuntu22:/proc/5686/fd$ ls -l
total 0
lrwx------ 1 zfj zfj 64  5月 19 21:37 0 -> /dev/pts/1
lrwx------ 1 zfj zfj 64  5月 19 21:37 1 -> /dev/pts/1
lrwx------ 1 zfj zfj 64  5月 19 21:37 2 -> /dev/pts/1
lrwx------ 1 zfj zfj 64  5月 19 21:37 3 -> 'anon_inode:[eventfd]'

eventfd是匿名fd,指没有对应具体文件。

  • 实战

eventfd支持poll机制,可使用poll和select接口监听。

#include <malloc.h>
#include <pthread.h>
#include <zconf.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <wait.h>
#include <sys/eventfd.h>

#define handle_error(msg)   \
    do { perror(msg); exit(EXIT_FAILURE);  } while(0)
int main(int argc, char** argv)
{
    int  efd;

    ssize_t rc;
    efd = eventfd(0, 0);
    if(-1 == efd){
        handle_error("eventfd");
    }
    printf("efd=%d\n", efd);
    int ret = fork();
    uint64_t buf;

    if (ret == 0)
    {
    /*消费者*/
    printf("wait for read:\n");
    rc = read(efd, &buf, sizeof(uint64_t));
    printf("read finished: %zd\n", rc);
    printf("Parent read %llu from efd\n",(unsigned long long)buf);
    return 0;
   }
   else
  {
    /*生产者*/
    buf = 0x5;
    rc = write(efd, &buf, sizeof(uint64_t));
    printf("after write\n");	 
  }
}

问题:

event没有做标识?所有进程都只能用同一组?系统中只能存在一组eventfd?

static const struct file_operations eventfd_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = eventfd_show_fdinfo,
#endif
.release = eventfd_release,
.poll = eventfd_poll,
.read_iter = eventfd_read,
.write = eventfd_write,
.llseek = noop_llseek,
};

eventfd没有open函数,只有一个eventfd的系统调用。

所以仅限于fork出来的进程,只能用于主子进程间。

1.3 内核实现

《fs/eventfd.c》

1.4 参考资料

Linux fd 系列 — eventfd 是什么? - 知乎 (zhihu.com)

二、timerfd

2.1 简介

timerfd可用于用户态的定时器,定时操作。

2.2 使用

  • API
// 创建一个 timerfd 句柄
/*clockid
* CLOCK_MONOTONIC:开机到现在的时间,不可设置的时间
* CLOCK_REALTIME:实时时间,到时间点执行,可重设时间点
* 
*/
int timerfd_create(int clockid, int flags);
// 启动或关闭 timerfd 对应的定时器
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
// 获取指定 timerfd 距离下一次超时还剩的时间
int timerfd_gettime(int fd, struct itimerspec *curr_value);

数据结构

struct timespec64 {
	time64_t	tv_sec;			/* seconds */
	long		tv_nsec;		/* nanoseconds */
};

struct itimerspec64 {
    /*开始执行后,每次间隔多久执行一次*/
	struct timespec64 it_interval;
	/*settime后多久开始执行*/
	struct timespec64 it_value;
};
  • 实战

timerfd 绑定的是匿名 inode,eventfd也是匿名inode

#include <stdio.h>
#include <sys/timerfd.h>
#include <poll.h>
#include <unistd.h>
#include <assert.h>

int main() {
    struct itimerspec timebuf;
    int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
   /*开始运行后每1s执行一次*/
    timebuf.it_interval.tv_sec = 1;
    timebuf.it_interval.tv_nsec = 0;	  // period timeout value = 1s
     
    /*第一次执行设置后5s开始执行*/
    timebuf.it_value.tv_sec = 5;
     timebuf.it_value.tv_nsec = 0;  // initial timeout value = 5s
    timerfd_settime(timerfd, 0, &timebuf, NULL);

    struct pollfd fds[1];
    int len = sizeof(fds) / sizeof(fds[0]);
    fds[0].fd = timerfd;
    fds[0].events = POLLIN | POLLERR | POLLHUP;

    while (1)
    {
        int n = poll(fds, len, -1);
        for (int i = 0; i < len && n-- > 0; ++i) {
            if (fds[i].revents & POLLIN)
            {
                unsigned long long val;
                int ret = read(timerfd, &val, sizeof(val));
                if (ret != sizeof(val)) // ret should be 8
                {
                    printf("ret bytes instead of 8 frome timerfd\n");
                    break;
                }
                printf("timerfd = %d timeout\n", timerfd);
            }
        }
    }
    close(timerfd);
    return 0;
}

2.3 内核实现

static const struct file_operations timerfd_fops = { 
    .release    = timerfd_release,
    .poll       = timerfd_poll,
    .read       = timerfd_read,
    .show_fdinfo    = timerfd_show,
    // ...
};

定时器的poll操作用于定时,读操作获取下一次超时剩余时间。

《fs/timerfd.c》

2.4 参考资料

Linux fd 系列 — 定时器 timerfd 是什么? - 知乎 (zhihu.com)

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值