进程间通讯之共享内存

进程间通讯之共享内存
一、引言:

管道、信号、信号量虽然满足了进程之间通讯的需要,但是还有一种没有满足,那就是进程之间需要共享大量的数据。就像一家人一样,他们彼此之间每天都在交流,但是对于家里面的一些共有物品,如电视,餐厅等等都是共享的,而我们所说的进程之间也是一样的,他们虽然互相独立存在,但是还有很多数据都是共享使用的,而进程之间的数据共享就是共享内存。

二、共享内存理论及其分析:

共享内存就是两个进程共同拥有同一片内存区域。这片区域中的内容,二者都可以访问。要使用共享内存进行通讯,一个进程首先创建一片内存空间专门作为通信使用,而其他的进程则将这片内存空间映射到自己的(虚拟)地址空间。这样就通过读写自己地址空间中对应的的共享内存区域时,就是在和其他的进程进行通信了。

通过这里的了解,是不是感觉共享内存和我们前面博客讲的管道很像呢,管道也是在一片内存区域上作为进程间通信的媒介,但是这里的共享内存和管道是不一样的,首先,使用共享内存时两个进程必须在同一台物理机器上,其次,共享内存的访问方式是随机的,而不是只能从一端写,另一端写,因此这也是共享内存的一大优点,所以共享内存传递的信息也比较多,随之而来大量信息的传递也使得传递时比较复杂。

如图所示,共享内存的图解:


那么共享内存与管道,信号量,信号等等通信有啥区别呢?
共享内存是最快的一种IPC通信方式,在各个进程都有指针指向开辟出来的内存区域,访问的时候当做进程中的一个内存控制直接操作,也就是说对于一个内存而言,共享内存就是进程的一部分,直接通过指针操作这片区域,少了内核的陷入过程;进程直接通过指针操作共享内存区域,少了用户态和内核态之间数据的拷贝。

区别图解:


但是共享内存也有它的缺点:由于共享内存是进程中通信最快的的,且信息量较大,也造成了它的管理复杂,且两个进程必须在同一台物理机器上才能进行通讯,另外一个缺点就是,安全性低,当两个进程共享一片内存区域进行通讯时,如果一个进程感染了病毒,很容易会传给(感染)另外一个进程。就像一家人一样,一个人感冒了,有些东西是公用的,到时候用的时候会很容易感染给别人,这是同样的道理。
再就是需要注意的是:使用全局变量在同一个进程的线程间实现通信不叫共享内存,它们是使用进程中的资源,线程只是进程内部的一个执行序列,后期将会进行分析说明。

共享内存使用时,是一个临界资源,所以进程之间使用必须做同步控制,而这里能更好的达到同步控制需要利用我们前面所说的信号量。

三、共享内存的使用函数:

头文件sys/types.h和sys/ipc.h被shm.h自动包含进程序。

shmget函数:
创建共享内存,获得一个共享内存标识符;
int shmget(key_t key,  size_t size,  int shmflg);
第一个参数是程序提供的一个键值,为共享内存命名,shmget函数返回一个共享内存标识符,后面的其他函数会使用。
第二个参数:size以字节为单位指定需要共享的内存容量。
第三个参数:如果共享内存存在则获取,不存在,使用IPC_CREAT创建,创建可以给设置权限标志 Onnn,这样允许创建共享内存的进程写入数据,设置其他的用户创建的进程只能以读取该共享内存。
返回值:创建成功,返回一个非负整数,即共享内存标识符,失败,则返回-1.

shmat函数:
将共享内存的段连接到进程的地址空间中。
void *shmat(int shm_id,  const *shm_addr,  int shmflg);
第一个参数shm_id是shmget函数的返回的共享内存标识符。
第二个参数shm_addr是:共享内存将要连接到当前进程中的地址位置,它通常是一个NULL,表示让系统来选择共享内存出现的地址。
第三个参数shmflg:是一组位标志,取值为:SHM_RND与shm_addr联合使用用来控制共享内存区域连续的地址        和SHM_RDONLY(它使得连续的内存只读)。
返回值:如果shmat调用成功,返回一个指向共享内存第一个字节的指针,如果失败,则返回-1.

shmdt函数:
断开连接;此函数只会断开连接,而不会删除共享内存。
int shmdt(void *addr);
addr参数是以前调用shmat函数的时的返回值,如果成功,shmdt将相关shmid_ds结构中的shm_nattch计数器值减1.

返回值:成功返回0,失败返回-1.


shmctl函数:
删除内核对象;
int shmctl(int shm_id,  int command,  struct shmid_ds *buf);
shmid_ds结构:
struct shmid_ds
{
uid_t  shm_perm.uid;
uid_t  shm_perm.gid;
mode_t  shm_perm.mode;
}
第一个参数shm_id是shmget返回的共享内存标识符
第二个参数command是要采取的动作,取以下三个值:
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET :如果进程有足够权限,把共享内存的当前关联值设置为shmid_ds结构中的值;
IPC_RMID:删除共享内存段;
第三个参数buf是一个指针,它指向包含共享内存模式和访问权限的结构。
返回值:成功时返回0;失败时返回-1;

基于intel的Linux系统上的存储区布局:



四、共享内存的测试题:


题目:A进程接收用户输入,B进程统计字符个数,遇到end结束!
代码如下:

信号量sem.c中信号量初始值为1;data.val = 1;,其他信号量封装函数见博客目录中的《进程通讯之信号量》

void sem_get()
{
	//获取信号量集
	semid = semget((key_t)1234,1,0666);
	if (semid == -1)
	{
		//完成创建信号量集
		semid = semget((key_t)1234, 1, 0666 | IPC_CREAT);
		assert(semid != -1);
		if (semid == -1)
		{
			printf("error\n");
			exit(0);
		}
		//完成初始化
		union sembuff data;
		data.val = 1;
		semctl(semid,0,SETVAL,data);

	}
}

文件:shma.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/shm.h>
#include "sem.h"

void main()
{
 	int shmid = shmget((key_t)1234,128,IPC_CREAT | 0666);
	assert(shmid != -1);
	char *ptr = shmat(shmid,NULL,0);
	assert(ptr != NULL);
	sem_get();
	while(1)
	{
		sem_p();
		printf("please input: ");
		fflush(stdout);
		fgets(ptr,128,stdin);
		if (strncmp(ptr,"end",3) == 0)
		{
			break;
		}
		ptr[strlen(ptr)-1] = 0;
		sem_v();
		
	}
 }

文件:shmb.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/shm.h>
#include "sem.h"

void main()
{
	int shmid = shmget((key_t)1234,128,IPC_CREAT | 0666);
	assert(shmid != -1);
	char *ptr = shmat(shmid,NULL,0);
	assert(ptr != NULL);
	sem_get();
	while(1)
	{
		sem_p();
		if (strncmp(ptr,"end",3) == 0)
		{
			break;
		}
		sleep(5);
		printf("size : %d\n",strlen(ptr));
		sem_v();
	}
}
Linux终端下编译命令:
shma.c文件:gcc -o  shma   shma.c  sem.c
shmb.c文件:gcc -o  shmb   shmb.c  sem.c

结果:

A进程用户:


B进程计算:


测试目的:为了更好的使用共享内存进行进程间的通讯,以及对于进程间使用共享内存熟练使用,得到加强,并且融入信号量,进程对共享内存访问时,是同步控制的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux下,进程通信的一种方式是通过共享内存来实现的。共享内存允许两个或多个进程共享一定的存储区,这样它们就可以直接访问同一块内存区域,而不需要进行数据的复制。共享内存是一种高效的进程通信方式,因为数据直接写入内存,不需要多次数据拷贝,所以传输速度很快\[2\]。 在使用共享内存进行进程通信时,需要给共享内存创建一个唯一的身份ID,以便区分不同的共享内存。当进程需要访问共享内存时,需要在映射时带上这个ID,这样就可以确定访问的是哪一个共享内存\[3\]。 需要注意的是,共享内存并没有提供同步机制,也就是说,在一个进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程开始对它进行读取。为了实现多个进程共享内存的同步访问,通常会使用信号量来实现对共享内存的同步访问控制\[2\]。 总结起来,Linux下的共享内存是一种高效的进程通信方式,允许多个进程共享一块存储区。通过给共享内存创建唯一的身份ID,可以区分不同的共享内存。然而,共享内存并没有提供同步机制,需要使用信号量来实现对共享内存的同步访问控制\[2\]\[3\]。 #### 引用[.reference_title] - *1* *3* [Linux多进程通信——共享内存实现](https://blog.csdn.net/zhm1949/article/details/124909541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Linux下进程通信方式——共享内存](https://blog.csdn.net/xujianjun229/article/details/118584955)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值