进程间通信——共享内存

什么是共享内存

共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式,允许两个不相关的进程访问同一逻辑内存。

共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到它们自己的地址空间中。

所有的进程都以访问共享内存中的地址,就像它们是由malloc分配的一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。

一般使用共享内存来提供对大块内存区域的有效访问,同时通过传递小消息来同步对该内存的访问。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJWRQq3G-1601017839281)(en-resource://database/1045:1)]

上图的剪头显示了每个进程的逻辑地址空间到可用物理内存的映射关系。实际情况要比图中显示的更加复杂,因为可用内存实际上由物理内存和已交换到磁盘上的内存页面混合组成。

共享内存处理函数

共享内存使用的函数类似于信号量的函数,它们的定义如下:

#include <sys/shm.h>

void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
1. shmget函数

用shmget函数来创建共享内存:

int shmget(key_t key, size_t size, int shmflg);

参数描述:

  • key:有效的为共享内存段命名,shmget函数返回一个共享内存标识符,该标识符将用于后续的共享内存函数。特殊键值IPC_PRIVATE,用于创建一个只属于创建进程的共享内存。
  • size:以字节为单位指定需要共享的内存容量。
  • shmflg:包含9个比特的权限标志,作用与创建文件时使用的mode标志一样。由IPC_CREAT定义的一个特殊比特必须和权限标志按位或才能创建一个新的共享内存段。设置IPC_CREAT标志的同时,给shmget函数传递一个已有共享内存段的键并不是一个错误,如果无需用到IPC_CREAT标志,该标志就会被悄悄的忽略掉。
shmat函数

第一次创建共享内存段时,它不能被任何进程访问。要想启用对该共享内存的访问,必须将其连接到一个进程的地址空间中,由shmat函数来完成,它的定义如下所示:

void *shmat(int shm_id, const void *shm_addr, int shmflg);

参数描述:

  • shm_id:由shmget返回的共享内存标识符。
  • shm_addr:指定的是共享内存连接到当前进程中的地址位置,它通常是一个空指针,表示让系统来选择共享内存出现的地址。
  • shmflg:是一组位标志。它的两个可能取值是SHM_RND(这个标志与shm_addr联合使用,用来控制共享内存连接的地址)和SHM_RDONLY(它使得连接的内存只读)。
    如果shmat调用成功,它返回一个指向共享内存第一个字节的指针;如果失败,它就返回-1.
    共享内存的读写权限由它的属主(共享内存的创建者)、它的访问权限和当前进程的属主决定。
shmdt函数

shmdt函数的作用是将共享内存从当前进程中分离。它的参数是shmat返回的地址指针。成功时返回0,失败时返回-1.

int shmdt(const void *shm_addr);
shmctl函数

与复杂的信号量控制函数相比,共享内存的控制函数稍微简单一些,定义如下:

int shmctl(int shmid, int cmd, struct shmid_ds *buf)

shmid_ds结构至少包含以下成员:

struct shmid_ds{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};

参数描述:

  • shm_id:是shmget返回的共享内存标识符;
  • cmd:是要采取的动作,可以取3个值,如下表所示:
命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID删除共享内存段
  • buf:是一个指针,指向包含共享内存模式和访问权限的结构。
    成功时返回0,失败时返回-1。

共享内存实现进程间通信

首先是头文件shmdata.h,共享内存读和写都需要用到的机构,如下所示。

#ifndef _SHMDATA_H_HEADER  
#define _SHMDATA_H_HEADER  
 //  单次可输入的最大数目
#define TEXT_SZ 1024  
  
struct shared_use_st  
{  
    //作为一个标志,非0:表示可读,0表示可写
	int written;
	//记录写入和读取的文本	
    char text[TEXT_SZ];  
};  
  
#endif

接下来是执行写的程序,shmwrite.c,如下所示:

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <string.h>  
#include <sys/shm.h>  
#include "shmdata.h"  
  
int main(void)  
{  
    int running = 1;  
    void *shm = NULL;  
    struct shared_use_st *shared = NULL;  
    char buffer[BUFSIZ + 1];//用于保存输入的文本  
    int shmid;  
    //创建共享内存  
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //将共享内存连接到当前进程的地址空间  
    shm = shmat(shmid, (void*)0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("Memory attached at %p\n", shm);  
    //设置共享内存  
    shared = (struct shared_use_st*)shm;  
    while(running)//向共享内存中写数据  
    {  
        //数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本  
        while(shared->written == 1)  
        {  
            sleep(1);  
            printf("Waiting...\n");  
        }  
        //向共享内存中写入数据  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        strncpy(shared->text, buffer, TEXT_SZ);  
        //写完数据,设置written使共享内存段可读  
        shared->written = 1;  
        //输入了end,退出循环(程序)  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
    }  
    //把共享内存从当前进程中分离  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        exit(EXIT_FAILURE);  
    }  
    sleep(2);  
    exit(EXIT_SUCCESS);  
}

最后是共享内存读函数shmread.c,如下所示:

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <sys/shm.h>  
#include "shmdata.h"  
  
int main(void)  
{  
    int running = 1;//程序是否继续运行的标志  
    void *shm = NULL;//分配的共享内存的原始首地址  
    struct shared_use_st *shared;//指向shm  
    int shmid;//共享内存标识符  
    //创建共享内存  
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);  
    if(shmid == -1)  
    {  
        fprintf(stderr, "shmget failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //将共享内存连接到当前进程的地址空间  
    shm = shmat(shmid, 0, 0);  
    if(shm == (void*)-1)  
    {  
        fprintf(stderr, "shmat failed\n");  
        exit(EXIT_FAILURE);  
    }  
    printf("\nMemory attached at %p\n", shm);  
    //设置共享内存  
    shared = (struct shared_use_st*)shm;  
    shared->written = 0;  
    while(running)//读取共享内存中的数据  
    {  
        //没有进程向共享内存定数据有数据可读取  
        if(shared->written != 0)  
        {  
            printf("You wrote: %s", shared->text);  
            sleep(rand() % 3);  
            //读取完数据,设置written使共享内存段可写  
            shared->written = 0;  
            //输入了end,退出循环(程序)  
            if(strncmp(shared->text, "end", 3) == 0)  
                running = 0;  
        }  
        else//有其他进程在写数据,不能读取数据  
            sleep(1);  
    }  
    //把共享内存从当前进程中分离  
    if(shmdt(shm) == -1)  
    {  
        fprintf(stderr, "shmdt failed\n");  
        exit(EXIT_FAILURE);  
    }  
    //删除共享内存  
    if(shmctl(shmid, IPC_RMID, 0) == -1)  
    {  
        fprintf(stderr, "shmctl(IPC_RMID) failed\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}

运行结果如下图所示:
在这里插入图片描述

结果分析

  • 首先运行shmread程序,可以看到系统给程序分配了程序的地址空间,并处于等待的状态;
  • 然后运行shmwrite程序,系统给该程序也分配了程序的地址空间,然后等待用户输入text信息,存储在结构体shared_use_st.text[]中;
  • 输入字符串之后,shmread进程立刻从共享内存中读取了用户输入的信息,直到用户输入end才结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值