Common lisp cffi函数调用挂起

Common lisp cffi函数调用挂起,common-lisp,sbcl,cffi,Common Lisp,Sbcl,Cffi,我想使用通用Lisp 我已经定义了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_

我想使用通用Lisp

我已经定义了
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输入不会返回。

如果查看
*低级lisp*
缓冲区,您将看到由于严重的内存损坏,SBCL已落入其低级调试器

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

尝试为操作系统可以任意定义的结构编写
defcstruct
表单可能不是一个好主意。代码可能会在您的计算机上运行,但如果其他人使用不同的操作系统或处理器体系结构,则可能不会在其他人的系统上运行-但如果您确实需要仅在您的系统上运行,最简单的方法可能是编写一个简短的C程序,转储屏幕上所有字段的大小和偏移量,然后从那里开始建设

如果您需要编写在多个不同系统上运行并使用
stat
的代码,一个好的选择是自己用C编写一个简单的代理模块,该模块具有定义良好的常量接口,可以代表您调用
stat
。这是我在好几次使用的方法,在C端保证外国资料的安全,只通过FFI传递真正需要的数据


对于更健壮的CFFI定义,还有。

@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))
请注意,它使用
'(:struct stat)
而不是
'(:pointer(:struct stat))
作为
buf
外部类型


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

以下是我使用的最终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));
}
然后我可以检查
: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)
匹配。

事实上,我的低级lisp被放入了ldb。谢谢你的信息!你有关于如何用C写代理模块的资源/文档吗?谷歌似乎让我失望了。顺便说一句,你知道如何调试这些问题吗?我使用了
offsetof()
cffi:foreign-type-size
来正确定义我的结构,但仍然存在问题。(尝试两种方法)以下是相关代码:我似乎已经解决了这个问题。我需要
(cffi:withforeign object(buf'(:struct stat))
而不是
(:pointer(:struct stat))
虽然我已经接受了你的答案,但我认为记录我的思考过程到最终结果是一个好主意。如果你对我的答案有任何反馈,我很高兴:)很高兴您可以从系统头中删除结构定义。参见cffi手册中的govelling。查看cffi grovel(包括在cffi中)。简言之,它可以自动编写和执行与您编写的程序类似的程序。@LuisOliveira我做了,但无法理解文档:-(
CL-USER> (cffi:foreign-type-size '(:struct stat))
144