2013-06-20 66 views
5

在我的Python(2.7.3)代碼中,我試圖使用一個ioctl調用,接受一個long int(64位)作爲參數。我在64位系統上,所以64位int與指針的大小相同。64位參數fcntl.ioctl()

我的問題是,Python似乎不接受64位int作爲fcntl.ioctl()調用的參數。它愉快地接受一個32位int或一個64位指針 - 但我需要的是通過一個64位int。

這裏是我的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有沒有辦法通過m 64位參數ioctl()?

+0

如果可能的話,將有助於提供可重複的示例。鑑於'ioctl()'調用是特定於設備的,所以用'IOC_GET_VAL'代替正在使用的實際請求代碼使得測試變得很困難。 – Aya

+0

@Aya:感謝您的評論。我是新手設備驅動程序,並且在構建一個微不足道的功能示例時遇到了一些問題。但我會看看我能做些什麼。 :) – Ziv

+0

與此同時,我發佈了一個基於ctypes的解決方案。 – Aya

回答

6

這是否可能使用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; 
} 

...和我的系統上,​​告訴我...

# 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) 
+0

是的!這工作:)我很驚訝沒有內置的fcntl.ioctl()內的支持,但這是一個完全合理的解決方法。 – Ziv

+0

比較http://stackoverflow.com/questions/9257065/int-max-in-32-bit-vs-64-bit-environment。 – Ziv

+0

很好的回答。非常感謝:D – Ziv

3

符號在Python的ioctl 'ARG' 的(再根據1)是從C

什麼

在蟒蛇不同它可以是python integer(不指定32位或64位),也可以是某種緩衝區對象(如字符串)。你並沒有真正擁有Python中的「指針」(所以所有的底層架構細節 - 比如32位或64位地址都完全隱藏)。

如果我的理解正確,你需要的是實際需要的一個SET_VALstruct.pack(your 64-bit integer)首先進入字符串並將這個字符串傳遞給ioctl,而不是直接傳遞整數。

像這樣:

struct.pack('>Q',1<<32)

對於你需要一個 'Q' 再次類型(而不是 'L')正常解壓64位整數值的GET_VAL

+0

對不起,這是不正確的。 (對不起,如果我原來的帖子不夠清楚。)據我所知,**任何**使用struct.pack()翻譯(在C驅動程序內)到一個指向二進制數據被髮送到C的ioctl論據。我不想要一個指針;我想直接控制傳遞的64位值。 – Ziv

+0

但嚴格來說,你不能通過32位參數傳遞任何64位值(不管它是一個指針還是一個'值')。在ioctl調用中的參數類型將是32位體系結構中的32位。 在64位系統上,一切都變成了64位,所以理論上你可以在C ***中將64位值傳遞給IOCTL **。 – Georgiy

+0

另一方面,Python試圖隱藏所有這些平臺細節,因此它不會暴露任何指針-API。想法是你一定不會在意建築是什麼。事實上,Python的整數可以是任意大的(即不受64位或其他限制) 因此,爲了保持_Pythonic_(和平臺無關),傳遞緩衝對象可能更好,即使對於像64位整數... – Georgiy