linux进程间通信(四)----IPC篇----共享内存初识篇

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
在这里插入图片描述

一 what

所谓共享内存通信,实际上就是在内核空间中有一个缓存数组,用于不同用户寂进程间实现通信。
共享内存是一种IPC通信对象,其他IPC通信对象还有消息队列以及信号灯,其处理思想和文件IO思想也是一样的,也是存在open、write、read、close函数,只是函数形式方式了变化,如下所示

文件IOIPC IO
openMsg_get: 创建消息队列
Shm_get: 创建共享内存
Sem_get: 创建信号灯
readmsgrecv
shmat
semop
writemsgsnd
shmdt
closemsgctrl
shmctrl
semctrl

二 why

简单来说,共享内存也是进程间通信的实现方法之一。那么共享内存和其他进程间通信有什么区别呢?
共享内存是一种进程间通信效率最高的方式,用户空间可以直接访问内核空间的共享内存数据,而不需要拷贝;我们知道进程通信时的数据都是存放在内核中的,管道通信时需要将数据从内核空间拷贝到用户空间,这是需要开销的。

三 how

3.1 创建共享内存

函数原型 : int shmget(key_t key, int size, int shmflg)
函数参数 : key: IPC_PRIVATE 或 ftok的返回值
                IPC_PRIVATE返回的key值都是一样的,都是0
           size : 共享内存区大小
           shmflg : 同open函数的权限位,也可以用八进制表示法
返回值   : 成功,共享内存段标识符   ID   文件描述符; -1 出错

3.2 程序示例代码

3.2.1 使用IPC_PRIVATE宏创建

#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"

int main(int argc, char *argv[])
{
    int shmid;
    
    shmid = shmget(IPC_PRIVATE, 128, 0777);
    if (shmid < 0) {
        printf("create shared memory fail\n");
        return -1;
    }
    printf("create shared memory sucess, shmid = %d\n", shmid);
    system("ipcs -m");   //在用户空间看到内核空间的对象
    //system("ipcrm -m shmid");
    return 0;
}

运行编译查看结果,我们发现key都是0
在这里插入图片描述
3.2.2 使用fotk函数创建key

ftok函数创建key值
函数原型 : char ftok(const char *path, char key)
参数 : path,文件路径和文件名
        key,一个字符
返回值 : 正确返回一个key值,出错返回-1
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"

int main(int argc, char *argv[])
{
    int shmid;
    int key;
    
    key = ftok("./a.c", 'a');
    if (key < 0) {
        printf("create key fail\n");
        return -1;
    }
    printf("create key sucess key = 0x%X\n",key);
    
    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory fail\n");
        return -1;
    }
    printf("create shared memory sucess, shmid = %d\n", shmid);
    system("ipcs -m");
    //system("ipcrm -m shmid");
    return 0;
}

编译运行
在这里插入图片描述
此时的key值就不在为0了,而是0x61110105。

3.2.3 为何需要ftok先创建key值呢?
这就类似于前面无名管道和有名管道,使用IPC_PRIVATE宏创建的共享内存就类似于无名管道,只能实现有亲缘关系的进程间通信。ftok函数创建了一个key值之后,就类似于有名管道,能够实现无亲缘关系的管道间通信。

3.2.4 用户空间如何操作共享内存
因为创建的共享内存仍然属于内核空间,用户空间如何操作这片共享内存呢?

shmat函数,将共享内存映射到用户空间,方便在用户空间操作
函数原型 : void *shmat(int shmid, const void *shmaddr, int shmflg)
参数 : shmid ID号
        shmaddr 映射到啊的地址, NULL为系统自动完成的映射
        shmflg SHM_RDONLY共享内存只读
               默认是0,可读可写
返回值:成功,映射后的结果;失败,返回NULL

示例代码

#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"

int main(int argc, char *argv[])
{
    int shmid;
    int key;
    char *p;
    
    key = ftok("./a.c", 'b');
    if (key < 0) {
        printf("create key fail\n");
        return -1;
    }
    printf("create key sucess key = 0x%X\n",key);
    
    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory fail\n");
        return -1;
    }
    printf("create shared memory sucess, shmid = %d\n", shmid);
    system("ipcs -m");
    
    p = (char *)shmat(shmid, NULL, 0);
    if (p == NULL) {
        printf("shmat fail\n");
        return -1;
    }
    printf("shmat sucess\n");
    
    //write share memory
    fgets(p, 128, stdin);
    
    //start read share memory
    printf("share memory data:%s\n", p);
    
    //start read share memory again
    printf("share memory data:%s\n", p);
    //system("ipcrm -m shmid");
    return 0;
}

编译运行
此时在console终端,通过键盘任意输入一串信息,console会打印出我们输入的信息,并且我们验证了连续2次读取同一个共享内存,发现内容都存在,这是和之前管道的区别,管道的内容读取完了之后,内容就不存在了。
在这里插入图片描述
3.2.5 删除用户空间的共享内存地址映射

shmdt
int shmdt(const void *shmaddr)
参数 ; shmat的返回值
返回值 : 成功0,出错-1
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "string.h"

int main(int argc, char *argv[])
{
    int shmid;
    int key;
    char *p;
    
    key = ftok("./a.c", 'b');
    if (key < 0) {
        printf("create key fail\n");
        return -1;
    }
    printf("create key sucess key = 0x%X\n",key);
    
    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory fail\n");
        return -1;
    }
    printf("create shared memory sucess, shmid = %d\n", shmid);
    system("ipcs -m");
    
    p = (char *)shmat(shmid, NULL, 0);
    if (p == NULL) {
        printf("shmat fail\n");
        return -1;
    }
    printf("shmat sucess\n");
    
    //write share memory
    fgets(p, 128, stdin);
    
    //start read share memory
    printf("share memory data:%s\n", p);
    
    //start read share memory again
    printf("share memory data:%s\n", p);

    //在用户空间删除共享内存的地址
    shmdt(p);
    
    memcpy(p, "abcd", 4);  //执行这个语句会出现segment fault
    return 0;
}

编译后执行,会出现segment fault,是正确的现象
在这里插入图片描述
3.2.6 删除内核空间的共享内存对象

shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数 ; shmid : 共享内存标识符
        cmd : IPC_START (获取对象属性)      --- 实现了命令 ipcs -m
              IPC_SET (设置对象属性)
              IPC_RMID (删除对象属性)       --- 实现了命令 ipcrm -m
        buf : 指定IPC_START/IPC_SET时用以保存/设置属性
返回值 : 成功0,出错-1
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "string.h"

int main(int argc, char *argv[])
{
    int shmid;
    int key;
    char *p;
    
    key = ftok("./a.c", 'b');
    if (key < 0) {
        printf("create key fail\n");
        return -1;
    }
    printf("create key sucess key = 0x%X\n",key);
    
    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory fail\n");
        return -1;
    }
    printf("create shared memory sucess, shmid = %d\n", shmid);
    system("ipcs -m");
    
    p = (char *)shmat(shmid, NULL, 0);
    if (p == NULL) {
        printf("shmat fail\n");
        return -1;
    }
    printf("shmat sucess\n");
    
    //write share memory
    fgets(p, 128, stdin);
    
    //start read share memory
    printf("share memory data:%s\n", p);
    
    //start read share memory again
    printf("share memory data:%s\n", p);

    //在用户空间删除共享内存的地址
    shmdt(p);
    
    //memcpy(p, "abcd", 4);  //执行这个语句会出现segment fault
    
    shmctl(shmid, IPC_RMID, NULL); //会删除内核的共享内存对象
    system("ipcs -m");
    return 0;
}

编译运行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值