ftok 陷阱
最近手上的项目出现了一个很严重的bug, 系统的message queue 资源被消耗完毕; 在创建 message queue 时提示已超出系统的资源限制(ENOSPC); 通过msgctl 查看系统上的 message queue 数目, 的确超出了系统的 限制(/proc/sys/kernel/msgmni).
System IPC (message queue, share memory 等等) 在创建时, 必须指定一个 KEY 值, 这个 KEY 来自于 ftok.
debug 发现, 调用ftok产生的 KEY, 每次都不一样.
查看fotk的 source code(glibc 2.20)
key_t ftok (const char *pathname, int proj_id) { struct stat64 st; key_t key; if (__xstat64 (_STAT_VER, pathname, &st) < 0) return (key_t) -1; key = ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16) | ((proj_id & 0xff) << 24)); return key; }
这样看, KEY 值与文件系统所在的 device id 和 文件节点号 有关, 如果 pathname 没有被删除, 则key值应该维持不变.
最终发现, pathname 在出错处理时被删除, 从而导致 KEY 值不固定.
实验发现, 如果 pathname 位于真实的文件系统, 比如EXT4, 删除后重新创建 pathname , st_ino 和 st_dev 不一定发生变化; 但如果 pathname 位于 ramfs 或者 tmpfs, 删除后重新创建, st_ino 一定会改变. 所以, pathname 所在的文件系统可能会影响 KEY 的产生. (笔者所在的项目中, pathname 位于 ramfs)
另外, 在 glibc 2.20 的 ftok 实现中, 只是使用了inode number 的低 16 位, 如果文件系统中包含大量的文件, 就会存在两个文件的 inode_number 低 16 位 相同; 此时, 这两个文件对应的 KEY 值 就会相同, 违背了 不同的文件产生不同的 KEY 的原则.
Linux programmer's Manual 是这样说的:
Of course no guarantee can be given that the resulting key_t is unique. Typically, a best effort attempt combines the given proj_id byte, the lower 16 bits of the inode number, and the lower 8 bits of the device number into a 32-bit result. Collisions may easily happen, for example between files on /dev/hda1 and files on /dev/sda1.