使用 Pythons ctypes 从 libc 调用 uname

Posted

技术标签:

【中文标题】使用 Pythons ctypes 从 libc 调用 uname【英文标题】:Calling uname from libc with Pythons ctypes 【发布时间】:2011-09-07 10:31:53 【问题描述】:

tl;博士

这适用于 GNU 版本的 libc(尚未尝试使用 uclibc)

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char * 65),
                 ('nodename', c_char * 65),
                 ('release', c_char * 65),
                 ('version', c_char * 65),
                 ('machine', c_char * 65),
                 ('domain', c_char * 65) ]

gnar = uts_struct()

libc.uname(byref(gnar))

print gnar.nodename

原帖

以下代码段错误;我不确定我做错了什么。

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char_p),
                 ('nodename', c_char_p),
                 ('release', c_char_p),
                 ('version', c_char_p),
                 ('machine', c_char_p) ]

utsname = uts_struct()
libc.uname(byref(utsname))

print utsname.sysname

这做同样的事情:

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char_p),
                 ('nodename', c_char_p),
                 ('release', c_char_p),
                 ('version', c_char_p),
                 ('machine', c_char_p) ]

utsname = uts_struct()
utsname_pointer = pointer(utsname)
libc.uname(utsname_pointer)

print utsname.sysname

我一定是搞砸了一些基本的东西......

(我知道os.uname(),这只是一个理解练习,我失败了)

我在这里引用了 uname 手册:http://www.cl.cam.ac.uk/cgi-bin/manpage?2+uname

我做错了什么?


编辑:

感谢Nemo我能够得到数据;

>>> from ctypes import *
>>> libc = CDLL('libc.so.6')
>>> gnar = create_string_buffer(512)
>>> libc.uname(byref(gnar))
0
>>> print gnar.value
Linux
>>> 

但是,我假设我只得到“Linux”,因为这些项目是 NULL 分隔的,调节器字符串也是如此。有什么方法可以阅读NULL吗?


编辑2:

根据 Nemos 的评论,我已经尝试过 - 这不起作用,但我认为这可能是朝着正确方向迈出的一步......错误:

Traceback (most recent call last):
  File "gnar.py", line 18, in <module>
    utsname = uts_struct(gnar)
TypeError: incompatible types, c_char_Array_512 instance instead of c_char_p instance

这只是不可行的吗?

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char_p),
                 ('nodename', c_char_p),
                 ('release', c_char_p),
                 ('version', c_char_p),
                 ('machine', c_char_p) ]

gnar = create_string_buffer(512)
libc.uname(byref(gnar))
utsname = uts_struct(gnar)

Edit3:(我要写有史以来最长的帖子……=P)

from ctypes import *
libc = CDLL('libc.so.6')
class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char * 65),
                 ('nodename', c_char * 65),
                 ('release', c_char * 65),
                 ('version', c_char * 65),
                 ('machine', c_char * 65) ]
gnar = uts_struct()
libc.uname(byref(gnar))
print gnar.machine

这可行,但是,它在打印值后会出现段错误...


最终编辑:

以下作品-我当然使用的是 GNU 版本的 libc。 (我在 Ubuntu 机器上)所以添加域的字段就是停止段错误所需​​要的。事后看来是有道理的。 :)

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char * 65),
                 ('nodename', c_char * 65),
                 ('release', c_char * 65),
                 ('version', c_char * 65),
                 ('machine', c_char * 65),
                 ('domain', c_char * 65) ]

gnar = uts_struct()
libc.uname(byref(gnar))
print gnar.nodename

【问题讨论】:

【参考方案1】:

根据this uname man page,该结构包含实现定义大小的数组,而不是 char* (c_char_p)。你看过sys/utsname.h中的结构定义吗?您必须匹配确切的结构定义。数据类型可能应该是c_char * n,其中n 是来自sys/utsname.h 中的该字段的数组大小。

或者,您应该能够使用print gnar.raw 访问您的第一个编辑 中的所有字符串,只要缓冲区足够大以容纳整个结构。

【讨论】:

.raw 确实返回了整个缓冲区!谢谢! (不知道我是如何设法错过raw 属性的。我将修改该结构,看看它是否可用;由我。:p 我在帖子中添加了另一个编辑 - 根据您的建议,它似乎有效,但打印后出现了段错误......这是我最近的一次!【参考方案2】:

utsname structure 中的字段不是指针;它们是“未指定大小的数组”。

因此字符串在结构中被背靠背打包并以空值结尾。

我不知道如何用 Python 来表示它。但我建议您从 uname() 以外的其他东西开始进行实验。 :-)

[更新]

517366245708 十进制为 0x78756E694C,即 "xuniL" 的 ascii。这在 64 位 little-endian 机器上实际上是有意义的......

但问题是您正在使用 c_char_p 创建一个指针,然后在调用 byref() 时将一个指针传递给 that。所以 uname() 正在填写您的指针(而不是它指向的内容)。更糟糕的是,它在其中放置了超过 8 个字节,因此您当前的代码正在破坏内存。

你需要弄清楚如何分配一个struct utsname大小的内存块,然后将一个指向that的指针传递给uname()函数,然后找出里面的偏移量该块对应于结构中的哪些字段。我不确定 Python 能做到多少……

[第二次更新]

这样更好...但是现在您必须查看数组中的偏移量。如果这是一个典型的 Linux 系统,每个字段是 65 个字节(是的,真的)。因此,您需要开始将 65 个字节读入字符串。这个东西不知道你能不能打电话string.index...

【讨论】:

不用担心。大多数 C 接口都没有这么奇怪。但是每当其中一个需要返回可变大小的东西时,它可能会期望您分配空间来容纳它。 (作为一名 Python 程序员,手工管理内存是你在 C 语言中会遇到的最烦人的事情。) @Nemo,我更新了问题,我能够获取数据,但现在需要将其转换为字符串。我不认为我可以使用restype,因为该功能不是return,我能做什么? @Nemo 好吧-我认为create_string_buffer 会分配内存?我再次编辑了问题。 @Nemo 每个字段 65 个字节?这是否意味着对于预期的 5 个项目,我需要创建一个 330 字节的缓冲区? 嗯,在我的系统上它是 390...所以我猜这里的结构字段之间有一些填充。说真的,uname() 很可能是这里要试验的最糟糕的 C 函数 :-)

以上是关于使用 Pythons ctypes 从 libc 调用 uname的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 libc 上使用 ctypes 调用 environ 时,为啥 Python 会出现段错误?

使用 ctypes/cffi 解决循环共享对象依赖关系

从 c++ 代码运行 python 脚本并在 c++ 中使用 pythons 输出

从 Python 访问 errno?

FFMPEG 和 Pythons 子进程

使用 Pythons imaplib 搜索之前/之后