内核资料收集
1. 概述
IPC是进程间通信(Interprocess Communication)的缩写,通常指允许用户态进程执行下列操作的一组进程机制:
a. 通过信号量与其它进程进行同步
b. 向其它进程发送消息或者从其它进程接收消息
c. 和其它进程共享一段内存区
IPC数据结构是在进程请求IPC资源(信号量/消息队列/或共享内存区)时动态创建的. 每个IPC资源都是持久的: 除非被进程显示地
释放,否则永远驻留
在内存中(直到系统关闭). IPC资源可以由任一进程使用,包括那些不共享祖先进程创建的资源的进程.
由于一个进程可能需要同类型的多个IPC资源,因此每个新资源都是使用一个32位的IPC关键字来标识的,这和系统的目录树中的文件
路径名类似. 每个IPC资源都有一个32位的IPC标识符, 这与和打开文件相关的文件描述符有些类似. IPC标识符由内核分配给IPC
资源, 在系统内部是唯一的,而IPC关键字可以由程序员自由地选择.
当两个或者更多的进程要通过一个IPC资源进程通信时, 这些进程都要引用该资源的IPC标识符.
2. 使用IPC资源
IPC资源创建: 调用semget()/msgget()/shmget()函数分别用于创建信号量/消息队列/共享内存. 这三个函数主要目的都是从IPC关键
字(作为第一个参数传递)中导出相应的IPC标识符,进程以后就可以使用这个标识符对资源进程访问. 如果还没有IPC资源和IPC关键字
相关联, 就创建一个新的资源. 如果一切都顺利,那么函数就返回一个正的IPC标识符; 否则,就返回一个错误码:
两个独立的进程想共享一个公共的IPC资源. 这可以使用两种方法来达到:
a. 这两个进程统一使用固定的, 预定义的IPC关键字. 这是最简单的情况,对于由很多进程实现的任一复杂应用程序也工作得很
好. 然而,另外一个无关的程序也可能使用了相同的IPC关键字.在这种情况下, IPC函数可能被成功地调用,但返回错误资源
的IPC标识符
b. 一个进程通过指定IPC_PRIVATE作为自己的IPC关键字来调用semget()/msgget()/shmget()函数. 一个新的IPC资源因此而被
分配,这个进程或者可以与应用程序中的另一个进程共享自己的IPC标识符,或者自己创建另一个进程. 这种方法确保IPC资源
不会偶然被其他应用程序使用.
semget()/msgget()/shmget()函数的最后一个参数可以包括三个标志. IPC_CREAT说明如果IPC资源不存在,就必须创建它;
IPC_EXCL说明如果资源已经存在而且设置了IPC_CREAT标志,那么函数就必定失败; IPC_NOWAIT说明访问IPC资源时进程从不阻
塞(典型情况如取得消息或获取信号量).
即使进程使用了IPC_CREAT和IPC_EXCL标志,也没有办法保证对一个IPC资源进行排它访问,因为其它进程也可能用自己的IPC标识
符引用这个资源.
为了把不正确地引用资源的风险降到最小, 内核不会在IPC标识符一空闲时就再利用它. 相反分配给资源的IPC标识符总是大于给
同类型的前一个资源所分配的标识符(唯一的例外发生在32位的IPC标识符溢出时). 每个IPC标识符都是通过结合使用与资源类型相关
的位置使用序号(slot usage sequence number), 已分配资源的任意位置索引(slot index)以及内核中为可分配资源所选定的最大值计算
出来的.如果我们使用s来表示位置使用序号, M来代表可分配资源的最大数目, i代表位置索引, 此处0<=i
...
ipc_id_ary数据结构有两字段: p和size. p字段是一个指向kern_ipc_perm数据结构的指针数组, 每个结构对应一个可分配资源.
size字段是这个数组的大小.最初数组为共享内存区/消息队列与信号量分别存放1,16,128个指针. 当太小时,内核动态地增大数组. 但是
每种资源都有个上限. 系统管理员可以修改/proc/sys/kernel/sem, /proc/sys/kernel/msgmni, /proc/sys/kernel/shmmni这三个文件以
改变这些上限.
每个kern_ipc_perm数据结构与一个IPC资源相关,并且包含下表所示的字段. uid/gid/cuid/cgid分别存放资源的创建者的用户标识
和数组标识符以及当前资源属主/组以及其它用户的读写访问权限.
semctl()/msgctl()/shmctl()都可以用来处理IPC资源. IPC_SET子命令允许进程改变属主的用户标识符和组标识符以及ipc_perm
数据结构中的许可权位掩码. IPC_STAT和IPC_INFO子命令取得和资源有关的信息. 最后IPC_RMID子命令释放IPC资源.
IPC资源创建后, 进程就可以通过一些专用函数对这个资源进程操作. 进程可以执行semop()函数获得或释放一个IPC信号量.
当进程希望发送或接收一个IPC消息时,就分别使用msgsnd()/msgrcv()函数. 最后,进程可以分别使用shmat()和shmdt()函数把一个共享
内存区附加到自己的地址空间中或者取消这种附加关系.
3. ipc系统调用
ipc函数都必须通过适当的linux系统调用实现. 在80x86体系结构中, 只有一个名为ipc()的系统调用. 当进程调用一个IPC函数
时, 如msgget()该函数实际上调用C库中的一个封装函数,该函数又通过传递msgget()的所有参数加上一个子命令码(本例中是
MSGGET)来调用ipc()系统调用. sys_ipc()服务例程检查子命令代码,并调用内核函数实现所请求的服务.