【摘要】本文详细讲述了在Linux内核中与命名空间概念相关的内核数据结构及其内在联系。
十、命名空间(namespace)相关数据结构
Linux内核通过数据结构之间互相的连接关系,形成了一套虚拟的命名空间的虚拟化概念。
10.1 struct pid_namespace
- \linux-2.6.32.63\include\linux\pid_namespace.h
struct pid_namespace
{
struct kref kref;
struct pidmap pidmap[PIDMAP_ENTRIES];
int last_pid;
/*
每个PID命名空间都具有一个进程,其发挥的作用相当于全局的init进程,init的一个目的是对孤儿调用wait4,命名空间局部的init变体也必须完成该工作。child_reaper保存了指向该进程的task_struct的指针
*/
struct task_struct *child_reaper;
struct kmem_cache *pid_cachep;
/*
2. level表示当前命名空间在命名空间层次结构中的深度。初始命名空间的level为0,下一层为1,逐层递增。level较高的命名空间中的ID,对level较低的命名空间来说是可见的(即子命名空间对父命名空间可见)。从给定的level位置,内核即可推断进程会关联到多少个ID(即子命名空间中的进程需要关联从当前命名空间一直到最顶层的所有命名空间)
*/
unsigned int level;
/*
3. parent是指向父命名空间的指针
*/
struct pid_namespace *parent;
#ifdef CONFIG_PROC_FS
struct vfsmount *proc_mnt;
#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
struct bsd_acct_struct *bacct;
#endif
};
10.2 struct pid、struct upid
- PID的管理围绕着两个数据结构展开,struct pid是内核对PID的内部表示、struct upid则表示特定的命名空间中可见的信息。
- \linux-2.6.32.63\include\linux\pid.h
/*
struct upid is used to get the id of the struct pid, as it is seen in particular namespace.
Later the struct pid is found with find_pid_ns() using the int nr and struct pid_namespace *ns.
*/
struct upid {
/* Try to keep pid_chain in the same cacheline as nr for find_vpid */
//1. 表示ID的数值
int nr;
//2. 指向该ID所属的命名空间的指针
struct pid_namespace *ns;
/*
3. 所有的upid实例都保存在一个散列表中,pid_chain用内核的标准方法实现了散列溢出链表
*/
struct hlist_node pid_chain;
};
struct pid
{
//1. 引用计数器
atomic_t count;
unsigned int level;
/*
lists of tasks that use this pid
task是一个数组,每个数组项都是一个散列表头,对应于一个ID类型,因为一个ID可能用于几个进程,所有共享同一个给定ID的task_struct实例,都通过该列表连接起来,PIDTYPE_MAX表示ID类型的数目
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX
};
*/
struct hlist_head tasks[PIDTYPE_MAX];
struct rcu_head rcu;
struct upid numbers[1];
};
- 另一幅图如下:
- 可以看到,内核用数据结构中的这种N:N的关系,实现了一个虚拟的层次命名空间结构。
10.3 struct nsproxy
- \linux-2.6.32.63\include\linux\nsproxy.h
/*
A structure to contain pointers to all per-process namespaces
1. fs (mount)
2. uts
3. network
4. sysvipc
5. etc
'count' is the number of tasks holding a reference. The count for each namespace, then, will be the number of nsproxies pointing to it, not the number of tasks.
The nsproxy is shared by tasks which share all namespaces. As soon as a single namespace is cloned or unshared, the nsproxy is copied.
*/
struct nsproxy
{
atomic_t count;
/*
1. UTS(UNIX Timesharing System)命名空间包含了运行内核的名称、版本、底层体系结构类型等信息
*/
struct uts_namespace *uts_ns;
/*
2. 保存在struct ipc_namespace中的所有与进程间通信(IPC)有关的信息
*/
struct ipc_namespace *ipc_ns;
/*
3. 已经装载的文件系统的视图,在struct mnt_namespace中给出
*/
struct mnt_namespace *mnt_ns;
/*
4. 有关进程ID的信息,由struct pid_namespace提供
*/
struct pid_namespace *pid_ns;
/*
5. struct net包含所有网络相关的命名空间参数
*/
struct net *net_ns;
};
extern struct nsproxy init_nsproxy;
10.4 struct mnt_namespace
- \linux-2.6.32.63\include\linux\mnt_namespace.h
struct mnt_namespace
{
//使用计数器,指定了使用该命名空间的进程数目
atomic_t count;
//指向根目录的vfsmount实例
struct vfsmount * root;
//双链表表头,保存了VFS命名空间中所有文件系统的vfsmount实例,链表元素是vfsmount的成员mnt_list
struct list_head list;
wait_queue_head_t poll;
int event;
};