
通过JNI调用C语言函数实现与linux上其他进程进行通信的共享内存和消息队列
发布日期:2021-05-07 09:37:14
浏览次数:21
分类:精选文章
本文共 6730 字,大约阅读时间需要 22 分钟。
JDBC实践
JDBC实践
一、JNI编程基本流程
1. 定义本地本地方法
以一个简单的打印为例,定义一个本地方法,用于在Java中调用C/C++代码。
public class JNIDemo { public native int sayHello();}
2. 编译生成class文件
本地方法的头文件需要依靠class文件生成。
javac -d out JNIDemo.java
3. 根据class文件生成h文件
可以自己手动生成,也可以使用工具生成。
# 通过IDEA设置里的External Tools创建自定义工具# 使用环境的javah程序根据编译生成的class文件产生头文件
4. 编写C文件实现
根据生成的h文件编写具体实现。
#include#include JNIEXPORT int JNICALL Java_JNIDemo_sayHello(JNIEnv *env, jobject thisObj) { printf("Hello World!\n"); return 1;}
5. 编译本地方法源文件并生成共享库
#!/bin/bashgcc -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.282.b08-1.el7_9.x86_64/include/ \-I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.282.b08-1.el7_9.x86_64/include/linux/ \-fPIC -shared -o JNIDemo.so JNIDemo.c
6. 编写测试Java类
加载共享库,并在测试类中调用本地方法。
public class JNIDemo { static { System.load("/home/xd/Documents/java_workspace/jni/jni_lib/JNIDemo.so"); } public native int sayHello(); public static void main(String[] args) { int i = 0; System.out.println(new JNIDemo().sayHello()); }}
二、需要用到的JNI方法
1. 数组
获取数组区域
void GetByteArrayRegion(JNIEnv *env, jobject array, jsize start, jsize len, byte *buf);
设置数组区域
void SetByteArrayRegion(JNIEnv *env, jobject array, jsize start, jsize len, byte *buf);
获取对象属性值
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);
设置对象属性值
void SetIntField(JNIEnv *env, jobject obj, jfieldID fieldID, int value);
三、通过JNI创建本地消息队列
1. 消息队列的基本原理
进程通过约定好的key值创建消息队列,对应key返回该消息队列的qid。
2. 消息队列的基本使用
定义消息结构
struct message { long msg_type; char msg_text[MSG_MAX];};
创建消息队列
int msqid = msgget(key, IPC_CREAT | 0666);
发送消息
ret = msgsnd(msqid, &msgq, mlen, 0);
获取消息
readmslen = msgrcv(msqid, &msgq, MSG_MAX, mstype, 0);
3. 本地方法定义
public class MsgQ { public native static int msgget(int msg_key); public native static int msgsnd(int msqid, int type, byte[] msg, int len); public native static int msgrcv(int msqid, byte[] msg, int len, int type);}
4. 编写C文件实现
#include#include #include #include #include #include #include #include #define MSG_MAX 8192 void* snd_pthread(void* arg); void* recv_pthread(void* arg); int main() { int msqid; int key = 0xff; if ((msqid = msgget(key, IPC_CREAT | 0666)) == -1) { perror("[JNI ERROR]msgget Error"); } printf("main:%d\n", msqid); pthread_t pid1 = -1; pthread_t pid2 = -1; if (pthread_create(&pid1, NULL, recv_pthread, (void *)(long)msqid) != 0) { printf("fail to create a pthread"); return -1; } if (pthread_create(&pid2, NULL, snd_pthread, (void *)(long)msqid) != 0) { printf("fail to create a pthread"); return -1; } printf("%d %d \n", pid1, pid2); pthread_join(pid1, NULL); pthread_join(pid2, NULL); return 0; }
5. 编写测试Java类
import java.util.Scanner;public class MsgQ { static { System.load("/home/xd/Documents/java_workspace/jni/jni_lib/MsgQ.so"); } public native static int msgget(int msg_key); public native static int msgsnd(int msqid, int type, byte[] msg, int len); public native static int msgrcv(int msqid, byte[] msg, int len, int type); public static void main(String[] args) { new Thread(() -> { int msqid = MsgQ.msgget(0xFF); System.out.println(msqid); while (true) { Scanner s = new Scanner(System.in); String ms = s.nextLine(); System.out.println("send:" + ms); byte[] msBytes = ms.getBytes(); int ret = MsgQ.msgsnd(msqid, 9527, msBytes, msBytes.length); } }).start(); new Thread(() -> { int msqid = MsgQ.msgget(0xFF); System.out.println(msqid); while (true) { byte[] buf = new byte[1024]; int len = MsgQ.msgrcv(msqid, buf, 1024, 9528); String msr = new String(buf, 0, len); System.out.println("接收到的消息:" + msr); } }).start(); }}
四、通过JNI创建本地共享内存
1. 共享内存的基本原理
通过将/dev/shm
内存区域与进程绑定,实现进程间的通信。
2. 共享内存的基本使用
获取共享内存区域
int shmid = shmget(key, size, IPC_CREAT | 0666);
映射共享内存
char *ptr = shmat(shmid, NULL, 0);
读取共享内存
JNIEXPORT void JNICALL Java_Shm_shmRead(JNIEnv *env, jclass obj, int shmid, jbyteArray msg, int size) { char *ptr = shmat(shmid, NULL, 0); if (ptr == (void *) -1) { perror("shmat"); exit(1); } (*env)->SetByteArrayRegion(env, msg, 0, strlen(ptr), ptr); if (shmdt(ptr) < 0) { perror("shmdt"); exit(1); }}
写入共享内存
JNIEXPORT void JNICALL Java_Shm_shmWrite(JNIEnv *env, jclass obj, int shmid, jbyteArray msg, int size, int mslen) { char *ptr = shmat(shmid, NULL, 0); if (ptr == (void *) -1) { perror("shmat"); exit(1); } (*env)->GetByteArrayRegion(env, msg, 0, mslen, ptr); if (shmdt(ptr) < 0) { perror("shmdt"); exit(1); }}
删除共享内存
JNIEXPORT void JNICALL Java_Shm_shmDelete(JNIEnv *env, jclass obj, int shmid) { if (shmctl(shmid, IPC_RMID, NULL) == -1) { perror("shmctl"); exit(1); }}
3. 本地方法定义
public class Shm { public native static int shmGet(int key, int size); public native static void shmRead(int shmid, byte[] msg, int size); public native static void shmWrite(int shmid, byte[] msg, int size, int mslen); public native static void shmDelete(int shmid);}
4. 编写测试Java类
import java.nio.charset.StandardCharsets;import java.util.Scanner;public class Shm { static { System.load("/home/xd/Documents/java_workspace/jni/jni_lib/Shm.so"); } public native static int shmGet(int key, int size); public native static void shmRead(int shmid, byte[] msg, int size); public native static void shmWrite(int shmid, byte[] msg, int size, int mslen); public native static void shmDelete(int shmid); public static void main(String[] args) { int shmid = shmGet(10, 1024); System.out.println(shmid); Scanner sc = new Scanner(System.in); while (true) { String[] s = sc.nextLine().split(" ", 2); String s1 = s[0]; System.out.println(s1); if (Integer.valueOf(s1) == 0) { byte[] bytes1 = s[1].getBytes(); System.out.println(bytes1.length); shmWrite(shmid, bytes1, 1024, bytes1.length); } else if (Integer.valueOf(s1) == 1) { byte[] bytes = new byte[1024]; shmRead(shmid, bytes, 1024); System.out.println(new String(bytes)); } else if (Integer.valueOf(s1) == 2) { shmDelete(shmid); break; } } }}
五、编译并运行
通过ipcs -q
可以查询系统的消息队列情况。
ipcs -q
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2025年05月13日 11时44分45秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Elasticsearch 时区问题
2025-03-29
Elasticsearch7.3.1启动指定JDK11
2025-03-29
Elasticsearch下载安装
2025-03-29
Elasticsearch入门教程(Elasticsearch7,linux)
2025-03-29
ElasticSearch设置字段的keyword属性
2025-03-29
Elasticsearch面试题
2025-03-29
element 如何使用自定义icon图标
2025-03-29
element-plus修改主题颜色
2025-03-29
element-ui:el-input输入数字-整数和小数
2025-03-29
ElementUI-el-progress改变进度条颜色跟文字样式
2025-03-29
ELK应用日志收集实战
2025-03-29
elTable火狐浏览器换行
2025-03-29
15个Python数据处理技巧(非常详细)零基础入门到精通,收藏这一篇就够了
2025-03-29
0基础成功转行网络安全工程师,年薪30W+,经验总结都在这(建议收藏)
2025-03-29