如何在Java应用中使用shmget实现无文件式共享内存?
在Java中使用
shmget实现无文件共享内存的方法 Java标准库没有直接提供对shmget这类System V共享内存API的支持,要实现无文件的共享内存,只能通过JNI(Java Native Interface)或JNA(Java Native Access)调用系统原生接口,以下是具体实现方案:
方案一:使用JNA(推荐,无需编写C代码)
JNA可以直接映射系统的C库函数,无需手动编译C代码,实现成本更低。
步骤1:引入JNA依赖
如果用Maven,在pom.xml中添加:
<dependency> <groupId>net.java.dev.jna</groupId> <artifactId>jna</artifactId> <version>5.13.0</version> </dependency>
步骤2:映射System V共享内存函数
定义接口映射libc中的shmget、shmat、shmdt、shmctl等函数:
import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Platform; import com.sun.jna.Structure; import java.util.Arrays; import java.util.List; public interface CLibrary extends Library { CLibrary INSTANCE = Native.load(Platform.isWindows() ? "msvcrt" : "c", CLibrary.class); // 创建或获取共享内存段 int shmget(int key, int size, int shmflg); // 将共享内存段附加到进程地址空间 Pointer shmat(int shmid, Pointer shmaddr, int shmflg); // 分离共享内存段 int shmdt(Pointer shmaddr); // 控制共享内存段(比如删除) int shmctl(int shmid, int cmd, ShmidDSE buf); // shmctl用的ShmidDSE结构体 class ShmidDSE extends Structure { public int shm_perm_uid; public int shm_perm_gid; public int shm_perm_mode; public int shm_segsz; public long shm_atime; public long shm_dtime; public long shm_ctime; public int shm_cpid; public int shm_lpid; public int shm_nattch; @Override protected List<String> getFieldOrder() { return Arrays.asList("shm_perm_uid", "shm_perm_gid", "shm_perm_mode", "shm_segsz", "shm_atime", "shm_dtime", "shm_ctime", "shm_cpid", "shm_lpid", "shm_nattch"); } } // 常用常量 int IPC_CREAT = 0001000; int IPC_EXCL = 0002000; int IPC_RMID = 0; int SHM_RDONLY = 010000; }
步骤3:Java中调用示例
public class ShmExample { public static void main(String[] args) { int SHM_KEY = 0x1234; // 进程间约定的共享内存键值 int SHM_SIZE = 1024; // 共享内存大小 // 创建或获取共享内存段 int shmid = CLibrary.INSTANCE.shmget(SHM_KEY, SHM_SIZE, CLibrary.IPC_CREAT | 0666); if (shmid == -1) { throw new RuntimeException("shmget failed: " + Native.getLastError()); } // 附加共享内存到进程地址空间 Pointer shmPtr = CLibrary.INSTANCE.shmat(shmid, null, 0); if (shmPtr == Pointer.NULL) { throw new RuntimeException("shmat failed: " + Native.getLastError()); } try { // 写入数据到共享内存 String data = "Hello Shared Memory"; shmPtr.setString(0, data); // 读取共享内存数据(模拟跨进程读取) String readData = shmPtr.getString(0); System.out.println("Read from shared memory: " + readData); } finally { // 分离共享内存 if (CLibrary.INSTANCE.shmdt(shmPtr) == -1) { System.err.println("shmdt failed: " + Native.getLastError()); } // 仅在最后一个进程退出时删除共享内存 // CLibrary.INSTANCE.shmctl(shmid, CLibrary.IPC_RMID, null); } } }
方案二:使用JNI(需要编写C代码)
如果需要更精细的控制或避免JNA依赖,可以用JNI实现:
步骤1:编写Java类声明Native方法
public class ShmJNI { static { System.loadLibrary("shmjni"); // 加载编译后的动态库 } public native int shmget(int key, int size, int shmflg); public native long shmat(int shmid, long shmaddr, int shmflg); public native int shmdt(long shmaddr); public native int shmctl(int shmid, int cmd, long buf); public static void main(String[] args) { ShmJNI shm = new ShmJNI(); int key = 0x1234; int size = 1024; int shmid = shm.shmget(key, size, 0666 | 01000); // 后续调用shmat、读写内存、shmdt等逻辑类似JNA方案 } }
步骤2:生成JNI头文件
执行命令:
javac -h . ShmJNI.java
会生成_ShmJNI.h头文件。
步骤3:编写C实现代码
#include "ShmJNI.h" #include <sys/ipc.h> #include <sys/shm.h> JNIEXPORT jint JNICALL Java_ShmJNI_shmget(JNIEnv *env, jobject obj, jint key, jint size, jint shmflg) { return shmget((key_t)key, size, shmflg); } JNIEXPORT jlong JNICALL Java_ShmJNI_shmat(JNIEnv *env, jobject obj, jint shmid, jlong shmaddr, jint shmflg) { void *addr = shmat(shmid, (void*)shmaddr, shmflg); if (addr == (void*)-1) { return (jlong)-1; } return (jlong)addr; } JNIEXPORT jint JNICALL Java_ShmJNI_shmdt(JNIEnv *env, jobject obj, jlong shmaddr) { return shmdt((void*)shmaddr); } JNIEXPORT jint JNICALL Java_ShmJNI_shmctl(JNIEnv *env, jobject obj, jint shmid, jint cmd, jlong buf) { return shmctl(shmid, cmd, (struct shmid_ds*)buf); }
步骤4:编译C代码为动态库
gcc -shared -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux shmjni.c -o libshmjni.so
将生成的libshmjni.so放入Java可加载路径,即可运行Java代码。
注意事项
- 共享内存键值(
key)需要在进程间约定一致,也可以用ftok生成(JNA/JNI中同样可以调用ftok函数)。 - 共享内存本身没有同步机制,多进程访问时需要配合信号量(
semget系列函数)或其他同步手段避免竞态条件。 - 共享内存段不会随进程退出自动销毁,需要显式调用
shmctl(IPC_RMID)删除,否则会残留系统资源。 - 权限设置(
shmflg中的权限位)要确保所有进程都能访问该共享内存段。
内容的提问来源于stack exchange,提问作者ThreadFrank
相关产品推荐
相关产品推荐

