进程间通信---共享内存
------->双向通信
------->仅是一块内存,可以随意写入数据
------->无同步互斥
------->生命周期随内核
-----共享内存是最快的IPC形式.共享内存的本质是物理内存,一旦这样的内存映射到共享它的进程的地址空间,这些空间不涉及内核.
进程是一个独立的资源管理单元,不同进程间的资源是独立的,不能在一个进程中访问另一个进程的用户空间和内存空间。但是,进程不是孤立的,不同进程之间需要信息的交互和状态的传递,因此需要进程间数据的传递、同步和异步的机制。
当然,这些机制不能由哪一个进程进行直接管理,只能由操作系统来完成其管理和维护,Linux提供了大量的进程间通信机制,包括同一个主机下的不同进程和网络主机间的进程通信,如下图所示:
共享内存是进程间通信中最简单的方式之中的一个。
共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。
共享内存同意两个或很多其他进程訪问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。
当一个进程改变了这块地址中的内容的时候,其他进程都会察觉到这个更改
用ftok()函数获得一个ID号
应用说明,在IPC中,我们经经常使用用key_t的值来创建或者打开信号量,共享内存和消息队列。
key_t ftok(const char *pathname, int proj_id);
參数 | 描写叙述 |
---|---|
pathname | 一定要在系统中存在而且进程能够訪问的 |
proj_id | 一个1-255之间的一个整数值,典型的值是一个ASCII值。 |
当成功运行的时候,一个key_t值将会被返回。否则-1被返回。我们能够使用strerror(errno)来确定详细的错误信息。
考虑到应用系统可能在不同的主机上应用,能够直接定义一个key,而不用ftok获得:
#define IPCKEY 0x344378
创建共享内存
进程通过调用shmget(Shared Memory GET,获取共享内存)来分配一个共享内存块。
int shmget(key_t key ,int size,int shmflg)
參数 | 描写叙述 |
---|---|
key | 一个用来标识共享内存块的键值 |
size | 指定了所申请的内存块的大小 |
shmflg | 操作共享内存的标识 |
返回值:假设成功,返回共享内存表示符,假设失败,返回-1。
该函数的第一个參数key是一个用来标识共享内存块的键值。
该函数的第二个參数size指定了所申请的内存块的大小
第三个參数shmflg是一组标志。通过特定常量的按位或操作来shmget
映射共享内存
shmat()是用来同意本进程訪问一块共享内存的函数。将这个内存区映射到本进程的虚拟地址空间。
int shmat(int shmid,char *shmaddr,int flag)
參数 | 描写叙述 |
---|---|
shmid | 那块共享内存的ID。是shmget函数返回的共享存储标识符 |
shmaddr | 是共享内存的起始地址,假设shmaddr为0,内核会把共享内存映像到调用进程的地址空间中选定位置。假设shmaddr不为0,内核会把共享内存映像到shmaddr指定的位置。所以一般把shmaddr设为0。 |
shmflag | 是本进程对该内存的操作模式。假设是SHM_RDONLY的话,就是仅仅读模式。 其他的是读写模式 |
成功时,这个函数返回共享内存的起始地址。失败时返回-1。
共享内存解除映射
当一个进程不再须要共享内存时,须要把它从进程地址空间中多里。
int shmdt(char *shmaddr)
參数 | 描写叙述 |
---|---|
shmaddr | 那块共享内存的起始地址 |
成功时返回0。失败时返回-1。
应通过调用 shmdt(Shared Memory Detach。脱离共享内存块)函数与该共享内存块脱离。
将由 shmat 函数返回的地址传递给这个函数。假设当释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。
控制释放
shmctl控制对这块共享内存的使用
函数原型
int shmctl( int shmid , int cmd , struct shmid_ds *buf );
參数 | 描写叙述 |
---|---|
shmid | 是共享内存的ID。 |
cmd | 控制命令 |
buf | 一个结构体指针。 IPC_STAT的时候,取得的状态放在这个结构体中。假设要改变共享内存的状态,用这个结构体指定。 |
当中cmd的取值例如以下
cmd | 描写叙述 |
---|---|
IPC_STAT | 得到共享内存的状态 |
IPC_SET | 改变共享内存的状态 |
IPC_RMID | 删除共享内存 |
返回值: 成功:0 失败:-1
代码演示
comm.h
1 #define PATHNAME "." 2 #define PROJ_ID 0x6666 3 #include<stdio.h> 4 #include<sys/types.h> 5 #include<sys/ipc.h> 6 #include<sys/shm.h> 7 int createShm(int size); 8 int destroyShm(int shmid); 9 int getShm(int size);
comm.c
1 #include "comm.h" 2 #include <sys/shm.h> 3 #include<stdlib.h> 4 int commShm(int size,int flag) 5 { 6 key_t key = ftok(PATHNAME,PROJ_ID); 7 if(key<0){ 8 perror("ftok"); 9 return -1; 10 } 11 int shmid = 0; 12 if(shmid = shmget(key,size,flag)<0){ 13 perror("shmget"); 14 return -2; 15 } 16 return shmid; 17 } 18 int destroyShm(int shmid){ 19 if(shmctl(shmid,IPC_RMID,NULL)){ 20 perror("shmctl"); 21 return -1; 22 } 23 return 0; 24 25 } 26 int createShm(int size){ 27 return commShm(size,IPC_CREAT|IPC_EXCL|0666); 28 29 } 30 int getShm(int size){ 31 return commShm(size,IPC_CREAT); 32 33 }
server.c
1 #include "comm.h" 2 int main(){ 3 int shmid = getShm(4096); 4 sleep(1); 5 char *addr =(char* )shmat(shmid,NULL,0); 6 sleep(2); 7 int i=0; 8 while(i<26){ 9 addr[i]='A'+i; 10 i++; 11 addr[i]=0; 12 sleep(1); 13 14 } 15 shmdt(addr); 16 sleep(2); 17 return 0; 18 }
client.c
1 #include "comm.h" 2 #include<stdlib.h> 3 int main(){ 4 int shmid=createShm(4096); 5 char *addr = shmat(shmid,NULL,0); 6 sleep(2); 7 int i=0; 8 while((i++)<26){ 9 printf("client# %s\n",addr); 10 sleep(1); 11 } 12 shmdt(addr); 13 sleep(2); 14 destroyShm(shmid); 15 return 0; 16 }