cffi 函数调用挂起

cffi function call hangs

我想使用 Common Lisp 中的 stat(2)

我已经定义了 stat 函数使用的结构:

(cffi:defctype mode_t :unsigned-int)
(cffi:defctype ino_t :unsigned-int)
(cffi:defctype dev_t :int)
(cffi:defctype nlink_t :int)
(cffi:defctype uid_t :unsigned-int)
(cffi:defctype gid_t :unsigned-int)
(cffi:defctype off_t :int)
(cffi:defctype time_t :long)
(cffi:defctype blksize_t :unsigned-int)
(cffi:defctype blkcnt_t :int)

(cffi:defcstruct stat
  (st_dev dev_t)
  (st_ino ino_t)
  (st_mode mode_t)
  (st_nlink nlink_t)
  (st_uid uid_t)
  (st_gid gid_t)
  (st_rdev dev_t)
  (st_size off_t)
  (st_atime time_t)
  (st_mtime time_t)
  (st_ctime time_t)
  (st_blksize blksize_t)
  (st_blocks blkcnt_t))

函数本身:

(cffi:defcfun "stat" :int
  (path :string)
  (buf (:pointer (:struct stat))))

我试着这样称呼它:

(cffi:with-foreign-object (buf '(:pointer (:struct stat)))
  (stat "/home/florian/tmp/msg.txt" buf)
  (cffi:with-foreign-slots ((st_mode) buf (:struct stat))
    st_mode))

史莱姆只是挂起。没有错误,REPL 输入没有返回。

如果您查看 *inferior-lisp* 缓冲区,您会发现由于一些严重的内存损坏,SBCL 已进入其低级调试器。

struct stat 的具体布局在很大程度上取决于您拥有的体系结构。在我的 64 位 Ubuntu 14.04 上,以下内容似乎有 144 个字节的长度,而您的定义只有 52 个字节长 - 难怪尝试写入它会导致内存损坏。

尝试为操作系统可以自由定义的结构编写 defcstruct 形式可能是个坏主意。该代码可能 运行 在您的计算机上,但可能不会 运行 在其他人的系统上,如果他们使用不同的 OS 或处理器架构 - 但如果您真的需要它运行仅在您的系统上,最简单的方法可能是编写一个简短的 C 程序,将所有字段的大小和偏移量转储到屏幕上,然后从那里构建。

如果您需要在多个不同的系统上编写 运行 并且使用 stat 的代码,一个不错的选择是自己用 C 编写一个简单的代理模块,具有定义明确的,常量接口,代表您调用 stat。这是我在多个场合使用过的方法,在 C 端保证外来内容的安全,并且只通过 FFI 传递真正需要的数据。

对于更稳健的 CFFI 定义,还有 SWIG

@jlahd 的回答帮助我找到了正确的解决方案。

找到正确的结构后,我的调用代码还是错了。这是最终代码:

(cffi:with-foreign-object (buf '(:struct stat))
  (stat "/home/florian/tmp/msg.txt" buf)
  (cffi:with-foreign-slots ((st_mode) buf (:struct stat))
    st_mode))

请注意,对于 buf 外来类型,它使用 '(:struct stat) 而不是 '(:pointer (:struct stat))


我是如何得到正确的结构的?我想记录下来。

这是我使用的最终 cffi 结构:

(cffi:defcstruct (stat :size 144)
  (st_mode :unsigned-int :offset 24))

这是我用来找出 stat 结构及其成员的大小和偏移量的 C 程序:

#include <stddef.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[])
{
    struct stat foo;
    printf("stat sizeof: %zu\n", sizeof(struct stat));
    printf("st_mode sizeof: %zu\n", sizeof(foo.st_mode));
    printf("st_mode offset: %zu\n", offsetof(struct stat, st_mode));
}

在我的电脑上,这给了我这个:

$ gcc stat.c
$ ./a.out
stat sizeof: 144
st_mode sizeof: 4
st_mode offset: 24

然后我可以检查 :unsigned-int 的大小是否为 4 个字节:

CL-USER> (cffi:foreign-type-size :unsigned-int)
4

并检查我的 cffi 结构的大小:

CL-USER> (cffi:foreign-type-size '(:struct stat))
144

在 C.

中匹配 sizeof(struct stat)