[C++学习] 多进程通信共享内存

ref:https://blog.csdn.net/qq_35733751/article/details/82872197

多线程共享进程的地址空间,如果多个线程需要访问同一块内存,用全局变量即可。

在多进程中,每个进程的地址空间是独立的,不共享的,如果多个进程需要访问同一块内存,不能用全局变量,只能用共享内存。

共享内存Shared Memory 允许多个进程(不要求进程之间有血缘关系)访问同一块内存空间,是多个进程之间共享和传递数据最高效的方式。进程可以将共享内存连接到他们自己的地址空间,如果某个进程修改了共享内存中的数据,其他的进程读到的数据也会改变。

共享内存没有提供锁机制,也就是说,在某一个进程对共享内存进行读/写的时候,不会阻止其它进程对它的读/写。如果要对共享内存的读/写加锁,可以使用信号量。

Linux 操作共享内存需要四步:

  1. 调用shmget函数获取或创建共享内存

  2. 调用shmat函数把共享内存连接到当前进程的地址空间

  3. 调用shmdt函数把共享内存从当前进程中分离 shmdt是用来取消进程空间的地址空间和共享内存的物理地址的挂接,不让进程访问共享内存了。

  4. 调用shmctl函数删除共享内存
    通常不需要第4个,除非整个服务停止了。
    查看当前的共享内存命令

    ipcs -m

bash 复制代码
(base) ubuntu@ubuntu:~$ ipcs -m 

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 360454     ubuntu     600        1048576    2          dest         
0x00000000 229384     ubuntu     700        131072     2          dest         
0x00000000 229385     ubuntu     700        1290240    2          dest         
0x00000000 360459     ubuntu     600        524288     2          dest         
0x00000000 229389     ubuntu     600        524288     2          dest         
0x00000000 229393     ubuntu     600        4194304    2          dest         
0x00000000 327698     ubuntu     700        8192       2          dest         
0x00000000 21         ubuntu     600        57024      2          dest         
0x00000000 24         ubuntu     600        524288     2          dest         
0x00000000 29         ubuntu     600        4194304    2          dest         
0x00000000 327713     ubuntu     700        2101248    2          dest         
0x00000000 36         ubuntu     777        3110400    2          dest         
0x00000000 37         ubuntu     777        3110400    2          dest         
0x00000000 196647     ubuntu     700        16384      2          dest         
0x00000000 40         ubuntu     600        806400     2          dest         
0x00000000 41         ubuntu     600        7904376    2          dest         
0x00000000 196650     ubuntu     700        20480      2          dest         
0x5106002b 44         ubuntu     600        1          1                       
0x00000000 45         ubuntu     600        986168     2          dest         
0x00000000 47         ubuntu     600        7574616    2          dest         
0x00000000 48         ubuntu     600        914292     2          dest         
0x00000000 196663     ubuntu     700        2242800    2          dest         
0x00000000 196669     ubuntu     700        1474560    2          dest         

参数解析:

key 是 共享内存的标识符, 16进制,owner 创建共享内存的用户或进程的用户ID。perms 权限 bytes 内存的大小 nattch 链接进来的进程数量 status 状态

第七列是共享内存的状态status。其中显示"dest"表示共享内存段已经被删除,但是还有用户在使用它,当该段内存的mode字段设置为 SHM_DEST时就会显示"dest"。当用户调用shmctl的IPC_RMID时,内存先查看多少个进程与这个内存关联着,如果关联数为0,就会销 毁这段共享内存,否者设置这段内存的mod的mode位为SHM_DEST,如果所有进程都不用则删除这段共享内存。

ipcrm -m shmid 删除共享内存

以上列出的是使用默认的"ipcs -m"命令输出的列。

C++ 复制代码
//
// Created by ubuntu on 9/7/23.
//
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

struct st_pid {
    int pid; // 进程编号
    char names[51]; // 进程名称
};

int main(int argc, char *argv[]) {
    int key = 0x1234;
    int shmid;
    // 调用shmget函数获取或创建共享内存
    if ((shmid = shmget(key, sizeof(struct st_pid), 0640 | IPC_CREAT)) == -1) {
        printf("shmget(Ox1234) failed \n");
        return -1;
    }
    struct st_pid *stpid = 0;
    // 调用shmat函数把共享内存连接到当前进程的地址空间,On success, shmat() returns the address of the attached shared memory segment
    if((stpid = (struct st_pid *)shmat(shmid, stpid, 0)) == (void *) -1) {
        printf("shmat failed\n");
        return -1;
    }

    //把共享内存从当前进程中分离
    //shmdt(stpid);

}

要注意的是这一部分:

struct st_pid *)shmat(shmid, stpid, 0))

man 啥看shmat, 它返回的是void × 类型,所以要强制转换何曾 struct st_pid * , 因为是要用st_pid * 做的数据接收。

同时手册明确说了,错误后是返回值是 void× 类型的 -1

bash 复制代码
SYNOPSIS
       #include <sys/types.h>
       #include <sys/shm.h>

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

RETURN VALUE
       On success, shmat() returns the address of the attached shared memory segment; on error, (void *) -1 is returned, and
       errno is set to indicate the cause of the error.

       On success, shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the error.

第三个参数:int shmflg 暂时用不到,需要传0;

shmflg参数:是一个权限标志位,如果shmflg = 0表示默认权限可读写,如果指定shmflg = HM_RDONLY表示只读模式,其他为读写模式。

参数shmaddr:表示进程空间的虚拟地址,shmat函数会把shmaddr地址映射到共享内存,具体如何映射是由参数shmaddr和shmflg来决定。

ref:ttps://blog.csdn.net/qq_35733751/article/details/82872197

删除共享内存:

函数原型:
shmctl(shmid, IPC_RMID, 0)
IPC_RMID表示没有进程绑定的时候才删除,第三个参数是啥意思???
       IPC_RMID  Mark  the  segment to be destroyed.  The segment will actually be destroyed only after the last process de‐
                 taches it (i.e., when the shm_nattch member of the associated structure shmid_ds is zero).  The caller must
                 be the owner or creator of the segment, or be privileged.  The buf argument is ignored.

                 If  a  segment  has  been marked for destruction, then the (nonstandard) SHM_DEST flag of the shm_perm.mode
                 field in the associated data structure retrieved by IPC_STAT will be set.

                 The caller must ensure that a segment is eventually destroyed; otherwise its pages  that  were  faulted  in
                 will remain in memory or swap.

                 See also the description of /proc/sys/kernel/shm_rmid_forced in proc(5).

(base) ubuntu@ubuntu:~/muke01/01sharemem$ ipcs -m  | grep 1234
0x00001234 1015831    ubuntu     640        56         1   

attach 是1了,代表有一个process attach了共享内存的地址。

c++ 复制代码
//
// Created by ubuntu on 9/7/23.
//
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>

struct st_pid {
    int pid; // 进程编号
    char names[51]; // 进程名称
};

int main(int argc, char *argv[]) {
    int key = 0x1234;
    int shmid;
    // 调用shmget函数获取或创建共享内存
    if ((shmid = shmget(key, sizeof(struct st_pid), 0640 | IPC_CREAT)) == -1) {
        printf("shmget(Ox1234) failed \n");
        return -1;
    }
    struct st_pid *stpid = 0;
    // 调用shmat函数把共享内存连接到当前进程的地址空间,On success, shmat() returns the address of the attached shared memory segment
    if ((stpid = (struct st_pid *) shmat(shmid, stpid, 0)) == (void *) -1) {
        printf("shmat failed\n");
        return -1;
    }
    printf("pid=%d,name=%s\n", stpid->pid, stpid->names);

    stpid->pid = getpid();
    strcpy(stpid->names, argv[1]);
    printf("pid=%d,name=%s\n", stpid->pid, stpid->names);

    sleep(10);
    //把共享内存从当前进程中分离
    shmdt(stpid);
    // 删除共享内存
//    if (shmctl(shmid, IPC_RMID, 0) == -1) {
//        printf("shmctl failed \n");
//        return -1;
//    }
}
相关推荐
engchina13 分钟前
Oracle ADB 导入 BANK_GRAPH 的学习数据
数据库·学习·oracle·graph
一丝晨光23 分钟前
gcc 1.c和g++ 1.c编译阶段有什么区别?如何知道g++编译默认会定义_GNU_SOURCE?
c语言·开发语言·c++·gnu·clang·gcc·g++
南城花随雪。34 分钟前
Spring框架之装饰者模式 (Decorator Pattern)
java·开发语言·装饰器模式
究极无敌暴龙战神X39 分钟前
前端学习之ES6+
开发语言·javascript·ecmascript
虞书欣的61 小时前
Python小游戏24——小恐龙躲避游戏
开发语言·python·游戏·小程序·pygame
Komorebi.py1 小时前
【Linux】-学习笔记03
linux·笔记·学习
FHYAAAX1 小时前
【机器学习】任务十:从函数分析到机器学习应用与BP神经网络
开发语言·python
汉克老师1 小时前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
程序员劝退师_1 小时前
Kafka学习笔记
笔记·学习·kafka
帅比九日2 小时前
【HarmonyOS NEXT】实战——登录页面
前端·学习·华为·harmonyos