memwriter 和 memreader 程序中,共享内存的主要着重点都在 shm_open 和 mmap 函数上:在成功时,第一个调用返回一个备份文件的文件描述符,而第二个调用则使用这个文件描述符从共享内存段中获取一个指针。它们对 shm_open 的调用都很相似,除了 memwriter 程序创建共享内存,而 `memreader 只获取这个已经创建的内存:
int fd = shm_open(BackingFile, O_RDWR | O_CREAT, AccessPerms); /* memwriter */ int fd = shm_open(BackingFile, O_RDWR, AccessPerms); /* memreader */
有了文件描述符,接着对 mmap 的调用就是类似的了:
caddr_t memptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
mmap 的第一个参数为 NULL ,这意味着让系统自己决定在虚拟内存地址的哪个地方分配内存,当然也可以指定一个地址(但很有技巧性)。MAP_SHARED 标志着被分配的内存在进程中是共享的,最后一个参数(在这个例子中为 0 ) 意味着共享内存的偏移量应该为第一个字节。size 参数特别指定了将要分配的字节数目(在这个例子中是 512);另外的保护参数(AccessPerms )暗示着共享内存是可读可写的。
当 memwriter 程序执行成功后,系统将创建并维护备份文件,在我的系统中,该文件为 /dev/shm/shMemEx ,其中的 shMemEx 是我为共享存储命名的(在头文件 shmem.h 中给定)。在当前版本的 memwriter 和 memreader 程序中,下面的语句
shm_unlink(BackingFile); /* removes backing file */
将会移除备份文件。假如没有 unlink 这个语句,则备份文件在程序终止后仍然持久地保存着。
memreader 和 memwriter 一样,在调用 sem_open 函数时,通过信号量的名字来获取信号量。但 memreader 随后将进入等待状态,直到 memwriter 将初始值为 0 的信号量的值增加。
if (!sem_wait(semptr)) { /* wait until semaphore != 0 */
一旦等待结束,memreader 将从共享内存中读取 ASCII 数据,然后做些清理工作并终止。
共享内存 API 包括显式地同步共享内存段和备份文件。在这次的示例中,这些操作都被省略了,以免文章显得杂乱,好让我们专注于内存共享和信号量的代码。
即便在信号量代码被移除的情况下,memwriter 和 memreader 程序很大几率也能够正常执行而不会引入竞争条件:memwriter 创建了共享内存段,然后立即向它写入;memreader 不能访问共享内存,直到共享内存段被创建好。然而,当一个写操作处于混合状态时,最佳实践需要共享内存被同步。信号量 API 足够重要,值得在代码示例中着重强调。
总结
上面共享文件和共享内存的例子展示了进程是怎样通过共享存储来进行通信的,前者通过文件而后者通过内存块。这两种方法的 API 相对来说都很直接。这两种方法有什么共同的缺点吗?现代的应用经常需要处理流数据,而且是非常大规模的数据流。共享文件或者共享内存的方法都不能很好地处理大规模的流数据。按照类型使用管道会更加合适一些。所以这个系列的第二部分将会介绍管道和消息队列,同样的,我们将使用 C 语言写的代码示例来辅助讲解。
【编辑推荐】
- DomTerm:一款为Linux打造的终端模拟器
- 4种在Linux中检查默认网关或者路由器IP地址的方法
- 微软在 Windows 10 中搞了个真正的 Linux 内核
- 是时候深入了解Linux的系统结构
- Windows加速合体Linux!史上强大的命令行控制台来了
【责任编辑:庞桂玉 TEL:(010)68476606】
点赞 0
(编辑:ASP站长网)
|