150-Linux的共享内存(与信号量结合)

共享内存

共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中所有进程都可以访问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
管道是把数据写在第三方空间
而共享内存是共用一块内存空间,直接用就可以,不用拷贝

在这里插入图片描述
使用方法:

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

/*
shmget()用于创建或者获取共享内存
shmget()成功返回共享内存的 ID,失败返回-1
key:不同的进程使用相同的 key 值可以获取到同一个共享内存
size:创建共享内存时,指定要申请的共享内存空间大小
shmflg: IPC_CREAT IPC_EXCL
*/
int shmget(key_t key, size_t size, int shmflg);


/*
shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上,就是把箭头指向共享内存空间
shmat()成功返回返回共享内存的首地址,失败返回 NULL
shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间
shmflg: 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写
*/
void* shmat(int shmid, const void *shmaddr, int shmflg);
//shmid: shmget()成功返回共享内存的 ID

/*
shmdt()断开当前进程的 shmaddr 指向的共享内存映射,就是把箭头切断,不是删除共享内存空间
shmdt()成功返回 0, 失败返回-1
*/
int shmdt(const void *shmaddr);


/*
shmctl()控制共享内存
shmctl()成功返回 0,失败返回-1
cmd: IPC_RMID
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

可以使用命令查看
在这里插入图片描述
在这里插入图片描述
创建共享内存
vi a.c
在这里插入图片描述
运行a程序前先查看现在有几个共享内存
在这里插入图片描述
运行a程序后查看
在这里插入图片描述
已经创建了一个共享内存。没有人使用它。(进程结束)
接下来实现映射
vi a.c
在这里插入图片描述
运行a程序中
在这里插入图片描述
可以看到有一个进程在使用这个共享内存(就是a程序)
a进程结束后查看
在这里插入图片描述
数字变为0
进程一结束,自动断开

如果要人工断开,看下面这段代码
在这里插入图片描述
在执行睡眠20秒之前,a进程就会与共享内存断开了
在这里插入图片描述
vi b.c(执行读取)
在这里插入图片描述
运行b程序
在这里插入图片描述
和管道有区别:如果是管道读取的话,读过去数据就消失了
而从共享内存读数据,数据是不会消失的

s指针指向共享内存的起始地址,每次都是从起始位置读取

共享内存空间大小是由自己定的,也可以是结构体类型大小
每次映射都是指针指向共享内存空间的起始位置地址

实现:
进程 a 向共享内存中写入数据,进程 b 从共享内存中读取数据并显示
vi a.c
在这里插入图片描述
vi b.c
在这里插入图片描述
打开两个终端,分别运行a程序和b程序
在这里插入图片描述
在这里插入图片描述
如何实现:

进程 a 从键盘循环获取数据并拷贝到共享内存中,进程 b 从共享内存中获 取并打印数据。要求进程 a 输入一次,进程 b 输出一次,进程 a 不输入,进程 b 也不输出。

引入信号量进行同步!

定义2个信号量s1,s2
s1=1,s2=0;
在a进程做:P(s1),V(s2)
在b进程做:P(s2),V(s1)
在这里插入图片描述
不管a还是b先执行起来都没有影响
如果b先执行,s2=0,b执行P(s2),就阻塞住了
如果a先执行,s1=1,a执行P(s1),可以通过,(这里的s1由1变为0),运行下去,往里写数据,要进入下一轮的写入的时候,进行了V(s2)操作,s2的值从0,变成1,b程序P(s2)可以通过了,(s2的值由1变为0),执行打印,b程序正在打印的过程中a程序不能写入(因为此时s1=0),只有b程序printf打印结束后,执行V(s1),s1的值由0变为1,a程序执行P(s1)通过了,a运行下去,此时b程序被阻塞住了(因为s2=0)

vi sem.h

#ifndef __SEM_H
#define __SEM_H

#define SEM_W 0
#define SEM_R 1

typedef union SemUn
{
    
    
	int val;
}SemUn;

int SemGet(int key, int semVal[], int nsems);

int SemP(int semid, int index);

int SemV(int semid, int index);

int SemDel(int semid);

vi sem.c

#include "sem.h"

#include <sys/sem.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>


int SemGet(int key, int semVal[], int nsems)
{
    
    
	int semid = semget((key_t)key, nsems, 0664);
	if(semid == -1)
	{
    
    
		semid = semget((key_t)key, nsems, 0664|IPC_CREAT);
		if(semid == -1)
		{
    
    
			return -1;
		}

		int i = 0;
		for(; i < nsems; ++i)
		{
    
    
			SemUn un;
			un.val = semVal[i];

			if(-1 == semctl(semid, i, SETVAL, un))
			{
    
    
				perror("semctl error");
				return -1;
			}
		}
	}

	return semid;
}

int SemP(int semid, int index)
{
    
    
	struct sembuf buf;
	buf.sem_num = index;
	buf.sem_op = -1;
	buf.sem_flg = SEM_UNDO;
	if(-1 == semop(semid, &buf, 1))
	{
    
    
		return -1;
	}

	return 0;
}

int SemV(int semid, int index)
{
    
    
	struct sembuf buf;
	buf.sem_num = index;
	buf.sem_op = 1;
	buf.sem_flg = SEM_UNDO;

	if(-1 == semop(semid, &buf, 1))
	{
    
    
		perror("semop error");
		return -1;
	}

	return 0;
}

int SemDel(int semid)
{
    
    
	if(-1 == semctl(semid, 0, IPC_RMID))
	{
    
    
		perror("semctl del error");
		return -1;
	}

	return 0;
}

vi shmA.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>

#include <sys/shm.h>
#include "sem.h"

int main()
{
    
    
	int shmid = shmget((key_t) 1234, 128, IPC_CREAT | 0664);
	assert(shmid != -1);
	int semVal[2] ={
    
    1, 0};
	int semid = SemGet(1000, semVal, 2);

	char *ptr = (char*)shmat(shmid, NULL, 0);
	assert(ptr != (char*)-1);

	while(1)
	{
    
    
		char buff[128] = {
    
    0};
		printf("please input: ");
		fgets(buff, 128, stdin);
		SemP(semid, SEM_W);
		strcpy(ptr, buff);

		SemV(semid, SEM_R);

		if(strncmp(ptr, "end", 3) == 0)
		{
    
    
			break;
		}
	}

	shmdt(ptr);
	shmctl(shmid, IPC_RMID, NULL);
	exit(0);
}

vi shmB.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>

#include <sys/shm.h>
#include "sem.h"

int main()
{
    
    
	int shmid = shmget((key_t) 1234, 128, IPC_CREAT | 0664);
	assert(-1 != shmid);

	int semVal[2] = {
    
    1, 0};
	int semid = SemGet(1000, semVal, 2);

	char *ptr = (char *)shmat(shmid, NULL, 0);
	assert((char*)-1 != ptr);

	while(1)
	{
    
    
		SemP(semid, SEM_R);

		if(strncmp(ptr, "end", 3) == 0)
		{
    
    
			break;
		}

		printf("lenth = %d : %s", strlen(ptr) - 1, ptr);
		SemV(semid, SEM_W);
	}

	shmdt(ptr);
	shmctl(shmid, IPC_RMID, NULL);
	SemDel(semid);
	exit(0);
}

编译过程
在这里插入图片描述
运行结果截图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/LINZEYU666/article/details/113125039