关于在 Linux 下多个不相干的进程互斥访问同一片共享内存的问题
转载请注明来源:https://www.cnblogs.com/hookjc/
这里的“不相干”,定义为:
- 这几个进程没有父子关系,也没有 Server/Client 关系
- 这一片共享内存一开始不存在,第一个要访问它的进程负责新建
- 也没有额外的 daemon 进程能管理这事情
看上去这是一个很简单的问题,实际上不简单。有两大问题:
进程在持有互斥锁的时候异常退出
如果用传统 IPC 的 semget 那套接口,是没法解决的。实测发现,down 了以后进程退出,信号量的数值依然保持不变。
用 pthread (2013年的)新特性可以解决。在创建 pthread mutex 的时候,指定为 ROBUST 模式。
pthread_mutexattr_t ma;
pthread_mutexattr_init(&ma);
pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&c->lock, &ma);
注意,pthread 是可以用于多进程的。指定 PTHREAD_PROCESS_SHARED 即可。
关于 ROBUST,官方解释在:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutexattr_setrobust.html
需要注意的地方是:
如果持有 mutex 的线程退出,另外一个线程在 pthread_mutex_lock 的时候会返回 EOWNERDEAD。这时候你需要调用 pthread_mutex_consistent 函数来清除这种状态,否则后果自负。
写成代码就是这样子:
int r = pthread_mutex_lock(lock);
if (r == EOWNERDEAD)
pthread_mutex_consistent(lock);
所以要使用这个新特新的话,需要比较新的 GCC ,要 2013 年以后的版本。
好了第一个问题解决了。我们可以在初始化共享内存的时候,新建一个这样的 pthread mutex。但是问题又来了:
怎样用原子操作新建并初始化这一片共享内存?
这个问题看上去简单至极,不过如果用这样子的代码:
void *p = get_shared_mem();
if (p == NULL)
p = create_shared_mem_and_init_mutex();
lock_shared_mem(p);
....
是不严谨的。如果共享内存初始化成全 0,那可能碰巧还可以。但我们的 mutex 也是放到共享内存里面的,是需要 init 的。
想象一下四个进程同时执行这段代码,很可能某两个进程发现共享内存不存在,然后同时新建并初始化信号量。某一个 lock 了 mutex,然后另外一个又 init mutex,就乱了。
可见,在 init mutex 之前,我们就已经需要 mutex 了。问题是,哪来这样的 mutex?前面已经说了传统 IPC 没法解决第一个问题,所以也不能用它。
其实,Linux 的文件系统本身就有这样的功能。
首先 shm_open 那一系列的函数是和文件系统关联上的。
~ ll /dev/shm/
其实 /dev/shm 是一个 mount 了的文件系统。这里面放的就是一堆通过 shm_open 新建的共享内存。都是以文件的形式展现出来。可以 rm,rename,link 各种文件操作。
其实 link 函数,也就是硬链接。是完成“原子操作”的关键所在。
搞过汇编的可能知道 CMPXCHG 这类(两个数比较,符合条件则交换)指令,是原子操作内存的最底层指令,最底层的信号量是通过它实现的。
而 link 系统调用,类似的,是系统调用级,原子操作文件的最底层指令。处于 link 操作中的进程即便被 kill 掉,在内核中也会完成最后一次这次系统调用,对文件不会有影响,不存在 “link 了一半” 这种状态,它是“原子”的。
伪代码如下:
shm_open("ourshm_tmp", ...);
// ... 初始化 ourshm_tmp 副本 ...
if (link("/dev/shm/ourshm_tmp", "/dev/shm/ourshm") == 0) {
// 我成功创建了这片共享内存
} else {
// 别人已经创建了
}
shm_unlink("ourshm_tmp");
首先新建初始化一份副本。然后用 link 函数。
最后无论如何都要 unlink 掉副本。
开源项目 kbz-event
这两种方法,貌似在各类经典书籍中都没提及,因为是 2013 年新出的,也是因为 Unix 鼓励用管道进行这类通信的原因。
在同类开源项目中。D-Bus 用的是另外的 daemon 进程去管理 socket。Android 的 IPC 则用了另外的内核模块(netlink 接口)来完成。
总之,都是用了额外的接口。
因此作者开发了不需要额外 daemon 的轻量级 IPC 通信框架 kbz-event。
来源:python脚本自动迁移
关于在 Linux 下多个不相干的进程互斥访问同一片共享内存的问题的更多相关文章
- linux下如何批量杀JAVA进程或某个进程方法
linux下如何批量杀JAVA进程或某个进程方法 在工作中经常需要停止JAVA进程,停止时间也比较长,那么有时候因为一些情况,需要把 linux 下JAVA所有进程 kill 掉,又不能用killal ...
- Linux下,如何监控某个进程到底向哪个地址发起了网络调用
Linux下,如何监控某个进程到底向哪个地址发起了网络调用 有时候,有些应用,比如idea,你发起某个操作时,其底层会去请求网络,获取一些数据. 但是不知道,请求了什么地址.举个例子,在idea中,m ...
- Linux进程通信之System V共享内存
前面已经介绍过了POSIX共享内存区,System V共享内存区在概念上类似POSIX共享内存区,POSIX共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而Sys ...
- 【Linux 应用编程】进程管理 - 进程间通信IPC之共享内存 mmap
IPC(InterProcess Communication,进程间通信)是进程中的重要概念.Linux 进程之间常用的通信方式有: 文件:简单,低效,需要代码控制同步 管道:使用简单,默认阻塞 匿名 ...
- 阐述linux IPC(五岁以下儿童):system V共享内存
[版权声明:尊重原创.转载请保留源:blog.csdn.net/shallnet 要么 .../gentleliu,文章学习交流,不用于商业用途] system V共享内存和posix ...
- linux进程间的通信之 共享内存
一.共享内存介绍 共享内存是三个IPC(Inter-Process Communication)机制中的一个. 它允许两个不相关的进程访问同一个逻辑内存. 共享内存是在两个正在进行的进程之间传递数据的 ...
- Linux进程IPC浅析[进程间通信SystemV共享内存]
Linux进程IPC浅析[进程间通信SystemV共享内存] 共享内存概念,概述 共享内存的相关函数 共享内存概念,概述: 共享内存区域是被多个进程共享的一部分物理内存 多个进程都可把该共享内存映射到 ...
- ASP.NET Core Linux下为 dotnet 创建守护进程(必备知识)
前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 asp.net core 应用程序,本篇主要是怎么样为我们在 Linux 或者 macOs 中部署的 dotnet 程序创建一个守护进程 ...
- ASP.ENT Core Linux 下 为 donet创建守护进程(转载)
原文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-supervisor.html 前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 ...
随机推荐
- [CodeLife]记毕业后第一份工作
记毕业后第一份工作与公司 写在前面--前言 已然临近21年五月,很快又是一年毕业季了,公司里来了应届的新人,忽然才意识到自己已经不是公司年龄最小的了((笑~).依稀还记得两年前,自己也是如他们那般青涩 ...
- Java初学者作业——实现控制台的猜数字游戏。游戏运行时产生一个1~100之间的随机数字
返回本章节 返回作业目录 需求说明: (1)实现控制台的猜数字游戏.游戏运行时产生一个1-100之间的随机数字 (2)要求用户从控制台输入数字,若输入的数字比随机数小,则输出"太小了,再大一 ...
- 云南农职《JavaScript交互式网页设计》 综合机试试卷①——实现购物车的结算
一.语言和环境 实现语言:javascript.html.css. 开发环境:HBuilder. 二.题目2(100分) 1.功能需求: 马上过节了,电商网站要进行促销活动,需要实现该商城购物车的商品 ...
- games101 - 4 - Ray Tracing
games101 - 4 - Ray Tracing 目录 games101 - 4 - Ray Tracing 为什么需要Ray Tracing Recursive (Whitted-Style) ...
- TypeScript中文教程基础部分上----翻译自TS官方
为什么使用TS? js中每一个值在不同的操作运行中表现出一系列不同的行为,比如说下面这个例子: message.toLowerCase();message(); 逐行看下,第一行调用了message的 ...
- linux 之 非root用户安装mysql5.7.27
下载 下载 mysql-5.7.27-linux-glibc2.12-x86_64.tar.gz 详见linux(CentOS7) 之 MySQL 5.7.30 下载及安装. 配置规划 用户: zhj ...
- vue2.0多页面开发
我们平常用vue开发的时候总觉得vue好像就是专门为了单页面应用而诞生的,其实不是.因为vue在工程化开发的时候很依赖webpack,而webpack是将所有的资源整合到一块,弄成一个单页面.但是vu ...
- Mybatis配置解析(核心配置文件)
4.配置解析 4.1.核心配置文件 Mybatis的配置文件包含了会深深影响mybatis行为的设置和属性信息 mybatis-config.xml properties(属性)重点 settings ...
- linux高大上命令001
[ linux命令 sed ] sed是一个管道命令,可以分析standard input 而且sed可以将数据进行替换.删除.新增.选取的特定的功能 sed [-nefr] [动作] -n ...
- 自旋锁-JUC系列
公众号原文:自旋锁-JUC系列 前言 2022!这个年份现在看起来都觉得有那么些恍惚的未来感,然而现在已在脚下. 无边落木萧萧下, 不尽长江滚滚来! 人生如白驹过隙! 本来计划最近把AQS源码分析做了 ...