You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 06:55:12