服务器设计细节 之 【eventfd、struct stat、stat接口】
创建一个 eventfd 对象,返回一个指向它的文件描述符。struct stat 是 POSIX 标准中用于。中的 8 字节无符号整数值加到内核计数器上。文件路径(绝对或相对,支持符号链接跟随)释放 eventfd 对象及其内核资源。,定义在 <sys/stat.h>用于存储文件元信息的结构体指针。通过文件描述符获取信息,判断文件是否为符号链接。(必须是 8 字节),判断文件类型和权限。的信息,
·
目录
1.eventfd
- eventfd 是 Linux 提供的一种轻量级事件通知机制,本质上是一个内核维护的 64 位无符号整数计数器,通过文件描述符来操作
- 它的核心用途是在线程间或进程间实现信号传递,典型场景是 epoll 事件驱动架构。相比于管道(pipe),它更轻量,无需管理缓冲区,开销更低
1.1.核心接口
- eventfd() 创建接口
| 项目 | 详细说明 |
|---|---|
| 函数原型 | int eventfd(unsigned int initval, int flags); |
| 所属头文件 | <sys/eventfd.h> |
| 作用 | 创建一个 eventfd 对象,返回一个指向它的文件描述符 |
参数 initval |
计数器的初始值(uint64_t 类型,范围 0 ~ 0xfffffffffffffffe) |
参数 flags |
行为控制标志,按位或组合(详见下方 flags 表) |
| 成功返回值 | 新的文件描述符(非负整数) |
| 失败返回值 | -1,并设置 errno |
可能 errno |
EINVAL:flags 无效EMFILE:进程 fd 数达上限ENFILE:系统 fd 数达上限ENOMEM:内核内存不足 |
- eventfd() flags 标志详解表
| 标志 | 值 | 作用 |
|---|---|---|
0 |
0 | 默认模式:读写均可能阻塞,read() 读后清零 |
EFD_CLOEXEC |
02000000 (八进制) |
exec() 时自动关闭该 fd,防止子进程继承 |
EFD_NONBLOCK |
04000 (八进制) |
非阻塞模式:计数器为 0 时 read() 返回 EAGAIN;即将溢出时 write() 返回 EAGAIN |
EFD_SEMAPHORE |
1 |
信号量模式:read() 每次减 1 并返回 1,支持多消费者场景 |
| 组合 | 场景 |
|---|---|
eventfd(0, 0) |
线程间同步,阻塞等待事件 |
eventfd(0, EFD_NONBLOCK) |
在轮询循环中使用,不阻塞线程 |
eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK) |
与 epoll 结合,安全且高效 |
eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK) |
多个消费者竞争同一个事件源,非阻塞 |
- write() 写入接口
| 项目 | 详细说明 |
|---|---|
| 函数原型 | ssize_t write(int fd, const void *buf, size_t count); |
| 所属头文件 | <unistd.h> |
| 对 eventfd 的作用 | 将 buf 中的 8 字节无符号整数值加到内核计数器上 |
参数 fd |
eventfd 的文件描述符 |
参数 buf |
指向一个 uint64_t 值的指针(必须是 8 字节) |
参数 count |
写入字节数,必须为 8 |
| 写入规则 | 1. 写入值被加到计数器 2. 如果结果超过 0xfffffffffffffffe,调用阻塞(除非设了 EFD_NONBLOCK)3. 写入值为 0 是合法的,但不改变计数器,用于唤醒阻塞的 read() |
| 成功返回值 | 8(永远完整写入,不存在部分写入) |
| 失败返回值 | -1,并设置 errno |
可能 errno |
EAGAIN:非阻塞模式下,写入会导致计数器溢出EINVAL:count 不等于 8 或 buf 值非法EBADF:fd 无效EINTR:被信号中断 |
- read() 读取接口
| 项目 | 详细说明 |
|---|---|
| 函数原型 | ssize_t read(int fd, void *buf, size_t count); |
| 所属头文件 | <unistd.h> |
| 对 eventfd 的作用 | 读取计数器的值,并根据模式清零或减 1 |
参数 fd |
eventfd 的文件描述符 |
参数 buf |
指向一个 uint64_t 变量的缓冲区(至少 8 字节) |
参数 count |
缓冲区大小,必须 ≥ 8 |
| 默认模式读取规则 | 1. 计数器 > 0:返回当前计数,原子清零 2. 计数器 = 0:阻塞,直到有 write() 写入值3. 非阻塞:返回 EAGAIN |
EFD_SEMAPHORE 模式 |
1. 计数器 > 0:返回固定值 1,原子减 12. 计数器 = 0:阻塞(非阻塞返回 EAGAIN)3. 相当于每次消耗一个"事件资源" |
| 成功返回值 | 8(读取到的 8 字节值填入 buf) |
| 失败返回值 | -1,并设置 errno |
可能 errno |
EAGAIN:非阻塞模式下计数器为零EINVAL:count < 8EBADF:fd 无效EINTR:被信号中断 |
- close() 关闭接口
| 项目 | 详细说明 |
|---|---|
| 函数原型 | int close(int fd); |
| 所属头文件 | <unistd.h> |
| 作用 | 释放 eventfd 对象及其内核资源 |
参数 fd |
eventfd 的文件描述符 |
| 成功返回值 | 0 |
| 失败返回值 | -1,并设置 errno |
可能 errno |
EBADF:fd 无效 |
| 注意事项 | 如果多个进程/线程共享同一个 fd(如通过 fork 或 fd 传递),只有在所有引用该对象的 fd 都关闭后,内核计数器才真正释放 |
2.struct stat
struct stat 是 POSIX 标准中用于存储文件元信息的结构体,定义在 <sys/stat.h>
struct stat {
dev_t st_dev; // 文件所在设备的 ID
ino_t st_ino; // inode 节点号
mode_t st_mode; // 文件类型和权限
nlink_t st_nlink; // 硬链接数
uid_t st_uid; // 所有者用户 ID
gid_t st_gid; // 所属组 ID
dev_t st_rdev; // 设备文件对应的设备 ID(非设备文件无意义)
off_t st_size; // 文件大小(字节),普通文件才有意义
blksize_t st_blksize; // 文件系统 I/O 最优块大小
blkcnt_t st_blocks; // 文件占用的 512B 块数量
time_t st_atime; // 最后访问时间(access)
time_t st_mtime; // 最后修改时间(modify)
time_t st_ctime; // 最后状态改变时间(change)
};
2.1 核心字段说明
| 字段 | 类型 | 含义 | 使用场景 |
|---|---|---|---|
st_mode |
mode_t |
文件类型 + 权限 | 最常用,判断文件类型和权限 |
st_size |
off_t |
文件大小 | 获取文件大小,分配缓冲区 |
st_mtime |
time_t |
最后修改时间 | HTTP 的 Last-Modified 头、缓存判断 |
st_ino |
ino_t |
inode 号 | 判断两个路径是否指向同一文件 |
st_nlink |
nlink_t |
硬链接数 | 判断文件是否有其他名字(为 0 时文件真正删除) |
st_uid/st_gid |
uid_t/gid_t |
所有者/组 | 权限检查 |
2.2 st_mode 详解
st_mode 是一个位掩码,包含两部分信息:文件类型(高 4 位)访问权限(低 12 位)
文件类型判断宏
| 宏 | 含义 | 示例 |
|---|---|---|
S_ISREG(m) |
普通文件 | .txt, .html, 可执行文件 |
S_ISDIR(m) |
目录 | /home, /tmp |
S_ISLNK(m) |
符号链接 | 软链接文件 |
S_ISCHR(m) |
字符设备 | /dev/tty, /dev/null |
S_ISBLK(m) |
块设备 | /dev/sda, /dev/sdb |
S_ISFIFO(m) |
命名管道 | FIFO 文件 |
S_ISSOCK(m) |
套接字 | socket 文件 |
权限判断宏
st.st_mode & S_XXX
| 宏 | 含义 | 八进制值 |
|---|---|---|
S_IRUSR |
所有者读 | 0400 |
S_IWUSR |
所有者写 | 0200 |
S_IXUSR |
所有者执行 | 0100 |
S_IRGRP |
组读 | 0040 |
S_IWGRP |
组写 | 0020 |
S_IXGRP |
组执行 | 0010 |
S_IROTH |
其他人读 | 0004 |
S_IWOTH |
其他人写 | 0002 |
S_IXOTH |
其他人执行 | 0001 |
2.3 stat 、lstat、fstat函数
#include <sys/stat.h>
int stat(const char *pathname, struct stat *statbuf);
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
pathname |
const char * |
输入 | 文件路径(绝对或相对,支持符号链接跟随) |
statbuf |
struct stat * |
输出 | 用于存储文件元信息的结构体指针 |
| 返回值 | 含义 | 后续操作 |
|---|---|---|
0 |
成功,statbuf 已填充 |
读取 statbuf 中各字段 |
-1 |
失败 | 检查 errno 获取原因 |
| errno | 宏常量 | 含义 |
2 |
ENOENT |
文件不存在 |
13 |
EACCES |
路径中某个目录无搜索权限 |
20 |
ENOTDIR |
路径中某个组件不是目录 |
36 |
ENAMETOOLONG |
路径名太长 |
40 |
ELOOP |
符号链接循环引用 |
14 |
EFAULT |
pathname 或 statbuf 指针无效 |
12 |
ENOMEM |
内核内存不足 |
116 |
ESTALE |
NFS 文件句柄过期 |
#include <sys/stat.h>
int lstat(const char *pathname, struct stat *statbuf);
| 项目 | 说明 |
|---|---|
| 参数 | 同 stat:pathname(输入路径)、statbuf(输出信息) |
| 返回值 | 同 stat:成功返回 0,失败返回 -1 并设置 errno |
| errno | 同 stat:ENOENT、EACCES、ENOTDIR、ELOOP 等 |
| 核心区别 | 若 pathname 是符号链接,返回符号链接自身的信息,而非目标文件 |
#include <sys/stat.h>
int fstat(int fd, struct stat *statbuf);
| 项目 | 说明 |
|---|---|
| 参数 | fd:已打开的文件描述符(输入);statbuf:输出信息 |
| 返回值 | 成功返回 0,失败返回 -1 并设置 errno |
| errno | EBADF(9):fd 无效;EFAULT(14):statbuf 指针无效;ENOMEM(12):内存不足 |
| 核心区别 | 通过文件描述符获取信息,无需文件名,适用于已打开的文件 |
三者对比
stat |
lstat |
fstat |
|
|---|---|---|---|
| 输入 | 路径字符串 | 路径字符串 | 文件描述符 |
| 符号链接 | 跟随,返回目标信息 | 不跟随,返回链接自身 | 跟随(文件已打开) |
| 典型场景 | 查询任意文件信息 | 判断文件是否为符号链接 | 已 open() 后查询 |
| 头文件 | <sys/stat.h> |
<sys/stat.h> |
<sys/stat.h> |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)