1、进程的地址空间都是独立,受保护的。
2、A、B进程有一块逻辑地址空间共同映射到同一块物理内存上,作为共享内存。用内核对象来保存。
得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符
(1)原型:
int shmget(key_t key, size_t size, int flg);
(2)key:共享内存标识
(3)size:共享内存大小
例如:申请128字节会映射到4k大小的物理内存,但是使用也只是使用128字节,其他的用不了,但内核分配是分配一页的大小。
把共享内存区对象映射到调用进程的地址空间
(1)原型:
void *shmat(int shmid, const void *shmaddr, int shmflg)
(2)shmid:共享内存标识符
(3)shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
(4)shmflg:SHM_RDONLY:为只读模式,其他为读写模式
(5)返回值:返回一个虚拟地址,此虚拟地址就是共享内存的首地址。
断开共享内存连接
(1)原型:
int shmdt(const void *shmaddr)
(2)shmaddr:连接的共享内存的起始地址
控制共享内存
(1)原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
(2) msqid:共享内存标识符
(3)cmd
它是A、B进程指针指向共享内存,其实就是将地址映射到共享内存上,所以就可以通过指针访问共享内存的内容。不用进行拷贝。
例如:A进程获取内存数据,B进程打印内存数据。
即B进程必须在A进程获取数据之后才能打印,A进程必须在B进程将上一次数据处理后才能再次获取。
因为两个进程是相互影响的,所以要用两个信号量来实现。
共享内存必须用信号量去做支持做进程同步
所以要用到信号量的实现
//sem.h
#pragma once
#include
union semval
{
int val;
};
int CreateSem(int key, int init_val[], int len);
void SemP(int semid, int index);
void SemV(int semid, int index);
void DeleteSem(int semid);
//sem.c
#include "./sem.h"
#include
#include
#include
/*获取信号量
如果内核中已经有了此key值对应的信号量,则直接返回
2、如果没有
2.1创建此信号量集
2.2所有的信号量根据init_val的值进行初始化
*/
int CreateSem(int key, int initval[], int len)
{
//获取
int semid = semget((key_t)key, 0, 0664);
if (semid != -1)
{
return semid;
}
//创建
semid = semget((key_t)key, len, IPC_CREAT | 0664);
if (-1 == semid)
{
perror("create sem fail ");
return -1;
}
//初始化
int i = 0;
for (; i < len; ++i)
{
union semval data;
data.val = initval[i];
if (-1 == semctl(semid, i, SETVAL, data))
{
perror("Init Sem Value Fail: ");
return -1;
}
}
return semid;
}
void 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))
{
perror("Sem P operation: ");
}
}
void 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("Sem P operation: ");
}
}
共享内存的实现
//shmA.c
#include
#include
#include
#include
#include
#include
#include "sem.h"
int main()
{
int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0664);//获取一个共享内存
assert(shmid != -1);
char *ptr = (char*)shmat(shmid, NULL, 0);
assert(ptr != (char*)-1);
//init_val[0]=0 : sem1 init_val[1]=1 : sem2
int init_val[2] = { 0, 1 };
int semid = CreateSem(1234, init_val, 2);
assert(semid != -1);
while(1)
{
//因为是A先执行,所以初始值为1
SemP(semid, 1);//B---->A ,B将数据处理完成后告诉A可以进行下一次操作了
printf("please input: ");
fgets(ptr, 127, stdin);
SemV(semid, 0);//A---->B ,当A获取到数据后,对信号量进行V操作,告诉B进程数据获取完毕可以进行处理了
if(strncmp(ptr, "end", 3) == 0)
{
break;
}
}
shmdt(ptr);
shmctl(shmid, IPC_RMID, NULL);
}
//shmB.c
#include
#include
#include
#include
#include
#include
#include "sem.h"
#include
int main()
{
srand((unsigned int)(time(NULL)*time(NULL)));
int shmid = shmget((key_t)1234, 128, IPC_CREAT | 0664);//获取一个共享内存
assert(shmid != -1);
char *ptr = (char*)shmat(shmid, NULL, 0);
assert(ptr != (char*)-1);
//init_val[0]=0 : sem1 init_val[1]=1 : sem2
int init_val[2] = { 0, 1 };
int semid = CreateSem(1234, init_val, 2);
assert(semid != -1);
while (1)
{
SemP(semid, 0);//A---->B
if (strncmp(ptr, "end", 3) == 0)
{
break;
}
printf("B process: %s",ptr);
int n = rand() % 3 + 1;
sleep(n);
printf("B Deal Over\n");
memset(ptr, 0, 128);
SemV(semid, 1);//B---->A
}
shmdt(ptr);
shmctl(shmid, IPC_RMID, NULL);
DeleteSem(semid);//最终结束的是B,当最后end后直接清除信号量
}