进程间通信:共享内存概念及代码
前言
接下讨论的IPC机制,它们最初由System V版本的Unix引入。由于这些机制都出现在同一个版本中并且有着相似的编程接口,所以它们被称为System V IPC机制。接下来的内容包括:
信号量:用于管理对资源的访问。
共享内存:用于在程序之间高效地共享数据。
消息队列:在程序之间传递数据。
操作系统中的同步和异步:https://blog.csdn.net/qq_38289815/article/details/81012826
进程间通信:管道和命名管道(FIFO) https://blog.csdn.net/qq_38289815/article/details/104742682
进程间通信:信号量 https://blog.csdn.net/qq_38289815/article/details/104762940
进程间通信:消息队列 https://blog.csdn.net/qq_38289815/article/details/104786412
共享内存
共享内存允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。不同进程之间共享的内存安排为同一段物理内存。
共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
Linux数字权限含义解释
在学习Linux编程时,常会用到755、777这些数字设置权限。下面我来详细的介绍644、755、777这些数字所代表的含义。Linux系统使用9个比特的权限标志来表示文件的权限。在终端中键入ll或ls -l查看目录下的文件:
图片中所展示的数字,从左至右,1-3位数字代表文件所有者的权限,4-6位数字代表同组用户的权限,7-9数字代表其他用户的权限。具体的权限是由数字来表示的,读取(r)的权限等于4;写入(w)的权限等于2;执行(x)的权限等于1。
通过4、2、1的组合,得到以下几种权限:0 (没有权限);4 (读取权限);5 (4+1 | 读取+执行);6 (4+2 | 读取+写入);7 (4+2+1 | 读取+写入+执行)。以755为例:
1-3位7等于4+2+1,rwx,所有者具有读取、写入、执行权限;
4-6位5等于4+1+0,r-x,同组用户具有读取、执行权限但没有写入权限;
7-9位5,同上,也是r-x,其他用户具有读取、执行权限但没有写入权限。
共享内存的使用
与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。定义如下:
#include <sys/shm.h>void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);
shmget()
shmget函数用来创建共享内存,它的原型为:
int shmget(key_t key, size_t size, int shmflg);
创建成功返回一个非负整数,即共享内存标识符;失败返回-1。
第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1。
第二个参数,size以字节为单位指定需要共享的内存容量。
第三个参数,shmflg是权限标志,如果要想在key标识的共享内存不存在时创建它的话,可以与IPC_CREAT做按位或操作。共享内存的权限标志与文件的读写权限一样。
不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。
权限标志对共享内存非常有用,因为它们允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其他用户创建的进程只能读取该共享内存。我们可以利用这个功能来提供一种有效的对数据进行只读访问的方法,通过将数据放入共享内存并设置它的权限,就可以避免其他用户修改它。
shmat()
第一次创建完共享内存时,它还不能被任何进程访问,shmat()函数的作用就是启动对该共享内存的访问,并把共享内存连接到进程的地址空间。它的原型为:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
shmat调用成功时返回一个指向共享内存第一个字节的指针;调用失败时返回-1。
第一个参数,shm_id是由shmget()函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常是一个空指针,表示让系统来选择共享内存出现的地址。
第三个参数,shm_flg是一组标志位,通常为0。一般很少需要控制共享内存连接的地址,通常都是让系统来选择一个地址,否则就会使应用程序对硬件的依赖性过高。
共享内存的读写权限由它的属主(共享内存的创建者)、它的访问权限和当前进程的属主决定。共享内存的访问权限类似于文件的访问权限。这个规则有个例外,当shmflg & SHM_RDONLY(它使得连接的内存只读)为true时,此时即使该共享内存的访问权限允许写操作,它也不能被写入。
shmdt()
shmdt函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型为:
int shmdt(const void *shmaddr);
参数shmaddr是shmat()函数返回的地址指针,调用成功时返回0,失败时返回-1.
shmctl()
shmctl用来控制共享内存,它的原型为:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
成功时返回0,失败时返回-1。
第一个参数,shm_id是shmget()函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。shmid_ds结构 至少包括以下成员:
struct shmid_ds
{uid_t shm_perm.uid;uid_t shm_perm.gid;mode_t shm_perm.mode;
};
使用共享内存完成进程间通信
下面就以两个不相关的进程来说明进程间如何利用共享内存来进行通信。其中一个文件shm1.c(消费者)创建共享内存,并读取其中的信息,另一个文件shm2.c(生产者)将连接一个已有的共享内存段,向共享内存中写入数据。
#ifndef _SHM_COM_H_HEADER //shm_com.h
#define _SHM_COM_H_HEADER#define TEXT_SZ 2048struct shared_use_st
{int written_by_you; // 作为一个标志,非0:表示可读,0:表示可写char some_text[TEXT_SZ]; // 记录写入 和 读取 的文本
};#endif
#include <stddef.h> //shm1.c
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "shm_com.h"int main(int argc, char **argv)
{int running = 1;void *shared_memory = NULL;struct shared_use_st *shared_stuff; // 指向shmint shmid; // 共享内存标识符srand((unsigned int)getpid());// 创建共享内存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}// 将共享内存连接到当前进程的地址空间shared_memory = shmat(shmid, (void *)0, 0);if (shared_memory == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("\nMemory attached at %p\n", shared_memory);printf("Memory attched at %d\n", *(int*)shared_memory);// 设置共享内存shared_stuff = (struct shared_use_st*)shared_memory; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换shared_stuff->written_by_you = 0;while (running) // 读取共享内存中的数据{// 没有进程向内存写数据,有数据可读取if (shared_stuff->written_by_you == 1){printf("You wrote: %s", shared_stuff->some_text);sleep(1);// 读取完数据,设置written使共享内存段可写shared_stuff->written_by_you = 0;// 输入了 end,退出循环(程序)if (strncmp(shared_stuff->some_text, "end", 3) == 0){running = 0;}}}// 把共享内存从当前进程中分离if (shmdt(shared_memory) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}// 删除共享内存if (shmctl(shmid, IPC_RMID, 0) == -1){fprintf(stderr, "shmctl(IPC_RMID) failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);reutrn 0;
}
#include <unistd.h> //shm2.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include "shm_com.h"int main(int argc, char **argv)
{int running = 1;void *shared_memory = NULL;struct shared_use_st *shared_stuff; // 指向shmchar buffer[BUFSIZ];int shmid; // 共享内存标识符// 创建共享内存shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);if (shmid == -1){fprintf(stderr, "shmget failed\n");exit(EXIT_FAILURE);}// 将共享内存连接到当前的进程地址空间shared_memory = shmat(shmid, (void *)0, 0);if (shared_memory == (void *)-1){fprintf(stderr, "shmat failed\n");exit(EXIT_FAILURE);}printf("Memory attched at %p\n", shared_memory);printf("Memory attched at %d\n", *(int*)shared_memory);// 设置共享内存shared_stuff = (struct shared_use_st *)shared_memory;while (running) // 向共享内存中写数据{// 数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本while (shared_stuff->written_by_you == 1){sleep(1);printf("Waiting...\n");}// 向共享内存中写入数据printf("Enter some text: ");fgets(buffer, BUFSIZ, stdin);strncpy(shared_stuff->some_text, buffer, TEXT_SZ);// 写完数据,设置written使共享内存段可读shared_stuff->written_by_you = 1;// 输入了end,退出循环(程序)if (strncmp(buffer, "end", 3) == 0){running = 0;}}// 把共享内存从当前进程中分离if (shmdt(shared_memory) == -1){fprintf(stderr, "shmdt failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);return 0;
}
实验解析
第一个程序shm1创建共享内存段,然后它连接到自己的地址空间中。我们在共享内存的开始处使用一个结构体shared_use_st。该结构体中有个标志written_by_you,当共享内存中有数据写入时,就设置这个标志。这个标志被设置时,程序就从共享内存中读取文本,将它打印出来,然后清除这个标志表示已经读完数据。我们用一个特殊字符串end来退出循环。接下来,程序分离共享内存段并删除它。
第二个程序shm2使用相同的键1234来取得并连接同一个共享内存段。然后它提示用户输入一些文本。如果标志written_by_you被设置,shm2就知道客户进程还未读完上一次的数据,因此就继续等待。当其他进程清除了这个标志后,shm2写入数据并设置该标志。它还使用字符串end来终止并分离共享内存段。
注意,这里提供了非常简陋的同步标志written_by_you,它包括一个非常缺乏效率的忙等待(不停地循环)。这可以使得我们的示例比较简单,但在实际编程中,应该使用信号量或通过传递消息、生成信号的方式来提供应用程序读、写部分之间的一种更有效的同步机制。
进程间通信:共享内存概念及代码相关推荐
- 【Linux】Linux进程间通信——共享内存/消息队列/守护进程
文章目录 进程间通信--共享内存/守护进程 一, 共享内存 1. 共享内存概念 2. 共享内存使用 1. 共享内存使用步骤 2. 共享内存操作函数 3. 共享内存常用操作命令 4. 共享内存使用示例: ...
- 操作系统实验报告7:进程间通信—共享内存。实现一个带有n个单元的线性表的并发维护。
操作系统实验报告7 实验内容 实验内容:进程间通信-共享内存.实现一个带有n个单元的线性表的并发维护. 建立一个足够大的共享内存空间(lock, M),逻辑值lock用来保证同一时间只有一个进程进入M ...
- 操作系统实验报告6:进程间通信—共享内存
操作系统实验报告6 实验内容 实验内容:进程间通信-共享内存. (1).验证:编译运行课件 Lecture 08 例程代码: Linux 系统调用示例 reader-writer 问题:Algorit ...
- C# 进程间通信(共享内存)
原文:C# 进程间通信(共享内存) 进程间通信的方式有很多,常用的方式有: 1.共享内存(内存映射文件,共享内存DLL). 2.命名管道和匿名管道. 3.发送消息 本文是记录共享内存的方式进行进程间通 ...
- linux篇【9】:进程间通信(共享内存)——<后序>
目录 一.system V共享内存--先让不同的进程看到同一份资源 1.共享内存原理 监控共享内存脚本 2.创建/获取 共享内存接口-shmget函数(shared memory get) 3.参数k ...
- Linux下进程间通信--共享内存:最快的进程间通信方式
内存共享最新整理: Linux下进程间通信-共享内存 - 码到城攻共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式https://www.codecomeon.com/posts/109/ ...
- Linux基础入门--进程间通信--共享内存
Linux基础入门--进程间通信--共享内存 1.共享内存IPC原理 2.共享内存管理 1.共享内存IPC原理 共享内存进程间通信机制主要用于实现进程间大量的数据传输,共享内存是在内存单独开辟的一段内 ...
- python多进程共享内存_python 进程间通信 共享内存
python多进程通信实例分析 python多进程通信实例分析操作系统会为每一个创建的进程分配一个独立的地址空间,不同进程的地址空间是完全隔离的,因此如果不加其他的措施,他们完全感觉不到彼此的存在.那 ...
- 【Linux系统编程】进程间通信--共享内存
概述 共享内存是进程间通信中最简单的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时 ...
最新文章
- ORA-**,oracle 12c操作问题
- MATLAB常用的学习的网站
- 让Python不在mac的dock上显示火箭图标
- 【IBM Tivoli Identity Manager 学习文档】15 用户管理
- 手游运营重度化,抓好论坛专区“预热战场”
- android开发使用c+_如何在Android项目中开始使用C ++代码
- 乐视网回击贾跃亭:债务处理没有进展,先拿出57亿再说
- php 去掉无关数据,php 读取 mysql 表中的double数据,去掉多余的0
- JavaOO 常用类新增
- oracle10自动扩分区,Oracle 11g数据库的分区表扩展(按年度)
- 如何使用BootStrapDialog实现数据的添加与删除?
- Redis 安装部署
- 计算机软件产品类退税,软件产品增值税退税政策详解.doc
- GitHub中国区前100名到底是什么样的人?
- 十行代码让你的单机“影分身”,分布式训练速度快到飞起
- Matlab中一球反弹的高度,matlab数学建模2乒乓球的弹跳和罗基斯帝模型.doc
- 闲话Variable Selection和Lasso
- gpu的单位表示_现代企业中的GPU计算!
- 基于标准库函数与基于HAL库函数的stm32编程方式对比
- linux的几个发行网站