(一)引入
(二)IPC 命名空间
(三)ipc_ips结构体
(四)ipc_id_ary结构体
(五)kern_ipc_perm结构体
(六)操作系统对IPC资源是如何管理的
(一)引入
在前三篇的博客中,我介绍了System V IPC在Linux中的三种方式:共享内存、消息队列以及信号量。观察这三种IPC资源的内核数据:
共享内存:
struct shmid_ds {struct ipc_perm shm_perm; /* Ownership and permissions */size_t shm_segsz; /* Size of segment (bytes) */time_t shm_atime; /* Last attach time */time_t shm_dtime; /* Last detach time */time_t shm_ctime; /* Creation time/time of lastmodification via shmctl() */pid_t shm_cpid; /* PID of creator */pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */shmatt_t shm_nattch; /* No. of current attaches */...};
消息队列:
struct msqid_ds {struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of creation or lastmodification by msgctl() */unsigned long msg_cbytes; /* # of bytes in queue */msgqnum_t msg_qnum; /* # number of messages in queue */msglen_t msg_qbytes; /* Maximum # of bytes in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */};
信号量:
struct semid_ds {struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime; /* Last semop time */time_t sem_ctime; /* Creation time/time of lastmodification via semctl() */unsigned long sem_nsems; /* No. of semaphores in set */};
我们不难发现,这三种IPC资源的数据结构中都包含了一个结构体ipc_perm,这就需要我们思考了,为什么要这么设计呢?跟着我的脚步往下走吧。
上图是在Linux内核中管理System V IPC的部分,接下来我将依照这个图来进行讲解。
(二)IPC 命名空间
在Linux内核中存在一个IPC命名空间,它是一种用于隔离不同进程间通信资源的机制。IPC命名空间允许在同一主机上创建多个独立的IPC资源集合,每个 IPC 命名空间都有自己的信号量、消息队列和共享内存等 IPC 对象。
通过IPC命名空间,可以实现不同进程之间的通信资源隔离,避免不同进程之间的通信资源相互干扰。
IPC命名空间里面用struct ipc_ids的结构体数组来管理各种IPC资源,该结构体数组中的每一个元素都代表一个IPC资源,比如ids[0]可能代表信号量,ids[1]可能代表消息队列等。
struct ipc_namespace{atomic_t count; struct ipc_ips ids[3];......
};
(三)ipc_ips结构体
struct ipc_ids结构体内容如下
struct ipc_ids {struct ipc_id_ary *entries;int in_use;int max_id;int seq;struct ipc_id_ary *free;struct ipc_id_ary *in_use_next;struct ipc_id_ary *in_use_prev;
};
- entries:存储 IPC 对象信息的数组,每个元素是一个 ipc_id_ary 结构体,用于存储特定类型的 IPC 对象的 id。
- in_use:当前已经被使用的 IPC 对象数量。
- max_id:当前 IPC 对象的最大 id。
- seq:IPC 对象的序列号。
- free:空闲的 ipc_id_ary 结构体链表,用于存储可以被重用的 IPC 对象 id。
- in_use_next 和 in_use_prev:指向已经被使用的 ipc_id_ary 结构体的链表的下一个和上一个元素。
(四)ipc_id_ary结构体
struct ipc_id_ary结构体内容如下:
struct ipc_id_ary {struct ipc_ids *ids;int in_use;int seq;struct kern_ipc_perm *p[IPCID_ARRAY_ENTRIES];
};
- ids:指向包含该 ipc_id_ary 结构体的 ipc_ids 结构体的指针,用于确定所属的 IPC 对象类型。
- in_use:表示该 ipc_id_ary 结构体当前是否被使用。
- seq:IPC 对象的序列号。
- p:一个长度为 IPCID_ARRAY_ENTRIES 的指针数组,用于存储指向 kern_ipc_perm 结构体的指针。kern_ipc_perm 结构体包含了 IPC 对象的权限信息等。
(五)kern_ipc_perm结构体
kern_ipc_perm结构体内容如下:
struct kern_ipc_perm {key_t key; // IPC 对象的键kuid_t uid; // 拥有者的用户 IDkgid_t gid; // 拥有者的组 IDkuid_t cuid; // 创建者的用户 IDkgid_t cgid; // 创建者的组 IDumode_t mode; // 权限模式unsigned int seq; // 序列号
};
(六)操作系统对IPC资源是如何管理的
可以发现,kern_ipc_perm跟共享内存、消息队列以及信号量中的ipc_perm的内容一致。
现在我们可以对操作系统如何管理IPC资源来进行总结了。
设计者通过先描述,再组织的思想将各种IPC资源通过结构体数组的数据结构来进行管理,所以对IPC资源的管理就变成了对数组的增删查改。
接下来我将通过 操作系统是如何查找一个特定的IPC对象(这里我以消息量为例子)的权限信息的,来讲解IPC管理。
用户进程传递IPC对象的唯一标识符给系统,系统根据这个标识符进行查找。
系统使用IPC对象的标识符作为索引,在全局的ipc_ids数组中进行查找。系统根据IPC对象的标识符,在全局的ipc_ids数组中查找到对应的ipc_ids结构体,获取IPC对象的全局序列号。
系统根据IPC对象的标识符计算出在ipc_ids数组中的位置,获取对应的ipc_ids结构体。在ipc_ids结构体中的entries数组中,根据IPC对象的全局序列号找到对应的ipc_id_ary结构体。
系统根据IPC对象的全局序列号,在entries数组中查找对应的ipc_id_ary结构体。在ipc_id_ary结构体中,根据IPC对象的全局序列号和局部序列号,通过p指针数组找到要查找的IPC对象的位置。
系统根据IPC对象的全局序列号和局部序列号,在p指针数组中找到对应的IPC对象的位置。最终在kern_ipc_perm *p数组中找到了目标IPC对象的位置,可以对该IPC对象进行操作。
系统根据在ipc_id_ary结构体中找到的位置,定位到kern_ipc_perm *p数组中对应的IPC对象。
通过以上步骤,系统可以根据用户传递的IPC对象的唯一标识符,通过一系列的查找和计算,最终定位到系统中具体的IPC对象,确保进程可以安全地访问和操作IPC资源。这种查找过程涉及到对全局数据结构的索引和计算,以及多层的查找和定位操作。
而最后找到的kern_ipc_perm结构体虽然和三种IPC方式中的ipc_perm结构体名称不同,但其内容都是相同的,操作系统会利用类型转换,通过kern_ipc_perm结构体就可以访问其他三种IPC方式的结构体了,这就类似于一种多态的思想,通过父类指针,访问子类。