我们都知道,多线程与多进程都可以访问共享内存;但是二者访问的共享内存是有区别的,是不一样的。多个线程可以由同一进程产生,它们共享同一进程的地址空间,如果这些线程需要访问同一块内存空间,定义一个全局变量就可以满足;但多个进程之间的地址空间是相互独立的,如果多进程需要访问同一块内存,则不能使用全局变量,必须使用共享内存,而且共享内存是多进程间信息共享与交换最高效的方式。
共享内存针对多进程的访问并未提供锁机制,是允许多个任意进程(不要求具有父子关系)访问同一块内存空间的。进程可以将共享内存链接到自己的地址空间进行使用处理,也就是说如果进程A修改了共享内存中的某个变量值,进程B读取到该变量的值可能就是已经修改过的。在一个进程访问共享内存的同时,不会阻止其他进程对该共享内存的读写。为了防止整个世界处于混乱的状态,还好聪明的操作系统提供了信号量实现进程间同步,使得多进程访问共享内存时实现加锁的操作。
下面几个函数都是用于操作共享内存的:
Function-shmget
int shmget(key_t key, size_t size, int shmflg); linux下查询该函数 man shmget
功能:该函数用于获取/创建(如果不存在,则创建)共享内存
key: 共享内存的键值,一般用十六进制数表示;不同共享内存的key值是不一样的
size: 待创建共享内存的大小,单位为 byte
shmflg: 共享内存的访问权限,和文件权限差不多一个意思理解,比如设置该值为 0666|IPC_CREAT, 表示全部用户对它可读(r,4)可写(w,2),(注意 0666 是八进制数,前面的0不能省略); 而且 IPC_CREAT 则表示,如果该共享内存不存在就创建它,如果没有这个宏出现,则只能获取已经存在的共享内存,不会创建不存在的。
返回值:成功--放回该共享内存的id(一个大于0的整数),失败--返回-1(导致失败原因一般有二:系统内存本身不够用;没有权限)
shmget函数使用示例代码如下,运行之后可使用 ipcs -m 命令查看系统共享内存:
如果共享内存的key使用十进制数也可以,但是一般不推荐这么使用(也是有一定的道理)
十进制1024对应十六进制0x400
共享内存一般使用程序进行创建或删除,也可以手动使用命令 ipcrm -m shmid 进行删除
Function-shmat
void *shmat(int shmid, const void *shmaddr, int shmflg); man shmat
功能:该函数用于把共享内存连接到当前进程的地址空间中;
shmid: 该参数由函数shmget()返回;
shmaddr: 指定共享内存连接到当前进程中的地址位置,通常填0,表示让系统自己选择共享内存的连接地址;
shmflg: 标志位,通常填0;
返回值:成功--返回共享内存起始地址,失败--返回(void*)-1
Function-shmdt
int shmdt(const void *shmaddr); man shmdt
功能:该函数用于将共享内存从当前进程中分离出去,相当于 shmat 的反向操作
shmaddr: shmat()函数返回的地址;
返回值:成功--0,失败-- -1
shmat函数与shmdt函数的示例代码如下,以及代码测试结果:
(注意:在使用 shmat 函数连接共享内存到当前进程后,需要与 (void*)-1 进行判断是否成功,而不仅仅是与-1进行判断)
Function-shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf); man shmctl
功能:该函数用于操作共享内存,最常用的操作是删除共享内存
shmid: shmget函数返回的共享内存id
cmd:操作共享内存的指令,若需要删除,则填 IPC_RMID
buf:操作共享内存的数据结构地址,如果需要删除共享内存,则填0就行
返回值:成功--0,失败-- -1
该函数示例代码及运行结果如下:
敲黑板:共享内存的数据结构中只能使用c/c++内置的数据类型,不能使用STL容器(STL会在堆区动态分配内存,这部分内存不属于共享内存),如果将刚才的结构体中的 cname 变量改为 string,则运行程序会报错。
以上为关于共享内存的基础处理及使用方法,共勉。