跳到主内容

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_inost_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.

评论