一、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. Seebelow.
查看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 = 5stimerfd_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)