目录
linux测试
1、动态链接库生产
动态链接库与普通的程序相比而言,没有main函数,是一系列函数的实现。通过shared和fPIC编译参数生产so动态链接库文件。程序在调用库函数时,只需要连接上这个库即可。例如下面实现一个简单的整数四则运输的动态链接库,定义的caculate.h和caculate.c两个文件,生产libcac.so动态链接库。
程序代码如下:
/*caculate.h*/
#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
int add(int a, int b);
//减法
int sub(int a, int b);
//除法
int div(int a, int b);
//乘法
int mul(int a, int b);
#endif
/*caculate.c文件*/#include "caculate.h"
//求两个数的和
int add(int a, int b)
{
return (a + b);
}
//减法
int sub(int a, int b)
{
return (a - b);
}
//除法
int div(int a, int b)
{
return (int)(a / b);
}
//乘法
int mul(int a, int b)
{
return (a * b);
}
编译生产libcac.so文件如下: gcc -shared -fPIC caculate.c -o libcac.so
编写一个测试程序调用此动态链接库的函数,程序如下所示:
#include <stdio.h>
#include "caculate.h"
int main()
{
int a = 20;
int b = 10;
printf("%d + %d = %d\n", a, b, add(a, b));
printf("%d - %d = %d\n", a, b, sub(a, b));
printf("%d / %d = %d\n", a, b, div(a, b));
printf("%d * %d = %d\n", a, b, mul(a, b));
return 0;
}
编译生产可执行文件main如下:gcc main.c -o main -L ./ -lcac (其中-L指明动态链接库的路径,-l后是链接库的名称,省略lib)
拷贝libcac.so到/usr/lib下
程序执行结果如下所示:
rpdzkj@ubuntu:~/jq_project/makefilelib_test$ ./main
20 + 10 = 30
20 - 10 = 10
20 / 10 = 2
20 * 10 = 200
2、获取动态链接库的函数
linux提供dlopen、dlsym、dlerror和dlcolose函数获取动态链接库的函数。通过这个四个函数可以实现一个插件程序,方便程序的扩展和维护。函数格式如下所示:
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
Link with -ldl.
dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。写个测试程序调用上面生产libcac.so库如下所示:
#include <stdio.h>
#include <dlfcn.h>
#define DLL_FILE_NAME "libcac.so"
int main()
{
void *handle;
int (*func)(int, int);
char *error;
int a = 30;
int b = 5;
handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
if (handle == NULL)
{
fprintf(stderr, "Failed to open libaray %s error:%s\n", DLL_FILE_NAME, dlerror());
return -1;
}
func = dlsym(handle, "add");
printf("%d + %d = %d\n", a, b, func(a, b));
func = dlsym(handle, "sub");
printf("%d + %d = %d\n", a, b, func(a, b));
func = dlsym(handle, "div");
printf("%d + %d = %d\n", a, b, func(a, b));
func = dlsym(handle, "mul");
printf("%d + %d = %d\n", a, b, func(a, b));
dlclose(handle);
return 0;
}
程序执行结果如下所示:gcc call_main.c -o call_main -ldl
rpdzkj@ubuntu:~/jq_project/makefilelib_test$ ./call_main
30 + 5 = 35
30 + 5 = 25
30 + 5 = 6
30 + 5 = 150
3.makefile管理
新建src和inc目录
src存放.c文件,inc存放.h文件,顶层目录存放Makefile文件
3.1目录结构
src/caculate.c
inc/test.h
Makefile
#定义伪目标
.PHONY : all clean copy
#编译存放的目录:build、源文件目录:src、头文件目录:inc
DIR_BUILD := build
DIR_LIB := lib
DIR_SRC := src
DIR_INC := inc
DIR_CODE := /mnt/hgfs/MAndroid5.1_source/x_ray/*
#使用变量方便后期makefile的修改和移植
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
#定义编译器变量
CC := gcc
#定义链接选项,如使用多线程时需要指定-pthread
LFLAGS :=
#指定头文件搜索路径
CFLAGS := -I $(DIR_INC)
#是否支持调试选项
ifeq ($(DEBUG),true)
CFLAGS += -g
endif
#定义创建目录、删除的变量
MKDIR := mkdir
RM := rm -fr
CP := cp -rf
#最终生成的可执行文件存放目录
APP := $(DIR_BUILD)/app.out
#最终生成的so文件存放目录
TARGET := $(DIR_LIB)/libcac
#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
#去除头文件的前缀
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))
#得到.o文件,并使用patsubst将路径DIR_SRC替换为DIR_BUILD
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))
#指定源文件和头文件的搜索路径
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
#all : $(DIR_BUILD) $(APP) $(TARGET).so
all : $(DIR_BUILD) $(DIR_LIB) $(TARGET).so
@echo "Target File ==> $(APP)"
$(DIR_BUILD) $(DIR_LIB):
$(MKDIR) $@
$(TARGET).so : $(OBJS)
@echo Generating shared lib...
$(CC) -shared -fPIC -o $(TARGET).so $(OBJS)
$(APP) : $(OBJS)
$(CC) $(LFLAGS) -o $@ $^
#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
$(CC) -c -fPIC $(CFLAGS) $< -o $@
# $(CC) $(CFLAGS) -o $@ -c $<
clean :
$(RM) $(DIR_BUILD) $(DIR_LIB) *~ SI
copy :
$(CP) $(DIR_CODE) .
3.2测试
生成的动态库libcac.so拷贝到顶层目录
编写测试程序call_main.c和对应头文件caculate.h
JavaJNI测试
新建src和inc目录
src存放.c文件,inc存放.h文件,顶层目录存放Makefile文件
目录结构
JNIDemo.java
src/caculate.c
src/native.c
inc/caculate.h
Makefile
native.c
#include <jni.h> /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include "caculate.h"
#if 0
typedef struct {
char *name; /* Java里调用的函数名 */
char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */
void *fnPtr; /* C语言实现的本地函数 */
} JNINativeMethod;
#endif
void c_hello(JNIEnv *env, jobject cls)
{
printf("add(1, 2) = %d\n", add(1, 2));
printf("Hello, world!\n");
}
static const JNINativeMethod methods[] = {
{"hello", "()V", (void *)c_hello},
};
/*
1.首先实现一个JNI_OnLoad方法
只要java里面加载了这个库,就会先执行里面实现的方法
如何实现:参考jni.pdf P117
*/
/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;
//JNI_VERSION_1_4:运行环境版本
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
}
cls = (*env)->FindClass(env, "JNIDemo");//查找java里面的JNIDemo类
if (cls == NULL) {
return JNI_ERR;
}
/* 2. map java hello <-->c c_hello 建立联系*/
//这个方法来自于运行环境;参考jni.pdf P115示例
//env:环境 cls:类 methods:方法 把这个方法注册到环境里面的这个类里
if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)
return JNI_ERR;
return JNI_VERSION_1_4;
}
caculate.c
/*caculate.c文件*/
//#include "caculate.h"
//求两个数的和
int add(int a, int b)
{
return (a + b);
}
//减法
int sub(int a, int b)
{
return (a - b);
}
//除法
int div(int a, int b)
{
return (int)(a / b);
}
//乘法
int mul(int a, int b)
{
return (a * b);
}
caculate.h
/*caculate.h*/
#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
int add(int a, int b);
//减法
int sub(int a, int b);
//除法
int div(int a, int b);
//乘法
int mul(int a, int b);
#endif
Makefile
#定义伪目标
.PHONY : all clean copy
#编译存放的目录:build、源文件目录:src、头文件目录:inc
DIR_BUILD := build
DIR_LIB := lib
DIR_SRC := src
DIR_INC := inc
DIR_CODE := /mnt/hgfs/MAndroid5.1_source/x_ray/*
#使用变量方便后期makefile的修改和移植
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
#定义编译器变量
CC := gcc
#定义链接选项,如使用多线程时需要指定-pthread
LFLAGS :=
#指定头文件搜索路径
CFLAGS := -I $(DIR_INC)
CFLAGS += -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
#是否支持调试选项
ifeq ($(DEBUG),true)
CFLAGS += -g
endif
#定义创建目录、删除的变量
MKDIR := mkdir
RM := rm -fr
CP := cp -rf
#最终生成的可执行文件存放目录
APP := $(DIR_BUILD)/app.out
#最终生成的so文件存放目录
TARGET := $(DIR_LIB)/libnative
#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
#去除头文件的前缀
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))
#得到.o文件,并使用patsubst将路径DIR_SRC替换为DIR_BUILD
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))
#指定源文件和头文件的搜索路径
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
#all : $(DIR_BUILD) $(APP) $(TARGET).so
all : $(DIR_BUILD) $(DIR_LIB) $(TARGET).so
@echo "Target File ==> $(APP)"
$(DIR_BUILD) $(DIR_LIB):
$(MKDIR) $@
$(TARGET).so : $(OBJS)
@echo Generating shared lib...
$(CC) -shared -fPIC -o $(TARGET).so $(OBJS)
$(APP) : $(OBJS)
$(CC) $(LFLAGS) -o $@ $^
#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
$(CC) -c -fPIC $(CFLAGS) $< -o $@
# $(CC) $(CFLAGS) -o $@ -c $<
clean :
$(RM) $(DIR_BUILD) $(DIR_LIB) *~ SI
copy :
$(CP) $(DIR_CODE) .
测试
生成的动态库libcac.so拷贝到顶层目录
编写测试程序JNIDemo.java
public class JNIDemo {
//加载库是放在静态代码快,因为构造对象之前执行,并且只执行一次
static { /* 1. load 加载*/
System.loadLibrary("native"); /* libnative.so */
}
public native void hello(); //声明 native表示不是在java语言里面实现的,而是在本地语言实现的
public static void main (String args[]) {
JNIDemo d = new JNIDemo();
/* 2. map java hello <-->c c_hello 建立映射*/
/* 3. call 调用*/
d.hello();
}
}
javac JNIDemo.java
java JNIDemo
Android_JNI
模拟访问硬件led
目录结构
inc/caculate.h
src/caculate.c
src/hardcontrol.c
Makefile
#定义伪目标
.PHONY : all clean copy
#export PATH=/usr/local/arm/arm-2009q3/bin:$PATH
#编译存放的目录:build、源文件目录:src、头文件目录:inc
DIR_BUILD := build
DIR_LIB := lib
DIR_SRC := src
DIR_INC := inc
DIR_CODE := /mnt/hgfs/MAndroid5.1_source/lib_bak
#依赖库路径
EXTERN_LIB_PATH := /home/rpdzkj/Trk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/
#依赖库
EXTERN_LIBC := $(EXTERN_LIB_PATH)/libc.so
EXTERN_LIBLOG := $(EXTERN_LIB_PATH)/liblog.so
#使用变量方便后期makefile的修改和移植
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
#定义编译器变量
CC := arm-none-linux-gnueabi-gcc
#定义链接选项,如使用多线程时需要指定-pthread
LFLAGS :=
#指定头文件搜索路径
CFLAGS := -I $(DIR_INC)
CFLAGS += -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
CFLAGS += -I /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include
#是否支持调试选项
ifeq ($(DEBUG),true)
CFLAGS += -g
endif
#定义创建目录、删除的变量
MKDIR := mkdir
RM := rm -fr
CP := cp -rf
#最终生成的可执行文件存放目录
APP := $(DIR_BUILD)/app.out
#最终生成的so文件存放目录
TARGET := $(DIR_LIB)/libhardcontrol
#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
#去除头文件的前缀
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))
#得到.o文件,并使用patsubst将路径DIR_SRC替换为DIR_BUILD
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))
#指定源文件和头文件的搜索路径
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
#all : $(DIR_BUILD) $(APP) $(TARGET).so
all : $(DIR_BUILD) $(DIR_LIB) $(TARGET).so
@echo "Target File ==> $(APP)"
$(DIR_BUILD) $(DIR_LIB):
$(MKDIR) $@
$(TARGET).so : $(OBJS)
@echo Generating shared lib...
$(CC) -shared -fPIC -o $(TARGET).so $(OBJS) -nostdlib $(EXTERN_LIBC) $(EXTERN_LIBLOG)
$(APP) : $(OBJS)
$(CC) $(LFLAGS) -o $@ $^
#这里其实不需要$(HDRS),因为$(CFLAGS)已经指定了
$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
$(CC) -c -fPIC $< -o $@ $(CFLAGS)
# $(CC) $(CFLAGS) -o $@ -c $<
clean :
$(RM) $(DIR_BUILD) $(DIR_LIB) *~ SI
copy :
$(CP) $(DIR_LIB)/* $(DIR_CODE)
/*caculate.h*/
#ifndef CACULATE_HEAD_
#define CACULATE_HEAD_
//加法
jint add(jint a, jint b);
//减法
jint sub(jint a, jint b);
/*
//除法
jint div(jint a, jint b);
//乘法
jint mul(jint a, jint b);
*/
#endif
/*caculate.c文件*/
//#include "caculate.h"
#include <jni.h>
//求两个数的和
jint add(jint a, jint b)
{
return (a + b);
}
//减法
jint sub(jint a, jint b)
{
return (a - b);
}
//除法
jint div(jint a, jint b)
{
return (jint)(a / b);
}
//乘法
jint mul(jint a, jint b)
{
return (a * b);
}
#include <jni.h> /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#include <stdio.h>
#include <stdlib.h>
#include "caculate.h"
#include <android/log.h> /* liblog */
//__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");
/*
arm-none-linux-gnueabi-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/libc.so -I /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/include /home/rpdzkj/rk3288_5.1/prebuilts/ndk/9/platforms/android-19/arch-arm/usr/lib/liblog.so
*/
#if 0
typedef struct {
char *name; /* JavaÀïµ÷Óõĺ¯ÊýÃû */
char *signature; /* JNI×Ö¶ÎÃèÊö•û, ÓÃÀŽ±íÊŸJavaÀïµ÷Óõĺ¯ÊýµÄ²ÎÊýºÍ•µ»ØÖµÀàÐÍ */
void *fnPtr; /* CÓïÑÔʵÏֵıŸµØº¯Êý */
} JNINativeMethod;
#endif
jint ledOpen(JNIEnv *env, jobject cls)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen ...");
return 0;
}
void ledClose(JNIEnv *env, jobject cls)
{
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledClose ...");
}
jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
jint tem = add(4, 6);
__android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl tem: %d, %d, %d...", which, status, tem);
return 0;
}
static const JNINativeMethod methods[] = {
{"ledOpen", "()I", (void *)ledOpen},
{"ledClose", "()V", (void *)ledClose},
{"ledCtrl", "(II)I", (void *)ledCtrl},
};
/* System.loadLibrary */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env;
jclass cls;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {
return JNI_ERR; /* JNI version not supported */
}
cls = (*env)->FindClass(env, "com/example/hardlibrary/HardControl");
if (cls == NULL) {
return JNI_ERR;
}
/* 2. map java hello <-->c c_hello */
if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)
return JNI_ERR;
return JNI_VERSION_1_4;
}
将生成的库拷贝到AS的工程中,测试