04 共享内存

1. 基本概念
  • 共享内存是最快的IPC,一旦内存区映射进程序的地址空间,进程间的数据传递就不在需要涉及内核,只是再共享内存区内存取数据需要用到同步,同步的方法有:互斥锁、条件变量、读写锁、记录锁和信号量。(同步可能需要较长时间)
  • 将文件映射进程序时,这时的映射文件共享内存呈随文件系统的持续性
  • 和管道、FIFO、消息队列读写数据时的区别
    1)管道(随进程)、FIFO(随进程)、消息队列(随内核或文件系统)读写数据时(消息队列可以通过内存映射实现、这时不涉及内核)。
    在这里插入图片描述
    2)共享内存读写数据 – 无需内核
    在这里插入图片描述

2.API
2.1 内存映射文件: mmapmunmapmsync函数

1) mmap

#include <sys/mman.h>
// addr为fd被映射入程序的起始地址,通常为NULL,这样内核会自己选择起始地址
// len:程序被映射的地址长度,映射的内容从fd的offset开始len长
// prot: PROT_READ 数据可读, PROT_WRITE数据可写, PROT_EXEC数据可执行,PROT_NONE数据不可访问
// flags: MAP_PRIVATED 程序改变这段映射区内容不影响其他程序,MAP_SHARED 变动影响映射区底层数据结构,MAP_FIXED
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
	// 成功返回被映射区的起始地址,失败则返回MAP_FAILED

在这里插入图片描述
父子进程之间共享内存区的方法之一是父进程在调用fork前先指定MAP_SHARED调用mmap。

#include <semaphore.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#define	FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

// 将想要共享的数据结构放入文件中,父进程将文件映射进内存后FORK,子进程同样拥有该共享内存
int main()
{
	sem_t *sem = sem_open("/tmp1212.test", O_CREAT | O_EXCL, FILE_MODE, 1);
	if (sem == SEM_FAILED) {
		printf("sem_open fail\n");
		return -1;
	}
	sem_unlink("/tmp1212.test");
	
	// fork之前加载至共享内存区域, 借助外界内存
	int count = 0;
	int fd = open("/tmp/test1213.txt", O_RDWR | O_CREAT, FILE_MODE);
	// 1. 将想要的数据结构写入文件
	write(fd, &count, sizeof(int));
	// 2. 将文件映射到内存: 第一个参数为NULL,由内核选择开始地址,
	void *ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	close(fd);

	// 子父进程的ptr虽然不相同,但是指向的内容相同,都是共享内存区
	if (fork() == 0) {
		for (int i = 0; i < 100; i++) {
			sem_wait(sem);
			printf("child:%d\n", (*((int *)ptr))++); // 子进程的ptr
			sem_post(sem);
		}
		return 0;
	}
	
	for (int i = 0; i < 200; i++) {
		sem_wait(sem);
		printf("father:%d\n", (*((int *)ptr))++); // 父进程的ptr
		sem_post(sem);
	}
	return 0;
}

2) munmap

#include <sys/man.h>
int munmap(void *addr, size_t len); // addr是mmap返回的地址
	// 成功返回0,失败返回-1

再次使用munmap后的映射内存,则会产生SIGSEGV信号。

3) msyn

#include <sys/man.h>
// flags: MS_ASYNC异步写,由内核排入写队列,立即返回  MS_SYNC同步写,写操作完成后返回,MS_INVALIDATE;
// addr和len为映射起始位置和长度,通常为整个映射区。
int msync(void *addr, size_t len, int flags); // 成功返回0, 失败返回-1

当 使用MAP_SHARED方式映射文件至内存区后,如果内存区的内容发生变化,内核会随后将变化写回文件,如果需要立马写回文件,调用msync

#include <semaphore.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#define	FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

// 将起同步作用的信号量和共享值count放到一个数据结构助攻,再将数据结构放入文件中,父进程将文件映射进内存后FORK,子进程同样拥有该共享内存

struct myStruct{
	sem_t sem;
	int count;
} myStruct;
int main()
{
	// fork之前加载至共享内存区域, 借助外界内存
	int fd = open("/tmp/test1213_2.txt", O_RDWR | O_CREAT, FILE_MODE);
	// 1. 将想要的数据结构写入文件
	write(fd, &myStruct, sizeof(myStruct));
	// 2. 将文件映射到内存: 第一个参数为NULL,由内核选择开始地址,
	struct myStruct *ptr = (struct myStruct *)mmap(NULL, sizeof(myStruct), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	close(fd);
	// 3. 将共享内存中的数据初始化
	ptr->count = 0;
	sem_init(&ptr->sem, 1, 1);  // sem_t, shared_flag,value
	
	// 子父进程的ptr虽然不相同,但是指向的内容相同,都是共享内存区
	if (fork() == 0) {
		for (int i = 0; i < 100; i++) {
			sem_wait(&ptr->sem);
			printf("child:%d\n", (ptr->count)++); // 子进程的ptr
			sem_post(&ptr->sem);
		}
		return 0;
	}
	
	for (int i = 0; i < 200; i++) {
		sem_wait(&ptr->sem);
		printf("father:%d\n", (ptr->count)++); // 父进程的ptr
		sem_post(&ptr->sem);
	}
	return 0;
}

注意:
1)4.4BSD提供匿名内存映射
上述的内容**都需要在文件系统中创建一个额外文件**,在文件中保存数据结构,再将文件映射进程序内存中。 4.4bsd中将mmap的flags指定为MAP_SHARED|MAP_ANON, fd 指定为-1,offset将忽略,这样内存初始化为0
2)读取共享内存时注意:
A. 内核保护时以页为单位的,如果一页为4096,写入5000个字节,可以读取两页也就是8192个字节,超过8192则会产生SIGSEGV信号。
B. 当mmap内存超过文件大小时,如mmap10000,文件5000个字节,则访问0-8192都是合法的,8192~10000则会产生SIGBUS信号,大于10000则会产生的4096倍内存就会产生SIGSEGV信号


2.2 Posix 共享内存: shm_open, shm_unlink
  1. POSIX共享内存区
    1)内存映射文件: open打开文件,再由mmap把得到的文件描述符映射到进程中。
    2)共享内存区对象shm_open打开一个Posix IPC名字(也可以是文件系统中的一个路径名),返回描述符给mmap使用。

  2. 注意:编译的时候需要加 -lrt

  3. 共享内存区对象的操作:

#include <sys/mman.h>
// oflag: O_CREAT,O_RDONLY,O_WRONLY,O_RDWR,O_EXCL  mode权限
int shm_open(const char *name, int oflag, mode_t mode); // 成功返回非负的描述符,失败返回-1
int shm_unlink(const char *name); // 成功返回0, 失败返回-1; 删除一个共享内存区名字
  1. 对描述符的操作,指定新创建的共享内存区对象的大小。
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int ftruncate(int fd, off_t len); // 指定创建新的共享内存对象大小,或修改老的共享内存对象大小。
int fstat(int fd, struct stat *buf); // 获取该fd对象的属性。
  1. demo
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <semaphore.h>
#include <fcntl.h>

#define	FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

// 共享内存区 基本使用
int main()
{
	// 1. shm_open
	int fd = shm_open("/test1214.txt", O_CREAT | O_RDWR, FILE_MODE);
	if (fd == -1) {
		printf("shm_open fail\n");
		return -1;
	}
	
	// 2. ftruncate
	if (ftruncate(fd, 1000) != 0) {
		printf("ftruncate fail\n");
		return -1;
	}
	
	// 3. mmap
	void *ptr = nullptr;
	ptr = mmap(NULL, 1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (ptr == MAP_FAILED) {
		printf("mmap fail\n");
		return -1;
	}
	// 关闭fd
	close(fd);
	// 向共享内存区写数据
	*((int *)ptr) = 1;
	
	// . shm_unlink
	/*
	if (shm_unlink("/test1214.txt") !=0) {
		printf("shm_unlink fail\n");
		return -1;
	}
	*/
	
	return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <fcntl.h>

#define	FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

// 共享内存区映射到不同程序的地址空间
int main()
{
	char str[] = "/test1214.txt.2";
	// 1. shm_open fd1
	int fd1 = shm_open(str, O_RDWR | O_CREAT, FILE_MODE);
	if (fd1 == -1) {
		printf("shm_open  fd1 fail\n");
		return -1;
	}
	// 2. ftruncate fd1
	if (ftruncate(fd1, sizeof(int)) != 0) {
		printf("ftruncate fail\n");
		return -1;
	}
	// 3. open fd2
	int fd2 = open("test1214.txt.22", O_CREAT | O_RDONLY, FILE_MODE);
	if (fd2 == -1) {
		printf("open fd2 fail\n");
		close(fd2);
		return -1;
	}
	struct stat stat; // #include <sys/stat.h>
	fstat(fd2, &stat);
	
	// 4. fork
	pid_t childPid;
	if ((childPid = fork()) == 0) {
		int *ptr2 = (int *)mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd2, 0);
		int *ptr1 = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED,  fd1, 0);
		printf("child : shm ptr2 = %p, ptr1 = %p\n", ptr2, ptr1);
		sleep(2);
		printf("ptr1 int = %d\n", *ptr1);
		return 0;
	}
	printf("begin\n");
	int *ptr4 = (int *)mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd2, 0);
	int *ptr3 = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED,  fd1, 0);
	printf("father: shm ptr4 = %p, ptr3 = %p\n", ptr4, ptr3);
	*ptr3 = 233;
	
	waitpid(childPid, NULL, 0); //#include <sys/wait.h>
	return 0;
}

总结:基本套路

服务端
	1. shm_unlink(IPC name) 删除可能仍然存在的共享内存区对象
	2. shm_opem((IPC name) 创建或打开一个共享内存区对象,返回描述符
	3. ftruncate 对返回的描述符做长度处理
	4. mmap 将这个对象映入进程序空间
	5. close 关闭这个描述符 
客户端
	1. shm_open 打开一个共享内存区对象,返回描述符
	2. mmap 将这个对象映入进程序空间
	3. close 关闭这个描述符 

2.3 Posix共享内存特点

Posix共享内存对象用来在无关进程间共享一块内存区域无需创建一个底层的磁盘文件shm_open基于内存的文件系统中创建一个文件用来给mmap提供文件描述符,注意基于内存与基于磁盘创建的文件不同点,基于内存具有内核持续性,基于磁盘具有文件持续性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用以下命令来查看 Linux 系统的内存大小: 1. free 命令 使用 free 命令可以查看系统的内存使用情况,包括总内存、已用内存、空闲内存等信息。 命令格式: ``` free [-h] ``` 示例输出: ``` total used free shared buff/cache available Mem: 7889 2767 2430 103 1692 4604 Swap: 2047 0 2047 ``` 其中, - total 表示总内存大小; - used 表示已使用的内存大小; - free 表示空闲的内存大小; - shared 表示被共享使用的内存大小; - buff/cache 表示被缓存的内存大小; - available 表示还可以被使用的内存大小。 2. top 命令 使用 top 命令可以查看系统的实时运行情况,包括内存使用情况、进程情况等。 命令格式: ``` top ``` 示例输出: ``` top - 09:57:47 up 23 days, 23:54, 1 user, load average: 0.00, 0.00, 0.00 Tasks: 88 total, 1 running, 87 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st MiB Mem : 7889.8 total, 2430.9 free, 2768.8 used, 1690.2 buff/cache MiB Swap: 2047.0 total, 2047.0 free, 0.0 used. 4606.7 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1 root 20 0 168972 10508 7088 S 0.0 0.1 0:04.68 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd 3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp 4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par_gp 6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H 8 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq 9 root 20 0 40544 3892 2996 S 0.0 0.0 0:00.05 systemd-j+ 10 root 20 0 94564 3860 2608 S 0.0 0.0 0:00.00 systemd-t+ ``` 其中, - MiB Mem 表示内存大小; - total 表示总内存大小; - free 表示空闲的内存大小; - used 表示已使用的内存大小; - buff/cache 表示被缓存的内存大小; - avail Mem 表示还可以被使用的内存大小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值