目录
system V共享内存
编辑
共享内存函数
共享内存的建立过程
shmget函数
shmctl函数
shmat函数
shmdt函数
实例代码
共享内存的特点
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间(即内存通过某种映射关系连接到了进程使得进程能够访问这段内存),这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
我们之前的文章介绍的管道通信本质是基于文件的,OS没有做过多的设计工作。
而system V进程间通信是OS特地设计的通信方式,是为了想尽一切办法让不同的进程看到同一份资源。(具体的方法就是将物理内存映射到进程的地址空间中,本质就是修改页表,在虚拟地址空间中开辟空间,用的是系统调用接口)
1、申请共享内存
2、共享内存挂接到地址空间
3、去关联共享内存
4、释放共享内存
前两点式通信过程,后两点是释放资源的过程
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样 的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
//comm.h 1 #ifndef _COMM_H_ 2 #define _ COMM_H_ 3 #include
4 #include 5 #include 6 #include 7 8 #define PATHNAME "." 9 #define PROJ_ID 0x6666 10 #define SIZE 4096 11 12 #endif //sever.c 1 #include "comm.h" 2 #include
3 int main() 4 { 5 key_t k = ftok(PATHNAME, PROJ_ID); 6 if(k < 0) 7 { 8 perror("ftok"); 9 return 1; 10 } 11 printf("%x\n", k); 12 int shmid = shmget(k, SIZE, IPC_CREAT|IPC_EXCL|0644); 13 if(shmid < 0) 14 { 15 perror("shmget"); 16 return 2; 17 } 18 19 return 0; 20 }
运行这一段代码,便能成功创建一块共享内存,可以使用ipcs -m命令查看
shm的生命周期是随内核的,进程不主动删除或者用命令删除,共享内存就一直存在,直到关机重启。
释放这一块空间则是用命名ipcrm -m [shmid]
当然这块内存的释放也有函数可以使用,没必要在命令行进行这么麻烦的操作
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
在上面创建的server.c中结尾加上这一段
sleep(10); shmctl(shm, IPC_RMID, NULL); sleep(10);
运行这一段代码,可以看到共享内存一开始被创建,然后过了一会就被删除了
至此我们就完成了共享内存的创建与释放,那么接下来要解决的问题就是如何进行映射了
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid:共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存的第一个节;失败返回-1
1 #include "comm.h" 2 #include
3 #include 4 #include 5 #include 6 #include 7 int main() 8 { 9 key_t _key = ftok(PATHNAME, PROJ_ID); 10 if(_key < 0) 11 { 12 perror("ftok"); 13 return -1; 14 } 15 printf("%x\n",_key); 16 int shmid = 0; 17 if((shmid = shmget(_key, SIZE, IPC_CREAT|IPC_EXCL|0644)) < 0) 18 { 19 perror("shmget"); 20 return -2; 21 } 22 23 printf("shmid: %d\n", shmid); 24 25 printf("attach begin!\n"); 26 sleep(3); 27 char* mem = shmat(shmid, NULL, 0); 28 if(mem == (void*)-1) 29 { 30 perror("shmat"); 31 return 3; 32 } 33 printf("attach end!\n"); 34 sleep(3); 35 36 shmctl(shmid, IPC_RMID, NULL); 37 38 return 0; 39 }
说明:
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标识,则以shmaddr为连接地址
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHML BA的整数倍。公式:shmaddr - (shmaddr % SHML BA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr:由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段
取消关联即在上面那段代码中的释放共享内存前加入
shmdt(mem);
即可取消进程与共享内存间的关联
//comm.h 1 #ifndef _COMM_H_ 2 #define _ COMM_H_ 3 #include
4 #include 5 #include 6 #include 7 8 #define PATHNAME "." 9 #define PROJ_ID 0x6666 10 #define SIZE 4096 11 12 #endif //server.c 1 #include "comm.h" 2 #include
3 int main() 4 { 5 key_t k = ftok(PATHNAME, PROJ_ID); 6 if(k < 0) 7 { 8 perror("ftok"); 9 return 1; 10 } 11 printf("%x\n", k); 12 int shmid = shmget(k, SIZE, IPC_CREAT|IPC_EXCL|0644); 13 if(shmid < 0) 14 { 15 perror("shmget"); 16 return 2; 17 } 18 19 printf("shmid: %d\n", shmid); 20 21 sleep(5); 22 char* mem = shmat(shmid, NULL, 0); 23 while(1) 24 { 25 printf("client msg# %s\n", mem); 26 sleep(1); 27 } 28 shmdt(mem); 29 30 shmctl(shmid, IPC_RMID, NULL); 31 32 return 0; 33 } 34 //client.c 1 #include "comm.h" 2 #include
3 #include 4 int main() 5 { 6 key_t k = ftok(PATHNAME, PROJ_ID); 7 if(k < 0) 8 { 9 perror("ftor"); 10 return 1; 11 } 12 int shmid = shmget(k, SIZE,0644); 13 if(shmid < 0) 14 { 15 perror("shmget"); 16 return 2; 17 } 18 19 char* mem = shmat(shmid, NULL, 0); 20 21 int i = 0; 22 while(1) 23 { 24 mem[i] = 'A' + i; 25 sleep(1); 26 i++; 27 mem[i] = 0; 28 } 29 30 shmdt(mem); 31 32 return 0; 33 }
可以看到我们实现了client端向server端通过共享内存进行信息的传输
1、共享内存是所有进程通信中速度最快的,因为它没有使用OS接口,拷贝次数减少
2、不提供任何保护机制(同步与互斥)