天天看点

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

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

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

所谓共享内存通信,实际上就是在内核空间中有一个缓存数组,用于不同用户进程间实现通信。

共享内存是一种IPC通信对象,其他IPC通信对象还有消息队列以及信号灯。其处理思想和文件IO思想是一样的,也是open、write、read、close函数,只是函数形式方式了变化,如下所示:

linux 共享内存_linux进程间通信----IPC篇(一)----共享内存初识篇
二 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 demo程序示例

3.2.1 使用IPC_PRIVAT宏创建

#include 
           

编译运行查看结果,我们发现key都是0

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

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 failn");
        return -1;
    }
    printf("create key sucess key = 0x%Xn",key);

    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory failn");
        return -1;
    }
    printf("create shared memory sucess, shmid = %dn", shmid);
    system("ipcs -m");
    //system("ipcrm -m shmid");
    return 0;
}
           

编译运行

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

此时的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 failn");
        return -1;
    }
    printf("create key sucess key = 0x%Xn",key);

    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory failn");
        return -1;
    }
    printf("create shared memory sucess, shmid = %dn", shmid);
    system("ipcs -m");

    p = (char *)shmat(shmid, NULL, 0);
    if (p == NULL) {
        printf("shmat failn");
        return -1;
    }
    printf("shmat sucessn");

    //write share memory
    fgets(p, 128, stdin);

    //start read share memory
    printf("share memory data:%sn", p);

    //start read share memory again
    printf("share memory data:%sn", p);
    //system("ipcrm -m shmid");
    return 0;
}
           

此时在console终端,通过键盘任意输入一串信息,console会打印出我们输入的信息,并且我们验证了连续2次读取同一个共享内存,发现内容都存在,这是和之前管道的区别,管道的内容读取完了之后,内容就不存在了。

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

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 failn");
        return -1;
    }
    printf("create key sucess key = 0x%Xn",key);

    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory failn");
        return -1;
    }
    printf("create shared memory sucess, shmid = %dn", shmid);
    system("ipcs -m");

    p = (char *)shmat(shmid, NULL, 0);
    if (p == NULL) {
        printf("shmat failn");
        return -1;
    }
    printf("shmat sucessn");

    //write share memory
    fgets(p, 128, stdin);

    //start read share memory
    printf("share memory data:%sn", p);

    //start read share memory again
    printf("share memory data:%sn", p);

    //在用户空间删除共享内存的地址
    shmdt(p);

    memcpy(p, "abcd", 4);  //执行这个语句会出现segment fault
    return 0;
}
           

编译后执行,会出现segment fault,是正确的现象

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

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 failn");
        return -1;
    }
    printf("create key sucess key = 0x%Xn",key);

    shmid = shmget(key, 128, IPC_CREAT | 0777);
    if (shmid < 0) {
        printf("create shared memory failn");
        return -1;
    }
    printf("create shared memory sucess, shmid = %dn", shmid);
    system("ipcs -m");

    p = (char *)shmat(shmid, NULL, 0);
    if (p == NULL) {
        printf("shmat failn");
        return -1;
    }
    printf("shmat sucessn");

    //write share memory
    fgets(p, 128, stdin);

    //start read share memory
    printf("share memory data:%sn", p);

    //start read share memory again
    printf("share memory data:%sn", p);

    //在用户空间删除共享内存的地址
    shmdt(p);

    //memcpy(p, "abcd", 4);  //执行这个语句会出现segment fault

    shmctl(shmid, IPC_RMID, NULL); //会删除内核的共享内存对象
    system("ipcs -m");
    return 0;
}