zoukankan      html  css  js  c++  java
  • Python cffi学习(二)

    上篇文章中讲到Python中调用外部c文件可以有两种方法,一是使用cffi.verify()的形式使用,但是该种方式仍然需要进行函数声明。二是将外部c文件编译成为函数库,然后通过cffi进行使用。

    由于第二种方法比较常用,在开源项目中使用较多,因此就第二种方法进行简单实验,主要分为两个步骤:编写c函数并创建动态链接库,在Python中使用链接库中的函数。

    (一)创建动态链接库

    将文件test_add.c及test_sub.c编译成一个动态库:libtest.so

    文件test_add.c及test_sub.c内容如下:

    //test_add.c
    #include <stdio.h>
    
    void add (int a, int b)
    {
        printf("%d + %d = %d
    ", a,b,a+b);
    }
    
    //test_sub.c
    #include <stdio.h>
    
    void sub(int a, int b)
    {
        printf("%d - %d = %d
    ", a,b,a-b);
    }

    使用命令:gcc test_add.c test_sub.c -fpic -shared -o libtest.so将上述文件编译成动态库libtest.so,其中-fpic为编译选项,pic为position independent code,表示生成位置无关的代码,-shared是链接选项,表示编译器生成动态库而不是可执行文件。结果如下:

    以下编写程序调用动态链接库中的函数,编写对应的头文件test_so.h声明动态库中可用的函数:

    //test_so.h
    #ifndef __TEST_SO__
    #define __TEST_SO__
    
    void add(int a, int b);
    void sub(int a, int b);
    
    #endif

    编写test.c调用动态库中的函数:

    //test.c
    #include <stdio.h>
    #include "test_so.h"
    
    int main(void)
    {
        add(3,4);
        sub(5,2);
        return 0;
    }

    执行gcc test.c -L. -ltest -o test 将test.c与动态库libtest.so链接生成可执行文件test。-L.表示要链接的库在当前目录中,-ltest则为编译器查找动态链接库时隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称。结果如下:

     

    可以看到运行test时会显示找不到libtest.so,可以通过两种方式进行解决:

    (1)设置环境变量LD_LIBRARY_PATH指向libtest.so的路径:

    (2)将共享库移动到/usr/local/lib文件夹下或其他lib文件夹下,然后执行ldconfig,重新编译执行即可:

    ldconfig命令主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下, 搜索出可共享的动态链接库(格式如lib*.so*), 进而创建出动态装入程序(ld.so)所需的连接和缓存文件,缓存文件默认为/etc/ld.so.cache。要在运行的程序中使用动态链接库,需要指定系统的动态链接搜索的路径,让系统找到运行所需的动态链接库才可以。系统中的配置文件/etc/ld.so.conf是动态链接库的搜索路径配置文件。在这个文件内,存放着可被Linux共享的动态链接库所在目录的名字(系统目录/lib,/usr/lib除外),多个目录名间以空白字符(空格,换行等)或冒号或逗号分隔。查看该文件的内容:

    可见其为包含ld.so.conf.d目录下的所有配置文件,进入该目录查看:

    可见该目录下libc.conf文件中包含了路径/usr/local/lib等。但若该目录下没有配置文件包含上述路径,如下:

    这种情况下在执行ldconfig之前,需要将新共享库目录加入到共享库配置文件/etc/ld.so.conf中,如下:

    echo "/usr/local/lib" >> /etc/ld.so.conf

    ldconfig

    之后将共享库移动到/usr/local/lib,重新编译执行即可找到对应链接库中的函数。

    (二)Python中通过cffi调用上述链接库中的函数

    为了通过include<test_so.h>的方式可以找到该头文件,需要将该头文件移动至/usr/include目录下。相应地之前的test.c若要正常执行也需要将include “test_so.h”替换为include <test_so.h>,之后重新编译执行。

    类似地编写setup.py:

    #!/usr/bin/env python
    
    import os
    import sys
    
    from setuptools import setup, find_packages
    
    os.chdir(os.path.dirname(sys.argv[0]) or ".")
    
    setup(
        name="cffi-test",
        version="0.1",
        packages=find_packages(),
        install_requires=["cffi>=1.0.0"],
        setup_requires=["cffi>=1.0.0"],
        cffi_modules=[
            "./cffi_test/build_test.py:ffi", #指定需要生成的ffi实例
        ],
    )

    在cffi_test文件夹下编写test.h:

    //test.h
    //按照test_so.h声明需要使用的test链接库中的c函数
    void add(int a, int b);
    void sub(int a, int b);

    build_test.py:这里需要注意链接库的头文件以及所需调用的库的名称

    #build_test.py
    
    import os
    
    from cffi import FFI
    
    ffi = FFI()
    
    ffi.set_source("_test",
        "#include <test_so.h>", #动态链接库libtest的头文件,在/usr/include文件下
        # 包含需要使用的库
        libraries=["test"], #库的名称,按照之前所编写的则为test
    )
    
    with open(os.path.join(os.path.dirname(__file__), "test.h")) as f: #这里的test.h为当前目录下编写的需要引用的链接库中的函数的声明
        ffi.cdef(f.read())
    
    if __name__ == "__main__":
        ffi.compile()

    test.py:

    #test.py
    from _test import ffi, lib #从产生的模块中引入ffi实例
    
    lib.add(3,5)
    lib.sub(5,3)

    同样地,执行python setup.py build

    python setup.py install 生成build等文件

    进入cffi_test目录,执行python build_test.py生成.c .o .so文件

    最后执行python test.py即可实现对自定义函数库中函数的调用

    以上即可实现在Python中调用外部c函数

    参考:https://www.jianshu.com/p/695d16a8947f

    https://blog.csdn.net/oscer2016/article/details/52048957

    http://zhb-mccoy.iteye.com/blog/1618511

  • 相关阅读:
    218. The Skyline Problem
    327. 区间和的个数
    37 Sudoku Solver
    36. Valid Sudoku
    差分数组(1109. 航班预订统计)
    android开发里跳过的坑——onActivityResult在启动另一个activity的时候马上回调
    重启系统media服务
    android源码mm时的编译错误no ruler to make target `out/target/common/obj/JAVA_LIBRARIES/xxxx/javalib.jar', needed by `out/target/common/obj/APPS/xxxx_intermediates/classes-full-debug.jar'. Stop.
    关于android系统启动不同activity默认过渡动画不同的一些认识
    android开发里跳过的坑——android studio 错误Error:Execution failed for task ':processDebugManifest'. > Manifest merger failed with multiple errors, see logs
  • 原文地址:https://www.cnblogs.com/ccxikka/p/9647108.html
Copyright © 2011-2022 走看看