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_VAL
是 struct.pack(your 64-bit integer)
首先进入字符串并将此字符串传递给 ioctl,而不是直接传递整数。
像这样:
struct.pack('>Q',1<<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 位模式吗?