NetBSD 进程间通讯系统分析
2006-08-09 22:57:11 来源:WEB开发网NOTE: 结合 msgsnd(2), msgrcv(2) 理解起内存分配和释放方法, 相当巧妙.
共享存储区 (shared memory)
NOTE: 这一节可以和 BsdSrcUvm 一文结合阅读.
shmget(2)
shmget(2) 的处理过程是简单的, 如果已存在相应的 shm 区域, 注意与消息队列不同, shmkey 到实体的映射是一个线性查找的过程.
我们来看其分配共享内存段的过程 shmget_allocate_segment(). 函数首先验证参数, 然后借助 shm_last_free 找出一个空闲的表项空间 (处理线性搜索空闲空间的时候的标准代码). 接下来我们创建 shm_handle 和我们所使用的 uobj (See BsdSrcUvm), 以便使用共享内存的进程连入自己的地址空间. 设置初始参数, 唤醒等待的进程 (XXX. 这是怎么来的?). 纵观整个过程, 我们无须真正分配由 size 指定的空间而只是开辟以此为限的 uobj, 到真正需求这个空间时, 缺页中断机制会为它分配的.
shmat(2)
shmat(2) 的过程相当好理解. 在验证参数正确性后, 根据参数设定建议的映射地址, 然后使用 uvm_map() 将相应 uobj 映射过来即可.
shmdt(2)
XXX. 跟上面一样, 太简单了, 重新组织. 讲透.
信号量 (semaphore)
数据结构
一个单独信号量在 __sem 结构中, 包括信号量值, 最近一次操作的进程的进程号, 等待信号量增加和等待信号量为 0 的数目. semget(2) 分配的结构 semid_ds 可以管理一组信号量, 可以并行原子操作.
semget(2)
同样的, 在 IPC_PRIVATE 或找不到相应的信号量结构的时候, 我们就要分配新的 semid_ds. 线性查找到一个空的 semid_ds 区, 填入自己的参数, 在 sem 池 (sem 数组, 由 semtot 控制) 中获得用户要求数目的 __sem 结构, 初始化后即可完成.
semop(2)
semop(2) 系统调用的语义比较复杂. 建议先认真阅读 manpage 熟悉一下. 嗯.
我们忽略前面的准备过程, 直接看 636 行的操作循环, 它的目的是完成所有操作并保证他们是原子的 --- 比如我们先对第一个信号量加 1, 在对第二个信号量减 1 的时候发生等待, 我们要取消原来的对第一个信号量加 1 的操作 (undo), 才能保证其原子性. 在 639 行的循环中, 我们尝试完成每一个操作 . 如果没有完成, 我们把原来对信号量做的操作做回去, 然后等待.
在所有操作都完成的时候 (原子的完成了), 我们跳到了 742 行的 done 标记处 . 我们要在这里处理 UNDO 的情况 --- SYSV sem 的复杂性皆来自这个 UNDO 功能, 我们在 semu_list 里记下操作, 以备 undo. 最后, 我们修改 semid_ds 上的数据, 唤醒等待在信号量上的进程, 完成操作.
更多精彩
赞助商链接