本文介绍在linux环境下python调用c语言编写的动态链接库so。
Python 调用动态链接库
- linux操作系统中动态链接库为so文件。
- python调用so文件使用动态方式加载,python中使用ctypes库
- ctypes是Python调用c的动态链接库的一个内置模块。
环境说明
- 操作系统:linux
- python: python3
构建动态链接库
- 项目结构
├── CMakeLists.txt # cmake构建配置文件
├── hello.cc # 动态链接库源文件
└── hello.h # 动态链接库头文件
- CMakeLists.txt
cmake_minimum_required(VERSION 3.1) # 设置cmake最小版本
project(hello) # 设置项目名称
set(src_h hello.h) # 设置头文件
set(src_c hello.c) # 设置源文件
add_library( hello SHARED ${src_h} ${src_c}) # 生成libhello.so库文件。
- hello.h
#ifdef __cplusplus
extern "C"{
#endif
#ifndef _HELLO_H_
#define _HELLO_H_
// -------------- so基础调用 -------------
void say_hello(); // 基础调用,无参数调用
void say_hello2(int iNum); // 传递int类型参数
void say_hello3(const char *str); // 传递char * 字符串
int say_hello4(int iNum1,int Num2); // 传递两个参数,并且带返回值。
// -------------- so结构体调用 -------------
struct POINT{
int x;
int y;
};
struct POINT get_mouse_position(); // 返回结构体
struct POINT *get_position(); // 返回结构体指针
void release_point(struct POINT * ptrData); // 将结构指针作为参数
// -------------- so 回调函数 -------------
typedef void (callback)();
typedef int (callback2)(int);
void send_message(int a, callback funcPtr); // 无参数的回调函数
int send_message2(int a, callback2 funcPtr); // 带参数,带返回值的回调函数
#ifdef __cplusplus
}
#endif
#endif
- hello.c
#include <stdio.h>
#include <stdlib.h>
#include "hello.h"
void say_hello(){
printf("hello\r\n");
}
void say_hello2(int iNum){
printf("hello2 num:%d\r\n",iNum);
}
void say_hello3(const char *str){
printf("hello3 str:%s\r\n",str);
}
int say_hello4(int iNum1,int iNum2){
int res = iNum1 + iNum2;
printf("hello4 res:%d\r\n",res);
return res;
}
struct POINT get_mouse_position(){
struct POINT temp;
temp.x = 12;
temp.y = 13;
return temp;
}
struct POINT *get_position(){
struct POINT *temp =NULL;
temp = (struct POINT *)malloc(sizeof(struct POINT));
temp->x = 10;
temp->y = 20;
return temp;
}
void release_point(struct POINT * ptrData){
if(NULL != ptrData){
free(ptrData);
ptrData = NULL;
}
}
void send_message(int a, callback funcPtr){
printf("send_message a:%d\r\n",a);
if(funcPtr != NULL)
funcPtr();
}
int send_message2(int a, callback2 funcPtr){
printf("send_message2 a:%d\r\n",a);
if(funcPtr != NULL){
int res= funcPtr(6);
printf("回调函数的执行结果为:%d\r\n",res);
}
return 5;
}
- 构建步骤,如果生成libhello.so文件那么就构建成功了
mkdir build
cd build
cmake ..
make
python动态链接库基础调用
将so文件拷贝到py文件的同级目录
运行命令:
python3 main.py
目录结构如下:
├── libhello.so
└── main.py
main.py
import ctypes
from ctypes import *
solib = ctypes.CDLL('./libhello.so')
func_say_hello = solib.say_hello
func_say_hello()
print("func_say_hello 执行完毕!")
print("============================")
func_say_hello2 = solib.say_hello2
func_say_hello2(2)
print("func_say_hello2 执行完毕!")
print("============================")
func_say_hello3 = solib.say_hello3
str = "I love you"
func_say_hello3(str.encode("UTF-8"))
print("func_say_hello3 执行完毕!")
print("============================")
func_say_hello4 = solib.say_hello4
res = func_say_hello4(5,6)
print("func_say_hello3 执行完毕!结果为:",res)
print("============================")
执行结果如下:
hello
func_say_hello 执行完毕!
============================
hello2 2
func_say_hello2 执行完毕!
============================
hello3 I love you
func_say_hello3 执行完毕!
============================
hello4 11
func_say_hello3 执行完毕!结果为: 11
============================
python动态链接库结构体调用
main.py
import ctypes
from ctypes import *
class PyPointStruct(Structure):
_fields_ = [
("x", c_int),
("y", c_int),
]
solib = ctypes.CDLL('./libhello.so')
func_get_mouse_position = solib.get_mouse_position
func_get_mouse_position.restype = PyPointStruct # 设置函数返回结果的类型为结构体
resp = func_get_mouse_position()
print("res2:",resp.x)
print("res2:",resp.y)
print("============================")
func_get_position = solib.get_position
func_get_position.restype = POINTER(PyPointStruct) # 设置函数返回结果的类型为结构体指针
resb = func_get_position()
print("res3:",resb.contents.x)
print("res3:",resb.contents.y)
print("============================")
执行结果如下:
res2: 12
res2: 13
============================
res3: 10
res3: 20
============================
python动态链接库回调函数
main.py
import ctypes
from ctypes import *
solib = ctypes.CDLL('./libhello.so') # 加载动态链接库
def cb_sayhello(): # 无参数的回调函数
print("python hello")
def cb_sayhello2(res): # 带参数有返回值的回调函数。
print("python hello2,res:",res)
return 1024
solib = ctypes.CDLL('./libhello.so')
func_send_message = solib.send_message
# CFUNCTYPE定义方法的签名,第一参数表示方法的返回类型,后面开始编译参数的类型
funcStruct = CFUNCTYPE(None)
solib.send_message(10,funcStruct(cb_sayhello))
print("============================")
send_message2 = solib.send_message2
# CFUNCTYPE定义方法的签名,第一参数表示方法的返回值,后面开始编译参数的类型
funcStruct2 = CFUNCTYPE(c_int,c_int)
send_message2(10,funcStruct2(cb_sayhello2))
print("============================")
执行结果:
send_message a:10
python hello
============================
send_message2 a:10
python hello2,res: 6
回调函数的执行结果为:1024
============================
Note:在一个项目中,我使用python调用动态库,发现奔溃的现象。
具体问题如下,
我将python中的回调函数传递给so调用,但是so的程序中使用了多线程调用回调函数,由于python的回调函数时通过构造对象的方式调的send_message2(10,funcStruct2(cb_sayhello2)) 其中,funcStruct2(cb_sayhello2)是一个对象,因此在这个函数执行完成后会释放这个对象,在so的程序中多线程函数执行回调函数对象就已经被释放了 ,因此照成回调函数调用崩溃的情况。
代码下载地址
https://download.csdn.net/download/arv002/33253778
本文详细介绍了在Linux环境下,如何使用Python的ctypes库调用C编写的动态链接库,包括基础调用、结构体操作和回调函数的实战。还探讨了遇到的回调函数并发问题及其解决方案。
340

被折叠的 条评论
为什么被折叠?



