fcntl.ioctl() 的 64 位参数

Posted

技术标签:

【中文标题】fcntl.ioctl() 的 64 位参数【英文标题】:64-bit argument for fcntl.ioctl() 【发布时间】:2013-06-20 09:54:31 【问题描述】:

在我的 Python (2.7.3) 代码中,我尝试使用 ioctl 调用,接受 long int(64 位)作为参数。我在 64 位系统上,所以 64 位 int 与指针大小相同。

我的问题是 Python 似乎不接受 64 位 int 作为 fcntl.ioctl() 调用的参数。 它很乐意接受 32 位 int 或 64-位指针 - 但我需要传递一个 64 位整数。

这是我的 ioctl 处理程序:

static long trivial_driver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

    long err = 0;

    switch (cmd)
    
        case 1234:
            printk("=== (%u) Driver got arg %lx; arg<<32 is %lx\n", cmd, arg, arg<<32);
            break;
        case 5678:
            printk("=== (%u) Driver got arg %lx\n", cmd, arg);
            break;
        default:
            printk("=== OH NOES!!! %u %lu\n", cmd, arg);
            err = -EINVAL;
    

    return err;

在现有的 C 代码中,我使用如下调用:

static int trivial_ioctl_test()
    int ret;
    int fd = open(DEV_NAME, O_RDWR);

    unsigned long arg = 0xffff;

    ret = ioctl(fd, 1234, arg); // ===(1234) Driver got arg ffff; arg<<32 is ffff00000000
    arg = arg<<32;
    ret = ioctl(fd, 5678, arg); // === (5678) Driver got arg ffff00000000
    close(fd);


在python中,我打开设备文件,然后我得到以下结果:

>>> from fcntl import ioctl
>>> import os
>>> fd = os.open (DEV_NAME, os.O_RDWR, 0666)
>>> ioctl(fd, 1234, 0xffff)
0
>>> arg = 0xffff<<32
>>> # Kernel log: === (1234) Driver got arg ffff; arg<<32 is ffff00000000
>>> # This demonstrates that ioctl() happily accepts a 32-bit int as an argument.
>>> import struct
>>> ioctl(fd, 5678, struct.pack("L",arg))
'\x00\x00\x00\x00\xff\xff\x00\x00'
>>> # Kernel log: === (5678) Driver got arg 7fff9eb1fcb0
>>> # This demonstrates that ioctl() happily accepts a 64-bit pointer as an argument.
>>> ioctl(fd, 5678, arg)

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ioctl(fd, 5678, arg)
OverflowError: signed integer is greater than maximum
>>> # Kernel log: (no change - OverflowError is within python)
>>> # Oh no! Can't pass a 64-bit int!
>>> 

Python 有什么方法可以将我的 64 位参数传递给 ioctl()?

【问题讨论】:

如果可能的话,将有助于提供一个可重现的例子。鉴于 ioctl() 调用是特定于设备的,将 IOC_GET_VAL 替换为您正在使用的实际请求代码会使测试变得困难。 @Aya:感谢您的评论。我是设备驱动程序的新手,在构建一个微不足道但功能强大的示例时遇到了一些麻烦。但我会看看我能做什么。 :) 同时,我发布了一个基于ctypes 的解决方案。 好的,我有时间发布一个更清晰的例子!我现在对答案感到困惑...... 我找到了一种更简单的测试方法,使用strace(1),并且在重新检查了Python的源代码后,使用fcntl模块可能是不可能的。另请参阅我的更新答案。 【参考方案1】:

这是否可以使用 Python 的 fcntl.ioctl() 将取决于系统。追溯源代码,错误信息来自line 658 of getargs.c上的以下测试...

else if (ival > INT_MAX) 
    PyErr_SetString(PyExc_OverflowError,
    "signed integer is greater than maximum");
    RETURN_ERR_OCCURRED;

...在我的系统上,/usr/include/limits.h 告诉我...

#  define INT_MAX   2147483647

...(大概)是(2 ** ((sizeof(int) * 8) - 1)) - 1

因此,除非您在 sizeof(int) 至少为 8 的系统上工作,否则您必须使用 ctypes 模块直接调用底层 C 函数,但它是特定于平台的。

假设 Linux,这样的东西应该可以工作......

from ctypes import *

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

fd = os.open (DEV_NAME, os.O_RDWR, 0666)
value = c_uint64(0xffff<<32)
libc.ioctl(fd, 5678, value)

【讨论】:

是的!这行得通 :) 我很惊讶 fcntl.ioctl() 中没有内置支持,但这是一个完全合理的解决方法。 参见。 ***.com/questions/9257065/…. 很好的答案。非常感谢:D @Ziv 是的。我的印象是(出于某种原因)int 应该与 CPU 寄存器的大小相同,但事实并非如此。【参考方案2】:

Python 的 ioctl 中的“arg”表示法与 C 中的不同。

在 python 中(再次根据1),它可以是 python 整数(不指定 32 位或 64 位),也可以是某种缓冲区对象(如字符串)。你在 Python 中并没有真正的“指针”(所以所有底层架构细节——比如 32 位或 64 位地址都是完全隐藏的)。

如果我理解正确,您实际上需要的是 SET_VALstruct.pack(your 64-bit integer) 首先进入字符串并将此字符串传递给 ioctl,而不是直接传递整数。

像这样:

struct.pack('&gt;Q',1&lt;&lt;32)

对于GET_VAL,您需要再次使用“Q”类型(而不是“L”)才能正确解压缩 64 位整数值。

【讨论】:

对不起,这不正确。 (对不起,如果我的原始帖子不够清楚。)据我所知,any 使用 struct.pack() 将(在 c 驱动程序内)转换为指向正在发送的二进制数据的指针到 c 的 ioctl 参数。我不想要指针;我想直接控制正在传递的64位值。 但严格来说,您不能真正通过 32 位参数(无论是指针还是“值”)传递任何 64 位值。 ioctl 调用中的参数类型将是 32 位架构中的 32 位。在 64 位系统上,一切都变成了 64 位,因此理论上您可以将 64 位值传递给 IOCTLin C* 另一方面,Python 试图隐藏任何这些平台细节,因此它不会公开任何指针 API。想法是你永远不要关心建筑是什么。事实上,Python 的整数可以是任意大的(即不受 64 位或其他限制......)所以要保持 Pythonic (并且独立于平台),即使是小事情也可能更好地传递缓冲区对象像一个 64 位整数... 是的,但是不是这个特定的现有ioctl 调用的实现方式。 ioctl 的参数可以被视为指针 为整数;这就是为什么 python 提供了以整数开头的选项。

以上是关于fcntl.ioctl() 的 64 位参数的主要内容,如果未能解决你的问题,请参考以下文章

如何正确地将 C ioctl 调用转换为 python fcntl.ioctl 调用?

从 32 位 C# 到 64 位 C++ 的 PostMessage 参数

Jarvis OJ- [XMAN]level2/3_x64-Writeup——64位简单栈溢出

可以通过参数“-d32”强制 64 位 JVM 使用 32 位模式吗?

可以将 64 位数据类型用作 AdjustWindowRect 的参数吗

64 位 DBeaver 无法连接到 ODBC 源 - “参数编号超出范围”。