Linux中的共享内存

Linux中的共享内存

先简单写写这里,后面有时间慢慢深挖

共享内存是啥

这里摘抄一下维基百科:

共享内存(shared memory)指在多处理器计算机系统中,可以被不同中央处理器访问的大容量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存。由于其他处理器可能也要存取,任一缓存数据更新后,共享内存就需要立即更新,否则不同处理器可能用到不同的数据(参见缓存一致内存一致)。

简单来说,共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最高效的方式。

操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。

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

相关函数

头文件

在Linux中对于共享内存的操作首先要包含头文件:

1
2
#include <sys/ipc.h>
#include <sys/shm.h>

创建或者获取共享内存——shmget函数

  • 作用:创建一段共享内存或者读取已经存在的共享内存

  • 函数原型:

    1
    
    int shmget(key_t key, size_t size, int shmflg);
    
  • 参数:

    • key:共享内存的键值,是一个整数,typedef unsigned int key_t,是共享内存在系统中的编号,不同共享内存的编号不能相同,这一点由程序员保证,个人经验之谈,key用十六进制表示比较好,因为一般在终端中查看时显示的也是十六进制,用十六进制设置比较直观也方便管理。
    • size:待创建的共享内存的大小,以字节为单位
    • shmflg:共享内存的访问权限,与文件的权限一样,0666|IPC_CREAT表示全部用户对它可读写,如果共享内存不存在,就创建一个共享内存
  • 返回值:

    • 成功:将返回有效的共享内存标识符
    • 失败或错误时,返回-1,并设置errno以指示错误。

连接共享内存——shmat函数

  • 作用:把共享内存连接到当前进程的地址空间

  • 函数原型:

    1
    
    void *shmat(int shm_id, const void *shm_addr, int shmflg);
    
  • 参数:

    • shm_id是由shmget函数返回的共享内存标识
    • 参数shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址
    • 参数shm_flg是一组标志位,通常为0。
  • 返回值:

    • 成功:返回一个指向共享内存第一个字节的指针
    • 失败:返回(void*) -1,并设置errno以指示错误的原因

分离共享内存——shmdt函数

  • 作用:将共享内存从当前进程中分离,相当于shmat函数的反操作

  • 函数原型:

    1
    
    int shmdt(const void *shmaddr);
    
  • 参数:

    • shmaddr:是shmat函数返回的地址
  • 返回值:

    • 成功:返回0

    • 失败:返回-1,并设置errno以指示错误的原因

删除共享内存——shmctl函数

  • 作用:控制共享内存,这里注意一下,shmctl不仅可以删除共享内存,还可以做其他事情,但删除是这个函数最主要也是最常用的功能

  • 函数原型:

    1
    
    int shmctl(int shm_id, int command, struct shmid_ds *buf);
    
  • 参数(删除):

    • shm_idshmget函数返回的共享内存标识符
    • commandIPC_RMID
    • buf填0
  • 返回值:

    • 删除成功返回0(其他有部分操作不是,详情看man手册)
    • 失败返回-1,并会设置对应errno
  • 另外:用root创建的共享内存,不管创建的权限是什么,普通用户无法删除

其他命令操作

  • ipcs -m可以查看系统的共享内存,内容有键值(key),共享内存编号(shmid),创建者(owner),权限(perms),大小(bytes)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    ➜  c git:(main) ✗ ipcs -m
    
    ------------ 共享内存段 --------------
    键        shmid      拥有者  权限     字节     连接数  状态      
    0x00000000 4          sugar      600        524288     2          目标       
    0x00000000 7          sugar      600        524288     2          目标       
    0x00000000 11         sugar      600        4194304    2          目标       
    0x00000000 12         sugar      600        524288     2          目标       
    0x00005005 13         sugar      640        56         0    
    
  • ipcrm -m共享内存编号,可以手工删除共享内存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    ➜  c git:(main) ✗ ipcrm -m 13
    ➜  c git:(main) ✗ ipcs -m    
    
    ------------ 共享内存段 --------------
    键        shmid      拥有者  权限     字节     连接数  状态      
    0x00000000 4          sugar      600        524288     2          目标       
    0x00000000 7          sugar      600        524288     2          目标       
    0x00000000 11         sugar      600        4194304    2          目标       
    0x00000000 12         sugar      600        524288     2          目标 
    

补充一个小demo

程序A创建共享内存并写入

代码:

 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
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>

struct st_pid {
    int pid;
    char name[51];
};

int main(int argc, char* argv[]) {

    int shmid;

    if ((shmid = shmget(0x5005, sizeof (st_pid), 0640 | IPC_CREAT)) == -1) {
        printf("shmget failed\n");
        return -1;
    }

    st_pid* stpid = nullptr;
    if ((stpid = (st_pid*)shmat(shmid, nullptr, 0)) == (void*)-1) {
        printf("shmat failed\n");
        return -1;
    }

    stpid->pid = 10010;
    strcpy(stpid->name, "JOJO");

    return 0;
}

执行结果:

1
2
3
4
5
6
7
8
9
➜  c git:(main) ✗ ipcs -m

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 4          sugar      600        524288     2          目标       
0x00000000 7          sugar      600        524288     2          目标       
0x00000000 11         sugar      600        4194304    2          目标       
0x00000000 12         sugar      600        524288     2          目标       
0x00005005 14         sugar      640        56         0           

程序B读取共享内存并读出

代码:

 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
#include <stdlib.h>
#include <iostream>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

struct st_pid {
    int pid;
    char name[51];
};

int main() {
    int shmid;
    if ((shmid = shmget(0x00005005, sizeof(st_pid), 0640 | IPC_CREAT)) == -1) {
        printf("shmget failed\n");
        return -1;
    }

    st_pid* test2 = nullptr;
    if ((test2 = (st_pid*)shmat(shmid, nullptr, 0)) == (void*)-1) {
        printf("shmat failed\n");
        return -1;
    }

    printf("name: %s\n", test2->name);
    printf("pid: %d\n", test2->pid);

    if (shmctl(shmid, IPC_RMID, nullptr) == -1) {
        printf("删除失败\n");
        return -1;
    }

    return 0;
}

执行结果:

1
2
name: JOJO
pid: 10010
1
2
3
4
5
6
7
8
➜  c git:(main) ✗ ipcs -m

------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     连接数  状态      
0x00000000 4          sugar      600        524288     2          目标       
0x00000000 7          sugar      600        524288     2          目标       
0x00000000 11         sugar      600        4194304    2          目标       
0x00000000 12         sugar      600        524288     2          目标   
0%