共享内存
共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是由 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);
}
编译过程
运行结果截图