深入理解 ftok:从源码手写一个 IPC key 生成函数
很多人用
ftok生成 System V IPC 的 key,但很少有人真正去看它的实现。今天我们从零手写一个,彻底搞懂它的原理。
一、ftok 是干什么的?
在 System V IPC(消息队列、共享内存、信号量)中,所有操作都需要一个key_t类型的 key 来标识资源。
ftok的作用就是:根据一个已存在的文件路径 + 一个项目ID,生成一个唯一的 key_t。
key_t key = ftok("/tmp/myfile", 65);这样不同进程只要用相同的路径和ID,就能拿到同一个 key,从而访问同一个 IPC 资源。
二、标准库的 ftok 原型
#include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id);pathname:必须是一个已存在的文件路径proj_id:项目标识符,通常取 0~255- 返回:成功返回 key_t,失败返回 -1
三、手写实现(核心源码)
下面这个实现和 glibc 的逻辑基本一致,非常适合理解原理:
#include <sys/ipc.h> #include <sys/stat.h> key_t ftok(const char *path, int id) { struct stat st; if (stat(path, &st) < 0) return -1; return ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16) | ((id & 0xffu) << 24)); }四、逐行拆解
| 步骤 | 代码 | 含义 |
|---|---|---|
| 1 | stat(path, &st) | 获取文件的 inode 和设备号 |
| 2 | st.st_ino & 0xffff | 取 inode 的低 16 位(文件唯一标识) |
| 3 | (st.st_dev & 0xff) << 16 | 取设备号低 8 位,放到第 17~24 位 |
| 4 | (id & 0xffu) << 24 | 取项目ID低 8 位,放到最高 8 位 |
| 5 | 三者按位或 | 组合成一个 32 位的 key_t |
最终 key 的 bit 布局:
| 高8位(proj_id) | 8位(dev) | 16位(ino) | | bit 31~24 | bit 23~16 | bit 15~0 |五、为什么这样设计?
1. 用 inode 保证唯一性
同一个文件系统中,inode 是唯一的。所以只要路径相同,st_ino就相同。
2. 用 st_dev 区分不同文件系统
不同挂载点可能有相同的 inode 号,加上设备号就能区分。
3. 用 proj_id 扩展空间
同一个文件可以生成多个不同的 key,靠的就是proj_id。比如:
key_t key1 = ftok("/tmp/ipcfile", 1); // key for 消息队列 key_t key2 = ftok("/tmp/ipcfile", 2); // key for 共享内存同一个文件,不同 ID,不同 key,互不干扰。
六、完整使用示例
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/stat.h> key_t ftok(const char *path, int id) { struct stat st; if (stat(path, &st) < 0) return -1; return ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16) | ((id & 0xffu) << 24)); } int main() { // 先创建一个文件(ftok 要求文件必须存在) system("touch /tmp/my_ipc_file"); key_t key = ftok("/tmp/my_ipc_file", 65); if (key == -1) { perror("ftok"); return 1; } printf("Generated key: 0x%x (%d)\n", key, key); // 用这个 key 创建共享内存 int shmid = shmget(key, 4096, IPC_CREAT | 0666); if (shmid < 0) { perror("shmget"); return 1; } printf("Shared memory id: %d\n", shmid); return 0; }编译运行:
gcc -o ftok_demo ftok_demo.c ./ftok_demo # Generated key: 0x4101d1a2 (1090636194) # Shared memory id: 65536七、踩坑清单
| 坑 | 说明 |
|---|---|
| ❌ 文件不存在 | stat失败直接返回 -1,一定要先创建文件 |
| ❌ 路径被删除后重建 | inode 变了,key 也变了,旧的 IPC 资源将无法访问 |
| ❌ 多个文件系统 inode 相同 | 所以实现里加了st_dev来区分 |
| ⚠️ key 不是全局唯一 | 它只是"大概率唯一",真正的唯一性靠IPC_EXCL标志保证 |
八、一句话总结
ftok 的本质 = inode(16bit) + 设备号(8bit) + 项目ID(8bit),用一个已存在文件的"身份信息"生成一个 IPC key。
理解了这个,System V IPC 的 key 机制就再也没有秘密了。
觉得有用的话点个赞👍,有问题评论区见~
参考:glibc sysdeps/posix/ftok.c
