【问题标题】:Using python ctypes to call io_submit in LinuxLinux下使用python ctypes调用io_submit
【发布时间】:2011-04-03 16:27:15
【问题描述】:

我正在尝试使用 python ctypes 调用 io_submit。 我正在编写的代码应该适用于 32 位和 64 位 Intel/AMD 架构,但这里我将重点关注 64 位。

我已经定义了以下内容:

def PADDED64(type, name1, name2):
    return [(name1, type), (name2, type)]

def PADDEDptr64(type, name1, name2): 
    return [(name1, type)]

def PADDEDul64(name1, name2):
    return [(name1, ctypes.c_ulong)]

class IOVec(ctypes.Structure):
    _fields_ = [("iov_base", ctypes.c_void_p), ("iov_len", ctypes.c_size_t)]

class IOCBDataCommon64(ctypes.Structure):
    _fields_ = PADDEDptr64(ctypes.c_void_p, "buf", "__pad1") + \
        PADDEDul64("nbytes", "__pad2") + \
        [("offset", ctypes.c_longlong), ("__pad3", ctypes.c_longlong), ("flags", ctypes.c_uint), ("resfd", ctypes.c_uint)]

class IOCBDataVector(ctypes.Structure):
    _fields_ = [("vec", ctypes.POINTER(IOVec)), ("nr", ctypes.c_int), ("offset", ctypes.c_longlong)]

class IOCBDataPoll64(ctypes.Structure):
    _fields_ = PADDED64(ctypes.c_int, "events", "__pad1")

class SockAddr(ctypes.Structure):
    _fields_ = [("sa_family", ctypes.c_ushort), ("sa_data", ctypes.c_char * 14)]

class IOCBDataSockAddr(ctypes.Structure):
    _fields_ = [("addr", ctypes.POINTER(SockAddr)), ("len", ctypes.c_int)]

class IOCBDataUnion64(ctypes.Union):
    _fields_ = [("c", IOCBDataCommon64), ("v", IOCBDataVector), ("poll", IOCBDataPoll64), ("saddr", IOCBDataSockAddr)]

class IOCB64(ctypes.Structure):
    _fields_ = PADDEDptr64(ctypes.c_void_p, "data" , "__pad1") + \
        PADDED64(ctypes.c_uint, "key", "__pad2") + \
        [("aio_lio_opcode", ctypes.c_short), ("aio_reqprio", ctypes.c_short), ("aio_fildes", ctypes.c_int), ("u", IOCBDataUnion64)]

class Timespec(ctypes.Structure):
    _fields_ = [("tv_sec", ctypes.c_long), ("tv_nsec", ctypes.c_long)]

class IOEvent64(ctypes.Structure):
    _fields_ = PADDEDptr64(ctypes.c_void_p, "data", "__pad1") + \
        PADDEDptr64(ctypes.POINTER(IOCB64), "obj", "__pad2") + \
        PADDEDul64("res", "__pad3") + \
        PADDEDul64("res2", "__pad4")

我有一个名为 AIOCommands 的包装类:

class AIOCommands:
    def __init__(self, aioCommandList):
        self.__commandList = aioCommandList
        self.__iocbs = (IOCB64 * len(self.__commandList))()
        for i in range(len(self.__commandList)):
            self.__commandList[i].initialize(self.__iocbs[i])
    def size(self):
        return len(self.__iocbs)
    def getIOCBArray(self):
        return self.__iocbs

我已经定义了io_submit的参数和返回值:

class Executor:
    def __init__(self, aioLibraryPath):
        self.__aio = ctypes.CDLL(aioLibraryPath)
        self.__aio.io_submit.argtypes = [self.aio_context_t, ctypes.c_long, ctypes.POINTER(ctypes.POINTER(IOCB64))]
        self.__aio.io_submit.restype = ctypes.c_long

现在,Executor.io_submit 主体应该是什么样子?我试过了:

def io_submit(self, aioContext, aioCommands):
    iocbPtr = ctypes.cast(aioCommands.getIOCBArray(), ctypes.POINTER(self.iocb_t))
    return self.__aio.io_submit(aioContext, aioCommands.size(), ctypes.byref(iocbPtr))

但每当 aioCommandList 的长度大于 1 时,我就会得到 分段错误。 当列表仅包含 1 个命令时,代码按预期工作。

这可能是我的结构定义有问题吗?我试图模仿 libaio.h 中的定义(假设只支持 little-endian 架构):

#if defined(__i386__) /* little endian, 32 bits */
#define PADDED(x, y)    x; unsigned y
#define PADDEDptr(x, y) x; unsigned y
#define PADDEDul(x, y)  unsigned long x; unsigned y
#elif defined(__ia64__) || defined(__x86_64__) || defined(__alpha__)
#define PADDED(x, y)    x, y
#define PADDEDptr(x, y) x
#define PADDEDul(x, y)  unsigned long x
#elif defined(__powerpc64__) /* big endian, 64 bits */
#define PADDED(x, y)    unsigned y; x
#define PADDEDptr(x,y)  x
#define PADDEDul(x, y)  unsigned long x
#elif defined(__PPC__)  /* big endian, 32 bits */
#define PADDED(x, y)    unsigned y; x
#define PADDEDptr(x, y) unsigned y; x
#define PADDEDul(x, y)  unsigned y; unsigned long x
#elif defined(__s390x__) /* big endian, 64 bits */
#define PADDED(x, y)    unsigned y; x
#define PADDEDptr(x,y)  x
#define PADDEDul(x, y)  unsigned long x
#elif defined(__s390__) /* big endian, 32 bits */
#define PADDED(x, y)    unsigned y; x
#define PADDEDptr(x, y) unsigned y; x
#define PADDEDul(x, y)  unsigned y; unsigned long x
#else
#error  endian?
#endif

struct io_iocb_poll {
    PADDED(int events, __pad1);
};  /* result code is the set of result flags or -'ve errno */

struct io_iocb_sockaddr {
    struct sockaddr *addr;
    int     len;
};  /* result code is the length of the sockaddr, or -'ve errno */

struct io_iocb_common {
    PADDEDptr(void  *buf, __pad1);
    PADDEDul(nbytes, __pad2);
    long long   offset;
    long long   __pad3;
    unsigned    flags;
    unsigned    resfd;
};  /* result code is the amount read or -'ve errno */

struct io_iocb_vector {
    const struct iovec  *vec;
    int         nr;
    long long       offset;
};  /* result code is the amount read or -'ve errno */

struct iocb {
    PADDEDptr(void *data, __pad1);  /* Return in the io completion event */
    PADDED(unsigned key, __pad2);   /* For use in identifying io requests */

    short       aio_lio_opcode; 
    short       aio_reqprio;
    int     aio_fildes;

    union {
        struct io_iocb_common       c;
        struct io_iocb_vector       v;
        struct io_iocb_poll     poll;
        struct io_iocb_sockaddr saddr;
    } u;
};

任何帮助将不胜感激,我已经坚持了几个小时。

【问题讨论】:

    标签: python c linux stack ctypes


    【解决方案1】:

    我的理解是io_submit()iocbpp 参数是一个指向struct iocb 的指针数组。

    这似乎通过此处的 Linux 特定示例得到了加强:http://voinici.ceata.org/~sana/blog/?p=248 和此处的 EINVAL 错误文档:http://linux.die.net/man/2/io_submit(数组下标优先于取消引用)

    您提供给io_submit() 的是对struct iocb 数组的引用。当 io_submit 在遍历 iocbpp 数组时,您肯定会得到一个段错误,因为它会取消引用虚假的内存地址。第一个元素(索引 0)可以正常工作,因为没有内存偏移来访问它。

    编辑 这里的另一个例子:http://www.xmailserver.org/eventfd-aio-test.c

    【讨论】:

    • 谢谢!我误解了 io_submit 的语义。现在可以使用了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 2013-11-25
    相关资源
    最近更新 更多