Linux——进程间通信(共享内存shm)笔记


前言

  共享内存是一种最为高效的进程间通信方式。因为进程可以直接读写内存,不需要任何数据的拷贝。为了在多个进程间交换信息,内核专门留出了一块内存区。这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。因此,进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高了效率。

  共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,如互斥锁和信号量等。


一、共享内存的通信原理

  在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

  共享内存的通信原理示意图:
在这里插入图片描述

二、共享内存函数

1.共享内存实现步骤

  1. 创建共享内存  &emsp:shmget( )
  2. 映射共享内存 &emsp: shmat( )
  3. 撤销映射 &emsp: shmdt( )

2.函数的说明

1.shmget( )函数

头文件:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

函数原型:

int shmget(key_t key,int size,int shmflg)

函数参数


【Key】:共享内存的键值,多个进程可以通过它,来访问同一个共享内存;其中特殊的值IPC_PRIVATE,用于创建当前进程的私有共享内存, 多用于父子进程间。

【 size】:共享内存区大小 。

【Shmflg】:同 open 函数的权限位,也可以用八进制表示法


返回值


  成功:共享内存段标识符
  出错:-1


2.shmat( )函数

在这里插入图片描述

3.shmdt( )函数

在这里插入图片描述

三、代码示例:

共享内存并未提供同步机制,所以需要额外的实现不同进程之间的同步,为了简单,本例中用标志字符串来实现简单的父子进程间的同步。


/* shmem.c */

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

#define BUFFER_SIZE 2048

int main()
{
	pid_t pid;
	int shmid;
	char *shm_addr;
	char flag[] = "WROTE";
	char buff[2048];
	
	/* 创建共享内存 */
	if ((shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666)) < 0)
	{
		perror("shmget");
		exit(1);
	}
	else
	{
		printf("Create shared-memory: %d\n",shmid);
	}
	
	/* 显示共享内存情况 */
	system("ipcs -m");
	
	pid = fork();
	if (pid == -1)
	{
		perror("fork");
		exit(1);
	}
	else if (pid == 0) /* 子进程处理 */
	{
		/*映射共享内存*/
		if ((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
		{
			perror("Child: shmat");
			exit(1);
		}
		else
		{
			printf("Child: Attach shared-memory: %p\n", shm_addr);
		}
		system("ipcs -m");
		
		/* 通过检查在共享内存的头部是否标志字符串"WROTE"来确认父进程已经向共享内存写入有效数据 */
		while (strncmp(shm_addr, flag, strlen(flag)))
		{
			printf("Child: Wait for enable data...\n");
			sleep(5);
		}
		
		/* 获取共享内存的有效数据并显示 */
		strcpy(buff, shm_addr + strlen(flag));
		printf("Child: Shared-memory :%s\n", buff);
		
		/* 解除共享内存映射 */
		if ((shmdt(shm_addr)) < 0)
		{
			perror("shmdt");
			exit(1);
		}
		else
		{
			printf("Child: Deattach shared-memory\n");
		}
		
	  	system("ipcs -m");
	  	
	  	if (shmctl(shmid, IPC_RMID, NULL) == -1)
		{
			perror("Child: shmctl(IPC_RMID)\n");
			exit(1);
		}
		else
		{
			printf("Delete shared-memory\n");
		}
		
		system("ipcs -m");
	}
	else /* 父进程处理 */
	{
		/*映射共享内存*/
		if ((shm_addr = shmat(shmid, 0, 0)) == (void*)-1)
		{
			perror("Parent: shmat");
			exit(1);
		}
		else
		{
			printf("Parent: Attach shared-memory: %p\n", shm_addr);
		}
		
		sleep(1);
		printf("\nInput some string:\n");
		fgets(buff, BUFFER_SIZE, stdin);
		strncpy(shm_addr + strlen(flag), buff, strlen(buff));
		strncpy(shm_addr, flag, strlen(flag));
		
		/* 解除共享内存映射 */
		if ((shmdt(shm_addr)) < 0)
		{
			perror("Parent: shmdt");
			exit(1);
		}
		else
		{
			printf("Parent: Deattach shared-memory\n");
		}
		system("ipcs -m");
		
		waitpid(pid, NULL, 0);		
		printf("Finished\n");
	}

  	exit(0);
}

执行结果显示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

本方法是通过访问物理内存来实现的进程间通信。

通过上述三个函数的配合可以实现进程间通信。但是,其本身是不具备同步机制的,需要额外的提供同步机制。

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux共享内存通信机制是一种进程间通信(IPC)方式,可以让多个进程共享同一块内存区域,从而实现高效的进程间通信。下面是一个简单的示例,演示如何使用共享内存通信机制实现两个进程间的通信: 1. 创建一个共享内存区域: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/shm.h> /* 共享内存相关的头文件 */ #define SHM_SIZE 1024 /* 定义共享内存区域的大小 */ int main() { int shmid; /* 共享内存标识符 */ char *shmaddr; /* 共享内存地址 */ key_t key = 1234; /* 定义共享内存的键值 */ /* 创建共享内存 */ if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666)) < 0) { perror("shmget error"); exit(EXIT_FAILURE); } /* 将共享内存映射到进程地址空间 */ if ((shmaddr = shmat(shmid, NULL, 0)) == (char *) -1) { perror("shmat error"); exit(EXIT_FAILURE); } /* 向共享内存写入数据 */ strcpy(shmaddr, "Hello, world!"); /* 解除共享内存映射 */ if (shmdt(shmaddr) < 0) { perror("shmdt error"); exit(EXIT_FAILURE); } return 0; } ``` 2. 读取共享内存区域中的数据: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/shm.h> /* 共享内存相关的头文件 */ #define SHM_SIZE 1024 /* 定义共享内存区域的大小 */ int main() { int shmid; /* 共享内存标识符 */ char *shmaddr; /* 共享内存地址 */ key_t key = 1234; /* 定义共享内存的键值 */ /* 获取共享内存标识符 */ if ((shmid = shmget(key, SHM_SIZE, 0666)) < 0) { perror("shmget error"); exit(EXIT_FAILURE); } /* 将共享内存映射到进程地址空间 */ if ((shmaddr = shmat(shmid, NULL, 0)) == (char *) -1) { perror("shmat error"); exit(EXIT_FAILURE); } /* 读取共享内存中的数据 */ printf("%s\n", shmaddr); /* 解除共享内存映射 */ if (shmdt(shmaddr) < 0) { perror("shmdt error"); exit(EXIT_FAILURE); } /* 删除共享内存 */ if (shmctl(shmid, IPC_RMID, 0) < 0) { perror("shmctl error"); exit(EXIT_FAILURE); } return 0; } ``` 以上示例中,第一个进程创建了一个共享内存区域,并向其中写入了数据;第二个进程获取了共享内存标识符,并将共享内存映射到自己的地址空间,然后读取了共享内存中的数据。需要注意的是,在使用完共享内存后,需要通过`shmdt()`函数解除共享内存与进程地址空间的映射关系,并通过`shmctl()`函数删除共享内存区域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值