2013-06-20 9 views
5

W moim kodzie Pythona (2.7.3) próbuję użyć wywołania ioctl, akceptując długie int (64-bitowe) jako argument. Jestem w systemie 64-bitowym, więc 64-bitowy int ma taki sam rozmiar jak wskaźnik.64-bitowy argument dla fcntl.ioctl()

Mój problem polega na tym, że Python prawdopodobnie nie akceptuje 64-bitowej wartości int jako argumentu dla wywołania fcntl.ioctl(). Z przyjemnością akceptuje 32-bitowy lub 64-bitowy wskaźnik - , ale potrzebuję przekazać 64-bitową int.

Oto mój obsługi 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; 
} 

W istniejącego kodu C, używam wywołanie takiego:

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); 

} 

W Pythonie I otworzyć plik urządzenia, a następnie pojawia się następujące wyniki:

>>> 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! 
>>> 

Czy jest jakiś sposób Python może przechodzić m y 64-bitowy argument do ioctl()?

+0

Pomogłoby to w zapewnieniu odtwarzalnego przykładu, jeśli to możliwe.Biorąc pod uwagę, że wywołania 'ioctl()' są specyficzne dla urządzenia, podstawienie 'IOC_GET_VAL' dla rzeczywistego kodu żądania, którego używasz sprawia, że ​​jest to trudne do przetestowania. – Aya

+0

@Aya: Dzięki za komentarz. Jestem nowy dla sterowników urządzeń i mam problem z skonstruowaniem trywialnego, choć funkcjonalnego przykładu. Ale zobaczę, co mogę zrobić. :) – Ziv

+0

W międzyczasie opublikowałem rozwiązanie oparte na 'ctypes'. – Aya

Odpowiedz

6

To, czy jest to możliwe, czy nie, przy użyciu Pythona fcntl.ioctl() będzie zależeć od systemu. Kalka poprzez kod źródłowy, komunikat o błędzie pochodzi z poniższym teście na line 658 of getargs.c ...

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

... I w moim systemie, /usr/include/limits.h mówi mi ...

# define INT_MAX 2147483647 

.. , który jest (prawdopodobnie) (2 ** ((sizeof(int) * 8) - 1)) - 1.

Tak więc, jeśli nie pracujesz w systemie, w którym sizeof(int) jest co najmniej 8, będziesz musiał wywołać podstawową funkcję C bezpośrednio przy użyciu modułu ctypes, ale jest to zależne od platformy.

Zakładając, Linux, coś jak to powinno działać ...

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

Tak! Działa to :) Jestem zaskoczony, że nie ma wbudowanej obsługi wewnątrz fcntl.ioctl(), ale jest to całkowicie uzasadnione obejście. – Ziv

+0

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

+0

Świetna odpowiedź. Wielkie dzięki: D – Ziv

3

Zapis 'arg' w Pythona ioctl różni się od tego, co C.

w Pythonie (znowu według 1) jest to albo pythonowa liczba całkowita (bez określenia 32 lub 64-bitowego), albo jakiś obiekt bufora (jak ciąg znaków). W Pythonie tak naprawdę nie ma "wskaźników" (wszystkie ukryte szczegóły architektury - takie jak 32- lub 64-bitowe adresy są całkowicie ukryte).

Jeśli dobrze zrozumiałem, to czego potrzebujesz, to w rzeczywistości numer SET_VAL jest struct.pack(your 64-bit integer) na początku ciągu i przekazuje ten ciąg do ioctl, zamiast bezpośredniego podawania liczby całkowitej.

jak poniżej:

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

Dla GET_VAL potrzebny jest 'Q' typ ponownie (nie 'L'), aby rozpakować 64-bitowej wartości całkowitej prawidłowo.

+0

Przykro mi, to nie jest poprawne. (Przepraszam, jeśli mój pierwotny wpis nie był wystarczająco jasny.) O ile mogę powiedzieć, ** dowolne ** użycie struct.pack() tłumaczy (w sterowniku c) na wskaźnik do danych binarnych wysyłanych do ioctl c argument. Nie chcę wskaźnika; Chcę bezpośrednio sterować przekazywaną wartością 64-bitową. – Ziv

+0

Ale ściśle rzecz biorąc, nie można tak naprawdę przekazać wartości 64-bitowej za pomocą 32-bitowych argumentów (jako wskaźnika lub wartości). Typ parametru w wywołaniu ioctl będzie 32-bitową architekturą 32-bitową. W systemach 64-bitowych wszystko stało się 64-bitowe, więc teoretycznie można przekazywać wartości 64-bitowe do IOCTL ** w C ***. – Georgiy

+0

Python z drugiej strony stara się ukryć każdą z tych cech platformy, więc nie ma żadnych odsłonięć wskaźników API. Pomysł jest taki, że nigdy nie musisz przejmować się architekturą. W rzeczywistości liczby całkowite w Pythonie mogą być dowolnie duże (tj. Nieograniczone przez 64-bitowe lub cokolwiek innego). Tak więc, aby pozostać _Pythonic_ (i niezależnym od platformy) prawdopodobnie lepiej jest przekazać obiekty-bufory nawet dla małych rzeczy, takich jak 64-bitowe liczby całkowite ... – Georgiy

Powiązane problemy