(4)linux进程通讯之共享内存

共享内存

①为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间,这块就是共享内存区域
②由于可以多个进程共享一段内存,因此也需要依靠某种同步机制(如互斥锁和信号量等)
③共享内存是一种最为高效的进程间通信方式,因为进程可以直接读写内存,而不需要任何数据的拷贝

Shell的ipcs命令可以查看共享内存情况

主要步骤

①创建/打开共享内存
②映射共享内存(即把指定的共享内存映射到进程的地址空间用于访问)
③撤销共享内存映射
④删除共享内存对象

1、创建/打开共享内存

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

参数
key:IPC_PRIVATE 或 一个key_t值(如(key_t)0001),一般使用ftok()
size:共享内存区大小
shmflg:同open函数的权限位,也可以用8进制表示法
返回值
成功:共享内存段标识符
出错:-1

注意

①key是系统建立IPC通讯(消息队列、信号量和共享内存)时必须指定一个ID值。因为一定要唯一!!(要不然IPC通讯就乱套了),我们就想起来了文件或目录的节点号(因为他们是唯一的),那么这个节点号具体怎么用呢,这时候ftok()就出场了。ftok()是将文件的索引节点号取出,前面加上子序号得到key_t的返回值,完全符合我们对键值获取!

②如果key的值指定为IPC_PRIVATE,表明由系统为进程创建一个新的共享内存对象
③shmflg如包含IPC_CREAT,表明如果指定的共享内存不存在,则新建一个对象

ftok()原型如下:

key_t ftok( char * fname, int id )

fname就时你指定的文件名,该文件必须是存在而且可以访问的,若该文件不存在返回0xffffffff
id是子序号,虽然为int,但是只有8个比特被使用(0-255)。
当成功执行的时候,一个key_t键值将会被返回

shmget例子:

int shmid;
key_t key;
key= ftok("./tmp.txt",0);
shmid = shmget(key,1024,0666|IPC_CREAT);

将目录下tmp.txt的索引节点号作为键值,打开共享内存,大小为1024,权限0666,如果共享内存不存在,就创建。

2、映射共享内存

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数
shmid:要映射的共享内存区标识符(shmget()返回值)
shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
shmflg :SHM_RDONLY:共享内存只读,默认0:共享内存可读写

返回值
成功:映射后共享内存的地址
出错:-1

//创立一个共享内存结构体
struct share_mm
{
    int w;
    char buf[BUFFER_SIZE];
}* shmaddr;

//映射,并获得映射地址,shmid为创建/打开共享内存的标识符
shmaddr=shmat(shmid,0,0);
if(shmaddr==(void *)-1)
{
    printf("shmat error\n");
    exit(1);
}
//shmaddr是共享内存的地址,可以对此地址进行读写

3、撤销共享内存映射

int shmdt(const void *shmaddr);

参数
shmaddr:共享内存映射后的地址
返回值
成功:0
出错:-1

例子

    if((shmdt(shmaddr))<0)
    {
        printf("shmdt error\n");
        exit(1);
    }

4、删除共享内存对象

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

参数
shmid:要操作的共享内存标识符
cmd : IPC_STAT (获取对象属性)
IPC_SET (设置对象属性)
IPC_RMID (删除对象)
buf : 指定IPC_STAT/IPC_SET时用以保存/设置属性
返回值
成功:0
出错:-1

例子

    if((shmctl(shmid,IPC_RMID,NULL))<0)//删除内核中的共享内存
    {
        printf("shmctl error\n");
        exit(1);
    }

最后一个例子

struct share_mm//共享内存结构体
{
    int pid_r;
    int pid_w;
    char buf[BUFFER_SIZE];
}*shmaddr;

共享结构体中,pid_r和pid_w保存读写进程的进程号,然后共享内存的read和write通过信号实现同步。
即writer进程通过发送信号告诉reader进程共享内存我已写入新的数据,你可以读;读完后,reader进程通过发送信号告诉writer进程共享内存我已读走新的数据,你可以写;

read.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#define BUFFER_SIZE 1024

struct share_mm//共享内存结构体
{
    int pid_r;
    int pid_w;
    char buf[BUFFER_SIZE];
}*shmaddr;
void read_message(int signum)
{
    printf("From father message:%s\n",shmaddr->buf);
}
int main()
{
    int shmid;
    pid_t pid;
    key_t key;
    //struct share_mm * shmaddr;//保存映射地址
    //创建共享内存
    key= ftok("./",0);
    shmid = shmget(key,sizeof(struct share_mm),0666|IPC_CREAT);
    if(shmid==-1)
    {
        printf("shmget error\n");
        exit(1);
    }
    else
    {
        printf("Shmid is %d\n",shmid);
        system("ipcs -m");
    }
    //映射,并获得映射地址
    shmaddr=shmat(shmid,0,0);
    if(shmaddr==(void *)-1)
    {
        printf("shmat error\n");
        exit(1);
    }
    else
    {
        printf("Chile attach shm is %p\n",shmaddr);
        system("ipcs -m");
    }
    //初始化共享内存
    shmaddr->pid_r=getpid();
    shmaddr->pid_w=0;
    memset((void *)shmaddr->buf,0,BUFFER_SIZE);
    while(shmaddr->pid_w==0);//等待写进程准备就绪
    signal(SIGUSR1,read_message);
    do
    {
        pause();//等带write进程的SIGUSR1信号,到来就读
        kill(shmaddr->pid_w,SIGUSR2);
    }while(strncmp(shmaddr->buf,"quit",4));

    printf("Writer byebye\n");
    if((shmdt(shmaddr))<0)
    {
        printf("shmdt error\n");
        exit(1);
    }

    exit(0);
    return 0;
}

write.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<signal.h>

#define BUFFER_SIZE 1024

struct share_mm//共享内存结构体
{
    int pid_r;
    int pid_w;
    char buf[BUFFER_SIZE];
}* shmaddr;

void write_message(int signum)
{
    memset((void *)shmaddr->buf,0,BUFFER_SIZE);
    printf("In father process:\nPlease write message\n");
    gets(shmaddr->buf);//从键盘写入数据到共享内存
}

int main()
{
    int shmid;
    pid_t pid;
    key_t key;
    //struct share_mm * shmaddr;//保存映射地址
    //创建共享内存
    key= ftok("./",0);
    shmid = shmget(key,sizeof(struct share_mm),0666);
    if(shmid==-1)
    {
        printf("shmget error\n");
        exit(1);
    }
    else
    {
        printf("Shmid is %d\n",shmid);
        system("ipcs -m");
    }
    //映射,并获得映射地址
    shmaddr=shmat(shmid,0,0);
    if(shmaddr==(void *)-1)
    {
        printf("shmat error\n");
        exit(1);
    }
    else
    {
        printf("Chile attach shm is %p\n",shmaddr);
        system("ipcs -m");
    }
    //初始化共享内存
    shmaddr->pid_w=getpid();
    signal(SIGUSR2,write_message);
    printf("In father process:\nPlease write message\n");
    gets(shmaddr->buf);//从键盘写入数据到共享内存
    do
    {
        kill(shmaddr->pid_r,SIGUSR1);
        pause();    
    }while(strncmp(shmaddr->buf,"quit",4));
    kill(shmaddr->pid_r,SIGUSR1);//通知reader进程推出
    waitpid(shmaddr->pid_r,NULL,0);//等待读进程先退出   
    printf("Reader byebye\n");
    if((shmdt(shmaddr))<0)
    {
        printf("shmdt error\n");
        exit(1);
    }
    if((shmctl(shmid,IPC_RMID,NULL))<0)//删除内核中的共享内存
    {
        printf("shmctl error\n");
        exit(1);
    }
    exit(0);
    return 0;
}

这里写图片描述

linux进程通讯的总结:

pipe: 具有亲缘关系的进程间,半双工,数据在内存中
fifo: 可用于任意进程间,双工,有文件名,数据在内存
signal: 唯一的异步通信方式
shm:效率最高(直接访问内存) ,需要同步、互斥机制

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值