通过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/bash
gcc -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
上一篇:System V共享内存的基本使用与实现
下一篇:小米商城项目分析(上)

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年05月13日 11时44分45秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

Elasticsearch 之(16)_filter执行原理深度剖析(bitset机制与caching机制) 2025-03-29
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配置文件里的一些坑 [Failed to load settings from [elasticsearch.yml]] 2025-03-29
Elasticsearch面试题 2025-03-29
element ui 时间日期选择器 el-date-picker 报错 Prop being mutated “placement“ 2025-03-29
element 如何使用自定义icon图标 2025-03-29
element-plus修改主题颜色 2025-03-29
element-plus的el-date-picker日期范围选择控件,根据开始日期限定结束日期的可选范围为开始日期到开始日期+30天 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
2023年深信服、奇安信、360等大厂网络安全校招面试真题合集(附答案),让你面试轻松无压力! 2025-03-29
2024年全国程序员平均薪资排名:同样是程序员,为什么差这么多?零基础到精通,收藏这篇就够了 2025-03-29
0基础成功转行网络安全工程师,年薪30W+,经验总结都在这(建议收藏) 2025-03-29