作者: tiankonguse | 更新日期:
之前了解cache的时候, 我曾说过:共享内存也仅仅只是一片内存而已. 现在来看看共享内存的基本操作吧.
前段时间, 我了解了共享内存,并写了一篇笔记: cache 的简单认识与思考 .
简单的说就是cache的数据储存在共享内存中, 而这片内存和程序中的内存其实是一样的,只是操作方式不同而已.
我今天花费了几个小时学习了一下共享内存,并简单的封装了一下, 然后为之后的 cache 做准备吧.代码在 github 上
下面我们就来简单的学习一下这些操作方式吧, 这是菜鸟级的记录, 大神直接忽视吧!
下面主要分两部分:
共享内存大概由四个操作组成: 申请查询内存、程序连接映射内存、程序断开映射内存、管理共享内存.
shmget 函数用来申请查询内存.
#include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
可以看到, 这个操作需要三个参数.
我们申请了共享内存并不代表我们可以直接使用这片内存.
大家都知道, 一个进程只能操作自己范围内的地址空间, 而申请的那片内存就不在自己的管辖范围内.这个时候我们就需要通过一种方法, 把那片内存映射到自己的范围内了.
shmat 函数用于把共享内存映射到进程的地址空间.
#include <sys/types.h> #include <sys/shm.h> void *shmat(int shmid, const void *shmaddr, int shmflg);
这个操作同样需要三个参数.
我们使用完了内存, 可能需要断开映射内存来节省资源.
shmdt 函数可以实现这个功能.
#include <sys/types.h> #include <sys/shm.h> int shmdt(const void *shmaddr);
这个操作只需要一个参数,就是这个内存地址的指针.
管理内存大概分这么三个部分: 查询共享内存的状态, 更新共享内存的状态, 删除这片共享内存.
shmctl 函数可以用来管理共享内存.
#include <sys/ipc.h> #include <sys/shm.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf);
这里只来看看 cmd 参数可以使用的值吧.如下
学完了共享内存的基本知识, 如果不来敲几行代码怎么可以呢?
仔细想想, 共享内存的作用就是用来临时储存数据的, 说的高大上点就是进程间通信的.
于是我是实现了这么几个小程序, 可以加深对共享内存的理解.
源代码见 github
核心代码如下:
Shm shm; pid = fork(); if (pid == 0) { char *shmaddr = shm.getAdr(); if (NULL == shmaddr) { printf("getAdr error. err=%s/n", shm.getLastError()); return -1; } snprintf(shmaddr, SIZE, "this is parent, but will print in child !"); printf("father end/n"); return 0; } else if (pid > 0) { sleep(3); char *shmaddr = shm.getAdr(); if (NULL == shmaddr) { printf("getAdr error. err=%s/n", shm.getLastError()); return -1; } printf("%s/n", shmaddr); printf("child end/n"); shm.delShm(); }
上面代码片段的含义是: 创建一个共享内存类的实例, 这样这个实例在 fork 的时候就会复制给子进程了, 进而可以和父进程共享这个内存了.
在父进程内, 先把内存映射到父进程,然后填充进一些信息.
在子进程内, 先等几秒, 这样就可以保证父进程把信息写进内存了, 然后子进程也把内存映射到子进程, 最后输出内存中的信息.
输出大概如下
tiankonguse:fork $ ./fork iShmID = 10813468 father end this is parent, but will print in child ! child end
进程间通信和父子进程通信没什么大的区别.
只要知道共享内存的key, 就可以读写同一片内存了, 进而就可以通信了.
源代码见 github
核心代码如下:
写进程:
Shm shm; Time *p_time; p_time = (Time *) shm.getAdr(); if (NULL == p_time) { printf("getAdr error. err=%s/n", shm.getLastError()); return -1; } srand(time(NULL)); for (int i = 0; i < TIME_NUM; i++) { struct timeval start; gettimeofday(&start, NULL); (p_time + i)->sec = start.tv_sec; (p_time + i)->usec = start.tv_usec; (p_time + i)->val = rand() % 100; }
读进程:
Shm shm; Time *p_time; p_time = (Time *) shm.getAdr(); if (NULL == p_time) { printf("getAdr error. err=%s/n", shm.getLastError()); return -1; } for (int i = 0; i < TIME_NUM; i++) { printf("i=%d sec=%lld usec=%lld val=%lld/n", i, (p_time + i)->sec, (p_time + i)->usec, (p_time + i)->val); }
大家可以看到, 这个和上面的父子进程没什么区别.
只是这里储存的不是字符串了, 而是若干个 sizeof(Time)
大小的内存了.
输出大概如下:
tiankonguse:readwrite $ make g++ -O0 -o read read.cpp -I../../../include/ -L../../../lib/ -static -lshm -pthread g++ -o write write.cpp -I../../../include/ -L../../../lib/ -static -lshm -pthread tiankonguse:readwrite $ ./write key=50462721 iShmID = 10944523 tiankonguse:readwrite $ ./read key=50462721 iShmID = 10944523 i=0 sec=1441286802 usec=81659 val=56 i=1 sec=1441286802 usec=81673 val=31 i=2 sec=1441286802 usec=81674 val=57
现在我对共享内存的接触还不多, 理解也可能有误, 欢迎大家吐槽.
下一步就开始进入cache 中 hash 的实现了.