我们知道每个进程都有一个自己的PCB和一个地址空间,每个进程间是相互独立的,无法相互看到对方的地址空间,因此就不能通信,但是为了能达到进程间通信,人们发明了许多方法,共享内存是其中之一。
(一)共享内存的原理
进程间通信的本质是使不同的进程可以看到同一份资源,这份资源称之为临界资源,那么这里的共享内存即是这份临界资源。
所以,首先应该创建一段共享存储区。然后使其要访问这个共享存储区的每一个进程都要将该共享存储区连接至每个进程的地址空间。
当通信完成后,所有的进程需要对其去关联,通俗点讲,也就是说将这些进程和这块共享存储区的连接断开,并由创建共享存储区的进程释放该共享存储区。
注意:建议用户申请的共享存储块的大小最好是系统页面大小的整数倍。因为,如果用户申请的共享存储段的大小不是页面大小的整数倍的话,那么系统会采用向上对齐至页面大小的整数倍,但是用户可以使用的还是自定义的存储段。在linux系统下,页面大小默认为4kb(4096字节)。
原理图:
(二).所用接口函数
1.shmght:创建一个新的共享内存或获得一个现有的共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
参数解释:
key:标识IPC资源,是一个端口或者由ftok函数产生。
size:共享内存的大小。
flag:有两个选项IPC_CREAT和IPC_EXCL,(这里不赘述,具体用法看前几篇博客)。
返回值:
成功时返回一个新创建的共享内存标识符或者一个已存在的共享内存标识符。取决于shmglg参数。
失败时返回-1并设置错误码。
2.shmat:挂接 ,将进程的地址空间与共享内存连接起来。
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数说明:
shmid: 共享内存标识符(ID)
shmaddr: shmaddr = 0,则存储段连接到由内核选择的第一个可用地址上。一般推荐使用该方法,因为内核最清楚进程的地址空间。
shmflg:
若指定了SHM_RDONLY位,则以只读方式连接此段,否则以读写方式连接此段,一般置0;
3.shmdt(去关联)
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
函数说明:
当对共享内存的操作已经结束时,则需调用shmdt与共享内存分离。与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存。
需要注意的是,该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。
返回值:
若成功,返回0,并将shmid_ds结构中的shm_nattch计数器值减一;若出错,返回-1.
4.4.shmctl:删除共享内存段。
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数说明:
shmid:共享内存标识符(ID);
cmd:指定的操作。
执行删除:设置为IPC_RMID。
buf:删除时忽略该参数,设置为NULL
返回值:若删除成功,返回0;出错,返回-1
(三)代码验证:
思路:
进程server创建共享内存,并与之连接,进程client获取共享内存的标识符,并与之关联起来,然后进程server将数据写入共享内存,同时,进程client就可以看见进程server写的数据了,
然后让进程client读取共享存储段的数据。最终,通信完成后,进程server和进程client都要断开和共享内存的关联,并由进程server释放共享内存。
comm.h
1 #ifndef _COMM_H_
2 #define _COMM_H_
3
4 #include <stdio.h>
5 #include <sys/types.h>
6 #include <sys/ipc.h>
7 #include <sys/shm.h>
8
9 #define FILENAME "."
10 #define PROJ_ID 100
11
12 int get_shm(int size);
13 int creat_shm(int size);
14 int destroy_shm(int shmid);
15 //char *at_shm(int shmid);
16 //int dt_shm(char* addr);
17
18 #endif
comm.c
#include "comm.h"
2
3 static int comm_shm(int size,int flag)
4 {
5 key_t _key = ftok(FILENAME,PROJ_ID);
6 if(_key<0)
7 {
8 perror("ftok");
9 return -1;
10 }
11 int shmid = shmget(_key,size,flag);
12 if (shmid <0)
13 {
14 perror("shmid");
15 return -2;
16 }
17 return shmid;
18 }
19
20 int get_shm(int size)
21 {
22 return comm_shm(size,IPC_CREAT);
23 }
24
25 int creat_shm(int size)
26 {
27 return comm_shm(size,IPC_CREAT|IPC_EXCL|0666);
28 }
29
30 int destroy_shm(int shmid)
31 {
32 if (shmctl(shmid,IPC_RMID,NULL)<0)
33 {
34 perror("destroy_shm");
35 return -3;
36 }
37 return 0;
38 }
39
40 //char *at_shm(int shmid)
41 //{
42 // return shmat(shmid,NULL,0);
43 //}
44
45 //int dt_shm(char* addr)
46 //{
47 // return shmdt(addr);
48 //}
49 //
server.c
1 #include "comm.h"
2
3 int main()
4 {
5 //创建共享内存
6 int shmid = creat_shm(4095);
7 char* buf;
8 //挂接
9 buf = shmat(shmid,NULL,0);
10 sleep(5);
11 int count = 0;
12 while (count < 20)
13 {
14 buf[count++] = 'a'+ count;
15 buf[count] = '\0';
16 sleep(1);
17 }
18 shmdt(buf);//去关联
19 destroy_shm(shmid);//删除共享内存
20 return 0;
21 }
client.c
1 #include "comm.h"
2
3 int main()
4 {
5 //获得共享内存
6 int shmid = get_shm(0);
7 char* buf;
8 //挂接
9 buf = shmat(shmid,NULL,0);
10 int count = 0;
11 while (count++ < 15)
12 {
13
14 printf("client# %s\n",buf);
15 sleep(1);
16 }
17
18 shmdt(buf);//去挂接
19 return 0;
20 }
结果:
(四)相关指令
查看共享内存 ipcs -m
删除共享内存 ipcrm -m shmid