• [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 
    
    • 1
    (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         
    
    • 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

    参数解析:
    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"命令输出的列。
    在这里插入图片描述

    //
    // Created by ubuntu on 9/7/23.
    //
    #include 
    #include 
    #include 
    #include 
    
    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);
    
    }
    
    • 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

    要注意的是这一部分:

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

    man 啥看shmat, 它返回的是void × 类型,所以要强制转换何曾 struct st_pid * , 因为是要用st_pid * 做的数据接收。
    同时手册明确说了,错误后是返回值是 void× 类型的 -1

    SYNOPSIS
           #include 
           #include 
    
           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.
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第三个参数: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).
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    (base) ubuntu@ubuntu:~/muke01/01sharemem$ ipcs -m  | grep 1234
    0x00001234 1015831    ubuntu     640        56         1   
    
    • 1
    • 2

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

    //
    // Created by ubuntu on 9/7/23.
    //
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    //    }
    }
    
    • 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
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    机器学习-学习笔记(一) --> (假设空间 & 版本空间)及 归纳偏好
    【PHP】Workerman开源应用容器的GatewayWorker 与 iOS-OC对接
    美团搜索粗排优化的探索与实践
    赋能工业数字化转型|辽宁七彩赛通受邀出席辽宁省工业互联网+安全可控先进制造业数字服务产业峰会
    MySQL之MVCC
    PLC中ST编程的基础知识
    无涯教程-Flutter - 安装步骤
    服务器之间免密登录
    React - 01
    LeetCode每日一题——2520. Count the Digits That Divide a Number
  • 原文地址:https://blog.csdn.net/weixin_40293999/article/details/132729503