• 共享内存 - 多进程编程(三)


    定义:
    共享内存(Shared Memory)就是允许两个或多个进程访问同一个内存空间,是在多进程通信的最高效的方式。
    操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。由于共享内存会成为进程用户空间的一部分,所有这种通信方式不需要内核介入。
    共享内存并未提供锁机制,也就是说,在某一个进程对共享内存的进行读写的时候,不会阻止其它的进程对它的读写,可能会出现数据的错乱。

    函数:
    1、必备头文件

    #include
    #include
    1
    2
    2、shmget() 获取或创建共享内存

    // 获取或者创建共享内存
    int shmget(key_t key, size_t size, int shmflg);
    - key是共享内存的键值,是一个整数,是共享内存在系统中的编号,不同共享内存的编号不同。一般使用十六进制。
    - size表示待创建共享内存的大小,以字节为单位
    - shmflg表示共享内存的访问权限,与文件的权限一样,
        0666 | IPC_CREAT 表示全部用户对它可读写
    - 返回值:返回共享内存标识
    1
    2
    3
    4
    5
    6
    7
    3、shmat() 把共享内存连接到当前进程的地址空间

    void *shmat(int shm_id, const void *shm_addr, int shmflg);
    - shm_id表示由 shmget函数返回的 共享内存的标识号
    - shm_addr指定共享内存连接到当前进程中的地址位置,通常为 NULL,表示让系统来选择共享内存的地址。
    - shm_flg是一组标志位,通常为0
    - 返回值:
        调用成功:返回一个指向共享内存第一个字节的指针
        调用失败:返回 -1
    1
    2
    3
    4
    5
    6
    7
    4、shmdt() 将共享内存从当前进程中分离,相当于 shmat()函数的反操作

    int shmdt(const void *shmaddr);
    - shmaddr是 shmat函数 返回的地址
    - 返回值:
        调用成功:返回 0
        调用失败:返回 -1
    1
    2
    3
    4
    5
    5、shmctl() 删除共享内存

    int shmctl(int shm_id, int command, struct shmid_ds *buf);
    - shm_id是共享内存的标识号
    - command 填写 IPC_RMID
    - buf 填写 0
    - 返回值:
        调用成功:返回 0
        调用失败:返回 -1
    1
    2
    3
    4
    5
    6
    7
    使用步骤
    调用 shmget() 函数创建一个新共享内存段或取得一个现有的共享内存段的标识号。返回后续可调用的需要用到的共享内存标识符。
    在进程中使用 shmat() 函数附上共享内存段,即让该共享内存段成为调用进程的虚拟内存中的一部分(进程绑定共享内存段)
    使用 shmat() 返回的内存地址指针,就可以在程序中对该共享内存段进行操作(写操作/读操作)。
    调用 shmdt() 函数,让进程和共享内存段分离,进程无法再操作共享内存了。(只是分离,共享内存依然存在)
    调用 shmctl() 函数,删除共享内存段,只有当与该共享内存段绑定好的进程都分离后,才会销毁,只有一个进程需要执行这一步。
    举例
    // write_shm.c
    #include
    #include
    #include
    #include
    #include  

    int main()
    {
        int shmid; // 共享内存标识符
     
          // 创建共享内存,键值为0x5005,共1024字节。
          if ( (shmid = shmget((key_t)0x5005, 1024, 0666 | IPC_CREAT)) == -1)
          { 
            printf("shmat(0x5005) failed\n"); 
            return -1; 
        }

          char *ptext = NULL;   // 用于指向共享内存的指针
     
          // 将共享内存连接到当前进程的地址空间,由ptext指针指向它
          ptext = (char *)shmat(shmid, NULL, 0);
        
          // 操作本程序的ptext指针,就是操作共享内存
        char *str = "Hello world!";
          memcpy(ptext, str, strlen(str));
        
          // 把共享内存从当前进程中分离
          shmdt(ptext);

        return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // read_shm.c

    int main()
    {
        int shmid; // 共享内存标识符
     
          // 创建共享内存,键值为0x5005,共1024字节。
          if ( (shmid = shmget((key_t)0x5005, 1024, 0666 | IPC_CREAT)) == -1)
          { 
            printf("shmat(0x5005) failed\n"); 
            return -1; 
        }

          char *ptext = NULL;   // 用于指向共享内存的指针
     
          // 将共享内存连接到当前进程的地址空间,由ptext指针指向它
          ptext = (char *)shmat(shmid, NULL, 0);
     
          // 操作本程序的ptext指针,就是操作共享内存
          printf("%s\n", ptext);
     
          // 把共享内存从当前进程中分离
          shmdt(ptext);
       
          // 删除共享内存
          if (shmctl(shmid, IPC_RMID, 0) == -1)
          { 
            printf("shmctl(0x5005) failed\n"); 
            return -1; 
        }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    read_shm.c的输出结果:

    Hello world!
    1
    可以看出,write_shm.c 申请共享内存,并写入数据,read_shm获取共享内存标识号,并绑定进程,读取该共享内存中的数据。

    查看共享内存
    ipcs -m # 查看现有的所有共享内存段

    ipcrm -m shmid # 根据共享内存段的标识号,删除共享内存
    ————————————————
    版权声明:本文为CSDN博主「HDD615」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Sir666888/article/details/125460499

    一、进程间通信

            管道、信号:只能用于两个进程间进行通讯

            共享内存、消息队列、信号量、套接字

    二、IPC通信

            1.相关知识

                    1)IPC可用于多个进程间进行通讯,包括共享内存、消息队列、信号量。

                    2)指令;

                            ipcs -m:查看共享内存

                            ipcs -q:查看消息队列

                            ipcs -s:查看信号量

                    3)键值:用于标识共享内存、消息队列以及信号量集,确保唯一性。

                            ftok():键值的创造

                                    头文件:

                                            #include

                                            #include

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

                                    参数:

                                            const char *pathname:参考位置  (绝对路径)

                                            int proj_id:参考的id  (0-255)

                                    返回值: 成功,返回键值(key_t)    失败,返回 -1

            2.共享内存

                    1)什么是共享内存?

                            在操作系统内核开辟一个空间,每一个进程都可以将这个空间映射到自己的进程空间里面,多个进程可以共享这块内存,

    对他的操作都可以立即实现。

                            范围:整个计算机的进程,都可以对它进行操作。

            2)过程

                    创建共享内存

                    进程映射到内存进行读写

                    进程接触映射

                    删除共享内存

            3)相关函数

                    shmget():打开或者创建一块共享内存

                    头文件:

                            #include

                            #include

                    原型:int shmget(key_t key, size_t size, int shmflg);

                    参数:

                            key_t key:键值

                            size_t size:共享内存的大小,以字节为单位

                            int shmflg:有则打开,没有则创建

                                                    IPC_CREAT | 0777

                    返回值: 成功,返回的是共享内存的id号 失败,返回 -1

                    shmat():把一个共享内存映射到进程空间

                    头文件:

                            #include

                            #include

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

                    参数:

                            int shmid, :共享内存的id

                            const void *shmaddr, :你要将共享内存映射的位置一般填写0,表示由系统分配一块,可用的地址空间

                            int shmflg:一般填写0 表示共享内存可读写

                    返回值: 系统给分配的空间的首地址  失败返回-1

                    shmdt():解除共享内存的映射

                    头文件:

                            #include

                            #include

                    原型:int shmdt(const void *shmaddr);

                    参数:系统给分配的空间的首地址

                    返回值:成功 0  失败 -1

                    shmctl():删除共享内存

                    头文件:

                            #include

                            #include

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

                    参数:

                            int shmid,:贡献内存的id

                            int cmd, :IPC_RMID  

                            struct shmid_ds *buf:NULL

                    返回值:成功 0  失败 -1

    下边是一个练习代码:

    #include
    #include
    #include
    #include
    #include
     
    int main()
     
    {
        key_t ft;
        int shmid;
        ft = ftok("/home/whs/002/7.4",5);
        if(ft < 0)
        {
            perror("ftok");
            return -1;
        }
        printf("ft=%d\n",ft);
        shmid = shmget(ft,256,IPC_CREAT|0666); //创建共享内存
        if(shmid < 0)
        {
            perror("shmget");
            return -1;
        }
        void *p = shmat(shmid,0,0); //映射到进程空间,0,由系统分配;0,可读写
        strcpy(p,"zhongguowansui!");//写内容到共享内存
        shmdt(p);                   //解除映射
        return 0;
    }
    文章知识点与官方知识档案匹配,可进一步学习相关知识
    ————————————————
    版权声明:本文为CSDN博主「编程小白菜123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/wanghongshuai1/article/details/125604916

  • 相关阅读:
    Radare2 框架介绍及使用
    保姆级教程,教你AI数字人应该怎么制作?!
    spring中注解来创建bean
    03.类跟对象
    Python接口自动化测试:利用的是requests库,Fiddler抓包配合、以及鉴权的流程
    【ASP.NET Core】在node.js上托管Blazor WebAssembly应用
    对称加密算法———AES算法
    聊聊 Nacos
    IntelliJ IDEA社区版学习一
    《c++ Primer Plus 第6版》读书笔记(4)
  • 原文地址:https://blog.csdn.net/u012294613/article/details/126298838