Python调用Linux c库:cty

发布时间:2019-08-27 08:02:51编辑:auto阅读(1778)

       我在http://jlnsqt.blog.51cto.com/2212965/1405052这篇博客中讲述了匹配URL的一个算法,因项目需要将其封装为动态库,并让python组调用,所以研究了一下ctypes,感觉超级棒,必须记录下来。


       首先介绍一下我的动态库接口。

       动态库结构体:

       
    typedef struct _whitelist_tree_node_ {
        uint8_t white_type; //匹配白名单是否结束,代表下一步执行的动作
        uint8_t child_count;                //该节点的子节点个数
        uint8_t child_order[MAX_CHILDS_NUM]; //子节点在childs的位置(起始1)
        struct _whitelist_tree_node_ *childs;   //子节点数组
    } whitelist_tree_node;
    typedef struct _whitelist_tree_ {
        whitelist_tree_node *root;          //根节点
        unsigned int whitelist_count;       //URL白名单个数
    } whitelist_tree, *PUrlWhiteListControl;
                                                                                                                                                                                                                                                                                                                                                        
    函数接口:
    /**
     * [InitUrlWhiteList 根据配置文件读取数据库中的URL白名单,并初始化到内存中]
     * @param  ini_file   [配置文件路径,一般若无特殊数据库配置可为NULL]
     * @param  p_control  [查询控制器,用完需要关掉]
     * @return            [0: success, other: failure code]
     */
    int InitUrlWhiteList(const char *ini_file, PUrlWhiteListControl *p_control);
    /**
     * [SearchUrlWhiteList 查询URL是否在白名单中]
     * @param  url        [待查URL]
     * @param  p_control  [查询控制器]
     * @return            [0: find, < 0: run failure code, > 0: not find]
     */
    int SearchUrlWhiteList(const char *url, PUrlWhiteListControl p_control);
    
    /**
     * [CloseUrlWhiteList 关闭初始化的查询控制器]
     * @param  control [控制器]
     */
    void CloseUrlWhiteList(PUrlWhiteListControl p_control);


       这里不再给出生成动态库方法和各个接口函数的定义,只介绍如何在python中调用。这里假设我动态库的名称为“liburlwhitelist.so”,动态库和python文件在同一目录,或者再引用动态库的时候用绝对路径。

       开始使用ctypes之前,介绍一下ctypes的类型对照:

    ctypes typeC typePython type
    c_bool_Boolbool (1)
    c_charchar1-character string
    c_wcharwchar_t1-character unicode string
    c_bytecharint/long
    c_ubyteunsignedcharint/long
    c_shortshortint/long
    c_ushortunsignedshortint/long
    c_intintint/long
    c_uintunsignedintint/long
    c_longlongint/long
    c_ulongunsignedlongint/long
    c_longlong__int64 or longlongint/long
    c_ulonglongunsigned__int64 or unsignedlonglongint/long
    c_floatfloatfloat
    c_doubledoublefloat
    c_longdoublelongdoublefloat
    c_char_pchar* (NUL terminated)string or None
    c_wchar_pwchar_t* (NUL terminated)unicode or None
    c_void_pvoid*int/long or None


       首先导入ctypes:


    from ctypes import *

       定义最大子节点个数,也即静态数组的大小。    

    #max child node number
    MAX_NODE_CHILD_NUM = 46

       下面就是重点了,需要用python模拟出Linux C的结构体来。


    #define tree node
    class whitelist_tree_node(Structure):
        pass
    whitelist_tree_node._fields_ = [
        ("white_type", c_ubyte),
        ("child_count", c_ubyte),
        ("child_order", c_ubyte * MAX_NODE_CHILD_NUM),
        ("childs", POINTER(whitelist_tree_node))
    ]
    #define tree
    class whitelist_tree(Structure):
        pass
    whitelist_tree._fields_ = [
        ("root", POINTER(whitelist_tree_node)),
        ("whitelist_count", c_uint)
    ]
    #define query url whitelist control
    PUrlWhiteListControl = POINTER(whitelist_tree)

       为了定义节点指向自己的指针,这里必须先用pass定义一个空的对象,否则会出现“NameError: name 'whitelist_tree_node' is not defined”的错误。取类型的指针用POINTER()函数,而取变量对象的指针用pointer()函数,注意区分。


       导入库,可用绝对路径:    

    #load library
    url_whitelist_lib = cdll.LoadLibrary("./liburlwhitelist.so")


       引入接口函数,并对接口函数属性进行设置。    

    #define init function
    InitUrlWhiteList = url_whitelist_lib.InitUrlWhiteList
    #define argument types
    InitUrlWhiteList.argtypes = [POINTER(PUrlWhiteListControl)]
    #define return types. By default functions are assumed to return the C int type
    InitUrlWhiteList.restype = c_int
                                                                                                                                                                                            
    #define search function
    SearchUrlWhiteList = url_whitelist_lib.SearchUrlWhiteList
    SearchUrlWhiteList.argtypes = [c_char_p, PUrlWhiteListControl]
    SearchUrlWhiteList.restype = c_int
                                                                                                                                                                                            
    #define close function
    CloseUrlWhiteList = url_whitelist_lib.CloseUrlWhiteList
    CloseUrlWhiteList.argtypes = [PUrlWhiteListControl]
    CloseUrlWhiteList.restype = None

       定义每个函数第一行“InitUrlWhiteList =url_whitelist_lib.InitUrlWhiteList”是为了减少函数调用。InitUrlWhiteList.argtypes设置函数的参数,为了更好的调用,减少出错。

    InitUrlWhiteList.restype设置函数的返回类型,因为ctypes默认的返回类型时C int,我这里还是指出,便于统一和减少出错。


       使用部分例子:

    p_control = PUrlWhiteListControl()
    ret_code = InitUrlWhiteList(None, pointer(p_control))
    if ret_code != 0:
        print "InitUrlWhiteList error code: %d" % ret_code
    else:
        print "init url whitelist num: %u" % p_control.contents.whitelist_count

       这里有三个地方需要注意。一、None变量即是C中NULL。二:InitUrlWhiteList参数,因其第二个参数为PUrlWhiteListControl的指针,所以这里用pointer()函数,当然也可用byref()函数,但是在官方文档中指出:

    The same effect can be achieved with the pointer() function, although
    pointer() does a lot more work since it constructs a real pointer
    object, so it is faster to use byref() if you don’t need the pointer
     object in Python itself

       所以这里我们还是用pointer()函数好。三、因PUrlWhiteListControl本身是一个指针,要访问他的内容需要用contents方法。如“p_control.contents.whitelist_count”。


       好了,关于ctypes,先介绍到这里,更详细的请参考官方文档:https://docs.python.org/2/library/ctypes.html。详细代码如下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Author: sqt
    # @Email:  qingtao_shang@vulnhunt.com
    # @Date:   2014-04-30 09:57:37
    # @Desc:   Python测试URL白名单动态库
    # @Last Modified by:   sqt
    # @Last Modified time: 2014-04-30 13:30:17
                   
    from ctypes import *
                   
    #max child node number
    MAX_NODE_CHILD_NUM = 46
                   
    #define tree node
    class whitelist_tree_node(Structure):
        pass
                   
    whitelist_tree_node._fields_ = [
        ("white_type", c_ubyte),
        ("child_count", c_ubyte),
        ("child_order", c_ubyte * MAX_NODE_CHILD_NUM),
        ("childs", POINTER(whitelist_tree_node))
    ]
                   
    #define tree
    class whitelist_tree(Structure):
        pass
                   
    whitelist_tree._fields_ = [
        ("root", POINTER(whitelist_tree_node)),
        ("whitelist_count", c_uint)
    ]
                   
    #define query url whitelist control
    PUrlWhiteListControl = POINTER(whitelist_tree)
    #load library
    url_whitelist_lib = cdll.LoadLibrary("./liburlwhitelist.so")
                   
    #simple call function name
    #define init function
    InitUrlWhiteList = url_whitelist_lib.InitUrlWhiteList
    #define argument types
    InitUrlWhiteList.argtypes = [POINTER(PUrlWhiteListControl)]
    #define return types. By default functions are assumed to return the C int type
    InitUrlWhiteList.restype = c_int
                   
    #define search function
    SearchUrlWhiteList = url_whitelist_lib.SearchUrlWhiteList
    SearchUrlWhiteList.argtypes = [c_char_p, PUrlWhiteListControl]
    SearchUrlWhiteList.restype = c_int
                   
    #define close function
    CloseUrlWhiteList = url_whitelist_lib.CloseUrlWhiteList
    CloseUrlWhiteList.argtypes = [PUrlWhiteListControl]
    CloseUrlWhiteList.restype = None
                   
    #sample
    if __name__ == "__main__":
        p_control = PUrlWhiteListControl()
        ret_code = InitUrlWhiteList(None, pointer(p_control))
        if ret_code != 0:
            print "InitUrlWhiteList error code: %d" % ret_code
        else:
            print "init url whitelist num: %u" % p_control.contents.whitelist_count
                    
        url = ""
        while 1:
            url = raw_input("Input url or exit:")
            if url == "q" or url == "exit":
                break
                   
            find_code = SearchUrlWhiteList(url, p_control)
            if find_code == 0:
                print "url: %s find." % url
            else:
                print "url: %s not find." % url
                    
        CloseUrlWhiteList(p_control)
        del p_control


关键字