内容简介:共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
一、IPC(Inter-Process Communication,进程间通信)对象的介绍
System V 的IPC对象有共享内存、消息队列、信号灯。
注意:在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号灯,每个IPC的对象都有唯一的名字,称为”键”(key)。通过”键”,进程能够识别所用的对象。”键”与IPC对象的关系就如同文件名称于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够公用一个文件。而在 IPC的通讯模式下,通过”键”的使用也使得一个IPC对象能为多个进程所共用。
二、共享内存的介绍
<1>共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
<2>为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
<3>由于多个进程共享一段内存,因此也需要依靠某种同步机制。
三、共享内存的特点
四、共享内存的操作流程
<1>创建/打开共享内存
<2>映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
<3>撤销共享内存映射
<4>删除共享内存对象
五、相关 API
A.获取一块共享内存
功能:分配一块共享内存
返回值:
调用成功返回一个shmid(类似打开一个或创建一个文件获得的文件描述符一样);
调用失败返回-1。
参数说明:
<1>key标识共享内存的键值(就像文件的标识是文件名):0 / IPC_PRIVATE。
当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;
如果key的取值为0,而参数shmflg中设置了IPC_CREATE这个标志,则同样创建一块新的共享内存。
通过这种方式分配的共享内存,一般用来亲缘关系的进程间通信。
注意:我们一般是通过ftok这个函数获取键值
功能 : 获取一个IPC对象的键值
参数说明:
pthname就是你指定文件名的路径(该文件必须是存在而且可以访问的),一般情况我们都写一个目录
proj_id : 和pthname一起完成创建键值的参数,虽然为int,但是只有8个比特被使用。一般我们写一个字符代替。
例如:
案例:
运行的结果:
<2>size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一个进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE = 4096字节)。
<3>shmflg有效的标志包括IPC_CREAT 和IPC_EXCL,他们的功能与open()的O_CREAT和O_EXCL相当。
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则直接打开已存在的
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误
例子一:假设键值为key,创建一个共享内存大小为4k,访问权限为066,如果已经存在则返回其标识号
1 2 3 4 5 6 |
int shmid; if( (shmid = shmget(key,4 * 1024,0666 | IPC_CREAT)) < 0) { perror("Fail to shmget"); exit(EXIT_FAILURE) } |
例子二、假设键值为key,创建一个共享内存大小为1k,访问权限为0666,如果已经存在则报错
1 2 3 4 5 6 |
int shmid; if((shmid = shmget(key,1024,0666 | IPC_CREAT | IPC_EXCL)) < 0) { perror("Fail to shmget"); exit(EXIT_FAILURE); } |
B.共享内存的映射
函数shmat将标识号为shmid共享内存映射到调用进程的地址空间中。
参数说明:
shmid : 要映射的共享内存区标识符
shmaddr : 将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)
shmflg : SHM_RDONLY 共享内存只读
默认0:共享内存可读写。
返回值 :调用成功放回映射后的地址 ,出错放回(void *)-1;
C.取消共享内存与用户进程之间的映射
参数shmaddr是shmat映射成功放回的地址。
注意:当一个进程不再需要共享内存段时,它将调用shmdt()系统调用取消这个段,但是,这并不是从内核真正地删除这个段,而是把相关shmid_ds结构的shm_nattch域的值减1,当这个值为0时,内核才从物理上删除这个共享段。
D.控制共享内存
参数说明:
shmid 共享内存标识ID
cmd IPC_STAT得到共享内存的状态
IPC_SET改变共享内存的状态
IPC_RMID删除共享内存
buf 是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。如果要改变共享内存的状态,用这个结构体指定;
注意:
1.IPC_RMID命令实际上不从内核删除一个段,而是仅仅把这个段标记为删除,实际的删除发生最后一个进程离开这个共享段时。
2.当cmd为IPC_RMID时,第三个参数应为NULL。呵呵,大部分我们都是这样做,用这个函数删除共享内存。
案例探究:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
#include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #define BUFF_SIZE 1024 int father_do_work(int shmid) { char *buf; void *shmaddr; sem_t *prsem; sem_t *pwsem; //有名信号量 if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED) { perror("Fail to sem open"); return -1; } //有名信号量 if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED) { perror("Fail to sem open"); return -1; } //映射共享内存 if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1) { perror("Fail to shmat"); exit(EXIT_FAILURE); } buf = (char *)shmaddr; while(1) { if(sem_wait(pwsem) < 0) { perror("Fail to sem wait"); break; } printf(">"); fgets(buf,BUFF_SIZE,stdin); buf[strlen(buf) - 1] = '\0'; if(sem_post(prsem) < 0) { perror("Fail to sem post"); break; } if(strncmp(buf,"quit",4) == 0) { if(shmdt(shmaddr) < 0) { perror("Fail to shmaddr"); exit(EXIT_FAILURE); } break; } usleep(500); } return 0; } int child_do_work(int shmid) { char *buf; void *shmaddr; sem_t *prsem; sem_t *pwsem; // if((prsem = sem_open("rsem",O_CREAT,0666,0)) == SEM_FAILED) { perror("Fail to sem open"); return -1; } if((pwsem = sem_open("wsem",O_CREAT,0666,1)) == SEM_FAILED) { perror("Fail to sem open"); return -1; } //映射共享内存 if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1) { perror("Fail to shmat"); exit(EXIT_FAILURE); } buf = (char *)shmaddr; while(1) { if(sem_wait(prsem) < 0) { perror("Fail to prsem"); break; } printf("read buf : %s.\n",buf); if(sem_post(pwsem) < 0) { perror("Fail to pwsem"); break; } if(strncmp(buf,"quit",4) == 0) { if(shmdt(shmaddr) < 0) { perror("Fail to shmaddr"); exit(EXIT_FAILURE); } break; } } return 0; } int main() { int shmid; int pid; void *shmaddr; //创建共享内存 if((shmid = shmget(IPC_PRIVATE,BUFF_SIZE,0666 | IPC_CREAT)) < 0) { perror("Fail to shmget"); exit(EXIT_FAILURE); } if((pid = fork()) < 0) { perror("Fail to fork"); exit(EXIT_FAILURE); }else if(pid == 0){ child_do_work(shmid); }else{ father_do_work(shmid); wait(NULL); if(shmctl(shmid,IPC_RMID,NULL) < 0) { perror("Fail to shmctl"); exit(EXIT_FAILURE); } } exit(EXIT_SUCCESS); } |
运行结果:
以上所述就是小编给大家介绍的《进程间通信---共享内存》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 查看php-fpm开启的进程数以及每个进程的内存限制
- 详解Oracle实例内存结构和进程结构
- 进程物理内存远大于Xmx的问题分析
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped
- hadoop地址配置、内存配置、守护进程设置、环境设置
- Linux 进程间通信之System V 共享内存
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learning PHP & MySQL中文版
车立红 / 中国电力出版社 / 2007-06 / 36.00元
《Learning PHP & MySQL中文版》将介绍程序、模板和数据库的工作原理,讲述如何应对其中的挑战,并彻底地探索这些技术。一起来看看 《Learning PHP & MySQL中文版》 这本书的介绍吧!