Linux进程间通信第四讲 标准IPC之共享内存

目录

四、标准IPC

4.1 共享内存

4.1.1 概念和原理

4.1.2 使用


四、标准IPC

标准IPC是一类遵循指定标准的IPC(进程间通信)的统称,其实就是其中所有的IPC都具有一部分相同的标准

标准IPC包含 共享内存 消息队列 信号量集

标准IPC的通用规范(上: 用户 下: 内核), 如下图所示:

( 1 )所有的标准IPC都有一个内部ID作为唯一标识

( 2 )获取内部ID, 需要借助外部的 key,类型为key_t

( 3 )获取key的方法有三种:

        1、使用宏IPC_PRIVATE作为key,但这种方式外部无法获取,因此基本不用

        2、在代码中自定义所有的key

        3、使用ftok()函数获取一个key

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);

             参数:

                    pathname - 路径(必须有效 / or .)

                    proj_id - 工程ID(非0的8位数据,通常给一个字符)

                    成功返回key,失败返回-1

( 4 )通过刚刚获取的key来获取ID, 常用的函数都是叫 xxxget(),比如:shmget() msgget()

( 5 )标准IPC一定提供一个叫 xxxctl()的函数,这个函数至少包括以下功能

         功能通过一个cmd的参数来控制

            查询 ------- IPC_STAT

            删除 ------- IPC_RMID    

            修改 ------- IPC_SET

( 6 )标准IPC共用命令

        ipcs - 查询当前系统的标准IPC结构

        ipcrm - 删除当前系统的标准IPC结构(通过ID删除)

            选项:

                  -a    所有IPC结构

                  -m    共享内存

                  -q    消息队列

                  -s    信号量集

          nattch 挂载进程数量

4.1 共享内存

4.1.1 概念和原理

共享内存是其中一个标准IPC, 其大概的标准如前面所讲的

它的原理就是系统取出一块物理内存作为媒介,让系统内核( MMU)负责管理, 它允许所有的进程对这块物理内存进行映射, 这样不同的进程就可以访问同一片物理内存,从而实现数据的交互

由于内存访问速度极快,所以共享内存是效率最高的IPC

但是,多个进程同时对一块物理内存读写容易造成数据混乱,需要额外的保护机制(利用信号量集)

4.1.2 使用

    1)获取key,通过ftok函数

    2)通过shmget() 获取/创建 共享内存

          

          参数:

                key - 上一步获取的key

                size - 共享内存的大小(创建时有效)

                shmflg - 创建标识和权限(创建时有效)

                IPC_CREAT | 0666

                成功返回共享内存的ID,失败返回-1

    3)通过shmat() 映射/挂接 共享内存

          

         参数:

               shmid - 共享内存ID

               shmaddr - 映射的目标地址(给NULL由系统选择)

               shmflg - 映射标识(通常给0,给SHM_RDONLY表示只读)

               成功返回映射目标地址,失败返回 (void *)-1

    4) 使用共享内存

    5) 使用完通过 shmdt() 解除映射/脱接 共享内存

         

    6) 如果不再使用,使用shmctl来删除共享内存 (删除共享内存只是给共享内存加一个删除标记, 等挂载数为0时 才删除)

        

        参数:

              shmid - 共享内存ID

              cmd - 命令

                    IPC_RMID:删除

                    IPC_SET:  修改

                    IPC_STAT:   获取

              buf - 设置/获取 时 传入/传出 共享内存的信息 , 下面是它的数据类型 ,其中可以修改的是uid、gid和mode

              shmctl函数 成功返回0,失败返回-

struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* 大小 */
               time_t          shm_atime;   /* 最后挂载(映射)时间*/
               time_t          shm_dtime;   /* 最后卸载(解除映射)时间 */
               time_t          shm_ctime;   /* 最后修改时间*/
               pid_t           shm_cpid;    /* 创建者PID */
               pid_t           shm_lpid;    /* 最后一个挂载/卸载PID */
               shmatt_t        shm_nattch;  /* 当前挂载的进程数*/
               ...
           };
struct ipc_perm {
               key_t          __key;    /* Key*/
               uid_t          uid;      /* Effective UID of owner */
               gid_t          gid;      /* Effective GID of owner */
               uid_t          cuid;     /* Effective UID of creator */
               gid_t          cgid;     /* Effective GID of creator */
               unsigned short mode;     /* 权限 */
               unsigned short __seq;    /* Sequence number */
           };

代码如下、这个进程创建共享内存后、并映射它到自己的进程空间、再使用它、最后解除映射:

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

int main()
{
    //1.获取key
    key_t key = ftok(".",'x');
    if(key==-1){
        perror("ftok");
        exit(-1);
    }

    //2.创建共享内存
    int shmid = shmget(key,10,IPC_CREAT|0666);
    if(shmid==-1){
        perror("shmget");
        exit(-1);
    }

    //3.映射共享内存
    void *p = shmat(shmid,0,0);
    if(p==(void *)-1){
        perror("shmat");
        exit(-1);
    }

    //4.使用共享内存
    *(int *)p = 100;

    //5.解除映射
    shmdt(p);

    return 0;
}

与它一起的进行通信的是下面这个代码、这个进程获取的key要保持一致才能获得同样的共享内存ID、用这个ID进行映射,然后使用它、最后要删除共享内存:

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

int main()
{
    //1.获取key
    key_t key = ftok(".",'x');
    if(key==-1){
        perror("ftok");
        exit(-1);
    }

    //2.获取共享内存
    int shmid = shmget(key,0,0);
    if(shmid==-1){
        perror("shmget");
        exit(-1);
    }

    //3.映射共享内存
    void *p = shmat(shmid,0,0);
    if(p==(void *)-1){
        perror("shmat");
        exit(-1);
    }

    //4.使用共享内存
    printf("data = %d\n",*(int *)p);

    //5.解除映射
    shmdt(p);

    //6.不再使用删除共享内存
    shmctl(shmid,IPC_RMID,0);

    return 0;
}

但是, 这是不规范的!

如果对于同一个资源, 一个进程对它写, 同时另一个进程又对它读

这会乱 !!!

因此产生了进程的互斥和同步(我博客中的信号量集有讲)

发布了52 篇原创文章 · 获赞 40 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_40519315/article/details/104210920