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\n", a,b,a+b);
} //test_sub.c
#include <stdio.h> void sub(int a, int b)
{
printf("%d - %d = %d\n", 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(,);
sub(,);
return ;
}
执行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
Python cffi学习(二)的更多相关文章
- Python基础学习二
Python基础学习二 1.编码 utf-8编码:自动将英文保存为1个字符,中文3个字符.ASCll编码被囊括在内. unicode:将所有字符保存为2给字符,容纳了世界上所有的编码. 2.字符串内置 ...
- Python cffi学习
cffi是连接Python与c的桥梁,可实现在Python中调用c文件.cffi为c语言的外部接口,在Python中使用该接口可以实现在Python中使用外部c文件的数据结构及函数. 由于资料较少,所 ...
- python多线程学习二
本文希望达到的目标: 多线程同步原语:互斥锁 多线程队列queue 线程池threadpool 一.多线程同步原语:互斥锁 在多线程代码中,总有一些特定的函数或者代码块不应该被多个线程同时执行,通常包 ...
- 【Python基础学习二】定义变量、判断、循环、函数基本语法
先来一个愉快的Hello World吧,就是这么简单,不需要写标点符号,但是需要严格按照缩进关系,Python变量的作用域是靠tab来控制的. print("Hello World" ...
- python基础学习二——第二天
对于python而言,一切事物都是对象,对象是基于类创建的,对象继承了类的属性,方法等特性 一.int 首先我们来查看一下int包含了哪些函数 # python3.x dir(int) # ['__a ...
- Python Tutorial 学习(二)--Using the Python Interpreter
Using the Python Interpreter 2.1. Invoking the Interpreter The Python interpreter is usually install ...
- Python爬虫学习(二) ——————爬取前程无忧招聘信息并写入excel
作为一名Pythoner,相信大家对Python的就业前景或多或少会有一些关注.索性我们就写一个爬虫去获取一些我们需要的信息,今天我们要爬取的是前程无忧!说干就干!进入到前程无忧的官网,输入关键字&q ...
- python爬虫学习(二):定向爬虫例子-->使用BeautifulSoup爬取"软科中国最好大学排名-生源质量排名2018",并把结果写进txt文件
在正式爬取之前,先做一个试验,看一下爬取的数据对象的类型是如何转换为列表的: 写一个html文档: x.html<html><head><title>This is ...
- python模块学习(二)
configparser模块 软件常见文档格式如下: [DEFAULT]ServerAliveInterval = 45Compression = yesCompressionLevel = 9For ...
随机推荐
- EF+LINQ事物处理 C# 使用NLog记录日志入门操作 ASP.NET MVC多语言 仿微软网站效果(转) 详解C#特性和反射(一) c# API接受图片文件以Base64格式上传图片 .NET读取json数据并绑定到对象
EF+LINQ事物处理 在使用EF的情况下,怎么进行事务的处理,来减少数据操作时的失误,比如重复插入数据等等这些问题,这都是经常会遇到的一些问题 但是如果是我有多个站点,然后存在同类型的角色去操作 ...
- 小白都能看明白的VLAN原理解释
为什么需要VLAN 1. 什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络.VLAN ...
- (8) MySQL主从复制架构使用方法
一. 单个数据库服务器的缺点 数据库服务器存在单点问题 数据库服务器资源无法满足增长的读写请求 高峰时数据库连接数经常超过上限 二. 如何解决单点问题 增加额外的数据库服务器,组建数据库集群 同一集群 ...
- [HDFS Manual] CH2 HDFS Users Guide
2 HDFS Users Guide 2 HDFS Users Guide 2.1目的 2.2.概述 2.3.先决条件 2.4. Web Interface 2.5. Shell Command 2. ...
- 数据类型表(DELPHI、C++)
delphi整型数据表 Integer -2147483648..2147483647 signed 32-bit Cardinal 0..4294967295 unsigned 32-bit Sho ...
- 100BASE-TX、100Base-FX等含义
100BASE-TX:双绞线,使用两对非屏蔽双绞线或两对1类屏蔽双绞线连接,传输距离100米 100Base-FX,是在光纤上实现的100 Mbps以太网标准,其中F指示光纤,IEEE标准为802.3 ...
- [转]kindeditor隐藏上传图片框网络图片或本地上传的功能
原文地址:http://www.lingchenliang.com/post/154.html kindeditor富文本编辑器点击上传图片按钮,在弹出的窗口中去掉上传网络图片的功能,只留下本地上传, ...
- Oracle分析函数-keep(dense_rank first/last)
select * from criss_sales where dept_id = 'D02' order by sale_date ; 此时有个新需求,希望查看部门 D02 内,销售记录时间最早,销 ...
- 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程
一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...
- tensorflow中moving average的用法
一般在保存模型参数的时候,都会保存一份moving average,是取了不同迭代次数模型的移动平均,移动平均后的模型往往在性能上会比最后一次迭代保存的模型要好一些. tensorflow-model ...