进程间通信——IPC之共享内存
相关函数
1.创建共享内存shmget
原型:int shmget(key_t key, size_t size, int shmflg) 返回值: 创建成功,则返回一个非负整数,即共享内存标识; 如果失败,则返回-1.
参数:
key: //程序需要提供一个参数key,它为共享内存段提供一个外部名。(每个IPC对象都与一个键 即key相关联,然后此键再由内核变换为标识符)。还有一个特殊的键值IPC_PRIVATE, 它用于创建一个只属于该创建进程的新共享内存,通常不会用到;
size: //以字节为单位指定需要共享的内存容量。 shmflag: //包含9个比特的权限标志,它们的作用与创建文件时使用的mode标志是一样。由IPC_CREAT定义的一个特殊比特位,同时必须和权限标志按位或才能创建一个新的共享内存段。 (注意:若想创建的新IPC结构没有引用具有同一标识符的现有的IPC结构,就要同时指定IPC_CREAT 和 IPC_EXCL;共享内存属IPC中一种,它同样如此) 注: 权限标志对共享内存非常有用,因为它允许一个进程创建的共享内存可以被共享内存的创建者所拥有的进程写入,同时其它用户创建的进程只能读取共享内存。我们可以利用这个功能来提供一种有效的对数据进行只读访问的方法,通过将数据放共享内存并设置它的权限,就可以避免数据被其他用户修改。 2.将共享内存端挂载到自己地址空间shmat 第一次创建共享内存段时,它不能被任何进程访问。要想启动对该内存的访问,必须将其连接到一个进程的地址空间
该函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg) 返回值:调用成功返回挂载的虚拟地址空间起始地址,失败返回NULL
参数:
int shmid //是由shmget函数返回的共享内存标识。
const void *shmaddr //指定共享内存连接到当前进程中的地址位置,通常为0,表示让系统来选择 共享内存的地址。
int shmflg //是一组标志位,通常为0。它还可取:SHM_RND,用以决定是否将当前共享内存段连接到指定的shmaddr上。该参数和shm_addr联合使用,用来控制共享内存连接的地址,除非只计划在一种硬件上运行应用程序,否则不要这样指定。填0让操作系统自己选择是更好的方式。
SHM_RDONLY单独使用则是指让它使连接的内存段只读,否则以读写方式连接此内存段
3. 与共享内存段分离 shmdt
原型:int shmdt(const void *shmaddr)
参数:
shm_addr: shmat返回的地址指针。 成功时,返回0, 失败时,返回-1. NOTE: 仅仅是共享内存分离但并未删除它,其标识符及其相关数据结构都在;直到某个进程的IPC_RMID命令的调用shmctl特地删除它为止只是使得该共享内存对当前进程不再可用。
4. shmctl 共享内存控制函数
#include#include 原型: int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数:
shm_id : 是shmget返回的共享内存标识符。 cmd: 它可以取3个值: IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值 IPC_SET 如果进程有足够的权限就把共享内存的当前关联值设置为shmid_ds结构中给出的值 IPC_RMID 删除共享内存段 buf:是一个指针,包含共享内存模式和访问权限的结构。 buf指向的shmid_ds结构体 一定要包含下列一些参数:struct shmid_ds { uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; }
简单使用
简单用共享内存来再两进程间交换数据,比如交换一个结构体
代码如下:
1 #includewrite.c2 #include 3 #include 4 #include <string.h> 5 #include 6 #include 7 typedef struct Stu 8 { 9 int age; 10 char name[10]; 11 }Stu; 12 13 int main( void) 14 { 15 Stu s; 16 strcpy(s.name, "jack"); 17 //创建共享内存段 18 int id = shmget(1234, 8, IPC_CREAT|0644); 19 if( id == -1)perror( " shmget"),exit( 1); 20 //挂载到进程的地址空间 21 Stu* p = ( Stu*)shmat( id, NULL, 0); 22 23 int i =0; 24 while( 1) 25 { 26 s.age = i++; 27 memcpy(p, &s, sizeof(Stu)); //写到共享段中 28 sleep( 2); 29 } 30 return 0; 31 }
1 #includeread.c2 #include 3 #include 4 #include 5 #include 6 typedef struct Stu 7 { 8 int age; 9 char name[10]; 10 }Stu; 11 int main( void) 12 { 13 int id = shmget(1234, 8, 0); 14 if( id == -1)perror( " shmget"),exit( 1); 15 16 Stu* p = ( Stu*)shmat( id, NULL, 0); 17 while( 1) 18 { 19 printf(" age= %d, name= %s\n", p->age, p->name); 20 sleep(2); 21 } 22 return 0; 23 }
执行结果如下:
小结
优点:我们可以看到使用共享内存进行进程间的通信真的是方便而高效,而且函数的接口也简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,也加快了程序的效率。同时,它也不像匿名管道那样要求通信的进程有一定的父子关系。
缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存进行进程间通信时,往往要借助其他的手段来进 行进程间的同步工作。上面只是共享内存的一些简单的应用,当多个进程对共享内存进行访问时,并没有保证同步,所以我们还需要用其它的机制来实现它的同步机制,要解决此,通常会用到信号量(PV操作)来实现。但要基于此的实现,前提还需要熟悉信号量的操作以及这里的共享内存使用。要用共享内存模拟做出一个带同步机制"先进先出"的消息通道,对我等萌新并不太容易,所以还得放到以后再实现了。。