0

0

Go语言中使用System V共享内存的完整实践指南

聖光之護

聖光之護

发布时间:2026-02-24 23:47:00

|

790人浏览过

|

来源于php中文网

原创

Go语言中使用System V共享内存的完整实践指南

本文详解如何在go中通过cgo调用posix/system v共享内存api实现跨进程数据共享,涵盖shmget/shmat/shmdt/shmctl等核心操作,并提供可运行的读写示例、内存安全注意事项及替代方案建议。

本文详解如何在go中通过cgo调用posix/system v共享内存api实现跨进程数据共享,涵盖shmget/shmat/shmdt/shmctl等核心操作,并提供可运行的读写示例、内存安全注意事项及替代方案建议。

在Go语言生态中,“不要通过共享内存来通信,而要通过通信来共享内存”(Don’t communicate by sharing memory, share memory by communicating)是广为推崇的设计哲学。标准库中的channel、sync包等机制已为协程间高效、安全的数据交换提供了原生支持。然而,在跨进程场景(如Go主程序与C子进程协作、嵌入式系统IPC、或与遗留C服务集成)下,System V共享内存(shmget/shmat等)或POSIX共享内存(shm_open/mmap)仍是不可替代的底层选择。Go本身不直接封装这些系统调用,但可通过cgo安全桥接C标准库与系统API。

以下是一个基于System V共享内存的完整实践方案,使用ftok生成键值、shmget创建/获取段、shmat映射地址空间、shmdt分离、shmctl(..., IPC_RMID)清理资源:

✅ 核心C封装(wrapper.c)

为避免直接暴露复杂系统调用细节,我们封装为简洁的C函数接口:

#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>

int my_shm_open(char* filename, int open_flag) {
    key_t key = ftok(filename, 0x03);
    if (key == -1) return -1;

    int shm_id = open_flag 
        ? shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0600)  // 创建新段
        : shmget(key, 0, 0);                               // 获取已有段
    return (shm_id == -1) ? -1 : shm_id;
}

int my_shm_update(int shm_id, char* content) {
    char* addr = shmat(shm_id, NULL, 0);
    if (addr == (char*)-1) return -1;

    size_t len = strlen(content);
    if (len > 4095) { shmdt(addr); return -1; }

    strcpy(addr, content);
    shmdt(addr);
    return 0;
}

char* my_shm_read(char* filename) {
    int shm_id = my_shm_open(filename, 0);
    if (shm_id == -1) return NULL;

    char* addr = shmat(shm_id, NULL, 0);
    if (addr == (char*)-1) return NULL;

    char* s = malloc(strlen(addr) + 1);
    if (!s) { shmdt(addr); return NULL; }
    strcpy(s, addr);
    shmdt(addr);
    return s;
}

int my_shm_close(int shm_id) {
    shmctl(shm_id, IPC_RMID, NULL); // 立即销毁段(仅当最后一个进程分离后生效)
    return 0;
}

✅ Go读写器实现(需启用cgo)

注意:必须在Go文件顶部添加// #cgo LDFLAGS: -lrt(若用POSIX)或确保系统库可用;此处为System V,通常无需额外链接。

拾贝
拾贝

一键同步微信读书所有笔记和划线,并在新标签页回顾

下载

立即学习go语言免费学习笔记(深入)”;

写入端(writer.go):

package main

/*
#cgo LDFLAGS: -lc
#include <stdlib.h>
#include "wrapper.c"
*/
import "C"
import (
    "log"
    "unsafe"
    "time"
)

func openShm(file string) (int, error) {
    cfile := C.CString(file)
    defer C.free(unsafe.Pointer(cfile))
    id := int(C.my_shm_open(cfile, 1))
    if id == -1 {
        return 0, log.New(nil, "", 0).Printf("failed to create shm segment")
    }
    return id, nil
}

func writeShm(shmID int, data string) error {
    cdata := C.CString(data)
    defer C.free(unsafe.Pointer(cdata))
    if int(C.my_shm_update(C.int(shmID), cdata)) != 0 {
        return log.New(nil, "", 0).Printf("failed to write to shm")
    }
    return nil
}

func closeShm(shmID int) {
    C.my_shm_close(C.int(shmID))
}

func main() {
    id, err := openShm("/tmp/shm_example") // 路径用于ftok生成key
    if err != nil {
        log.Fatal(err)
    }
    defer closeShm(id)

    if err := writeShm(id, "Hello from Go Writer!"); err != nil {
        log.Fatal(err)
    }
    log.Println("Data written. Waiting for reader...")
    time.Sleep(5 * time.Second) // 模拟等待读者
}

读取端(reader.go):

package main

/*
#cgo LDFLAGS: -lc
#include <stdlib.h>
#include "wrapper.c"
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func readShm(file string) string {
    cfile := C.CString(file)
    defer C.free(unsafe.Pointer(cfile))
    cstr := C.my_shm_read(cfile)
    if cstr == nil {
        return ""
    }
    defer C.free(unsafe.Pointer(cstr))
    return C.GoString(cstr)
}

func main() {
    data := readShm("/tmp/shm_example")
    fmt.Printf("Read from shared memory: %s\n", data)
}

⚠️ 关键注意事项

  • 键值一致性:ftok(path, proj_id)要求所有进程使用完全相同的路径和proj_id生成key,否则无法定位同一共享段。路径不必真实存在,但需保证各进程调用时字面量一致。
  • 生命周期管理:IPC_RMID标记段为“待删除”,实际释放发生在所有进程均调用shmdt之后。避免过早shmctl(..., IPC_RMID)导致读者失效。
  • 内存安全:C侧malloc分配的内存必须由C侧free释放(如my_shm_read返回的指针),Go中不可用C.free释放非C.CString或C.CBytes分配的内存——本例中my_shm_read内部已malloc,故Go侧需C.free其返回值。
  • 竞态与同步:共享内存本身不提供同步机制!务必配合信号量(semget)、文件锁或外部协调服务,防止读写冲突。
  • 替代方案优先级
    ✅ 首选:net/rpc、HTTP API、gRPC(跨语言、易调试)
    ✅ 次选:os.Pipe、os/exec管道、Unix Domain Socket
    ⚠️ 最后选:System V / POSIX 共享内存(复杂、平台依赖、调试困难)

✅ 运行验证

# 终端1:启动写入器
go run writer.go

# 终端2:稍后执行读取器(确保写入器已创建段)
go run reader.go
# 输出:Read from shared memory: Hello from Go Writer!

综上,Go通过cgo调用System V共享内存是可行的底层IPC手段,但应严格评估必要性。务必遵循C内存管理规则、键值一致性原则,并始终将同步与错误处理纳入设计——毕竟,共享内存不是银弹,而是需要谨慎驾驭的利刃。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

207

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

242

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

349

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

212

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

405

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

365

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

200

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

1091

2025.06.17

Golang 生态工具与框架:扩展开发能力
Golang 生态工具与框架:扩展开发能力

《Golang 生态工具与框架》系统梳理 Go 语言在实际工程中的主流工具链与框架选型思路,涵盖 Web 框架、RPC 通信、依赖管理、测试工具、代码生成与项目结构设计等内容。通过真实项目场景解析不同工具的适用边界与组合方式,帮助开发者构建高效、可维护的 Go 工程体系,并提升团队协作与交付效率。

1

2026.02.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 5.5万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号