进程间通信 - 共享内存

共享内存

如上图所示,多个进程之间通过自己的页表,映射到物理内存的共享区,现在这些进程都可以对这个共享区域的数据进行操作,且一个进程操作了这块空间的数据,其他的进程也可以“看得到”。

和管道/消息队列比起来
  • 共享内存的效率是比较高的
           管道和消息队列在执行数据的读写时,需要将数据从用户区拷贝到内核区,再将数据从内核区拷贝至数据区。消耗比较大,所以效率比较低。
            消息队列不需要进行这两次拷贝,不需要进行内核区和用户区的交互,效率较高,速度较快。

  • 安全性比较低
            没有提供任何的保护机制,多个进程操作共享内存,可能出现二义性的问题。

共享内存函数

shmget - 创建共享内存
int shmget(key_t key, size_t size, int shmflg);
 参数 
    key:这个共享内存段名字  //获取方法同消息队列的key,利用ftok
    size:共享内存⼤⼩  //size是一个向上取整至页的倍数的整数,例如页表大小4k,当前用户设置的size为 4097,那么系统会分配 2 * 4k的大小 
    shmflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的 

返回值:成功返回⼀个⾮负整数,即该共享内存段的标识码;失败返回-1

shmat - 建立映射关系(将共享内存的段连接到虚拟地址空间)
void *shmat(int shmid, const void *shmaddr, int shmflg); 
参数 
    shmid: 共享内存标识 
    shmaddr:指定连接的地址  //一般都设置为 NULL,系统会自动分配一个地址返回
    shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY 

返回值:成功返回⼀个指针,指向共享内存第⼀个节;失败返回-1

shmdt - 解除映射关系 (将共享内存段与当前进程脱离)
int shmdt(const void *shmaddr); 
参数 
    shmaddr: 由shmat所返回的指针 

返回值:成功返回0;失败返回-1 注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl - 控制贡献个内存 (常用于删除共享内存)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数 
    shmid:由shmget返回的共享内存标识码 
    cmd:将要采取的动作(有三个可取值) //删除共享内存 IPC_RMID
    buf:指向⼀个保存着共享内存的模式状态和访问权限的数据结构 //删除共享内存时设置为NULL

返回值:成功返回0;失败返回-1

代码演示

创建一块共享内存,客户端向共享内存中写数据,服务器将数据读出来,并输出至标准输出。

//com.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <unistd.h>

int CreateShm(int size);

int GetShm(int size);

void DestoryShm(int shmid);

//com.c

#include "com.h"

int Command(int size,int flag)
{
    key_t key = ftok(".",0666);
    if(key == -1)
    {
        perror("ftok");
    }
    int shmid = shmget(key,size,flag);
    if(shmid < 0)
    {
        perror("shmid");
    }
    return shmid;
}

int CreateShm(int size)
{
    return Command(size,IPC_CREAT | IPC_EXCL | 0666);
}

int GetShm(int size)
{
    return Command(size,IPC_CREAT);
}

void DestoryShm(int shmid)
{
    if(shmctl(shmid,IPC_RMID,0) < 0)
    {
        perror("shmctl");
        exit(1);
    }
    return;
}

//service.c

#include "com.h"

int main()
{
    int shmid = CreateShm(1024);
    char* addr = (char*)shmat(shmid,NULL,0);
    int i = 0;
    while(i < 26)
    {
        printf("client say : %s\n",addr);
        sleep(1);
        ++i;
    }
    shmdt(addr);
    DestoryShm(shmid);
    return 0;
}

//client.c

#include "com.h"

int main()
{
    int shmid = GetShm(1024);
    char* addr = (char*)shmat(shmid,NULL,0);
    int i = 0;
    while(i < 26)
    {
        addr[i] = 'A' + i;
        addr[i+1] = '\0';
        sleep(1);
        ++i;
    }
    shmdt(addr);
    return 0;
}

service 先运行,创建共享内存,client再运行,并向共享内存中发送数据,service从共享内存中读取并且输出到屏幕上。



同消息队列一样,共享内存也可通过 ipcs -m 指令查看


也可以通过 ipcrm -m [shmid] 删除


共享内存总结
  • 没有面向字节流/数据报的概念,和普通内存一样,可以随机访问
  • 没有同步互斥机制,多个进程可同时读写,不安全
  • 可以双向通信
  • 适用于任何进程之间的通信
  • 生命周期随内核


猜你喜欢

转载自blog.csdn.net/j4ya_/article/details/80562656
今日推荐