User namespace
当需要一个新用户命名空间时,当前用户命名空间就会被复制,并与当前任务的nsproxy相关联,声明如下:
- struct user_namespace {
-
struct kref kref;
-
struct hlist_head uidhash_table[UIDHASH_SZ];
-
struct user_struct *root_user;
-
}
其中root_user用于记录单个用户资源情况,而uidhash_table将所有user_struct连接起来.
- struct user_struct {
-
atomic_t __count;//该结构体引用计数
-
atomic_t processes;//该用户拥有的进程数量
-
atomic_t sigpending;//该用户拥有的悬而未决的信号量数目
-
….
-
unsigned long locked_shm;//锁住的共享页个数
-
//hash表所维护的信息
-
struct hlist_node uidhash_node;
-
uid_t uid;
-
…...
-
}
当设置新的用户时,用户命名空间创建如下:
- static struct user_namespace *clone_user_ns(struct user_namespace *old_ns)
-
{
-
struct user_namespace *ns;
-
struct user_struct *new_user;
-
int n;
-
-
ns = kmalloc(sizeof(struct user_namespace),GFP_KERNEL);
-
if(!ns)
-
return ERR_PTR(-ENOMEM);
-
-
kref_init(&ns->kref);
-
-
for(n=0; n< UIDHASH_SZ;++n)
-
INIT_HLIST_HEAD(ns->uidhash_table+n);
-
-
/*insert into new root user*/
-
ns->root_user = alloc_uid(ns,0);
-
if(!ns->root_user) {
-
kfree(ns);
-
return ERR_PTR(-ENOMEM);
-
}
-
-
new_user = alloc_uid(ns,current->uid);
-
if(!new_user) {
-
free_uid(ns->root_user);
-
kfree(ns);
-
return ERR_PTR(-ENOMEM);
-
}
-
switch(new_user);
-
return ns;
-
}
命名空间增加了pid管理的复杂性,pid命名空间按照树型层次化管理。当一个新的命名空间创建,该命名空间内所有的pid都对其父命名空间可见,但是子命名空间却不能看到父命名空间中pid,这样就意味着有些任务包含有多个pid:父命名空间,子命名空间。这样就产生了两种类型的pid:
Global
pid:内核本身包含有效的pid,init任务中可见的全局唯一的pid,也就是系统唯一的pid。
Local
pid:在该命名空间内部的pid,全局不是唯一的,在不同的命名空间中,可能存在相同的pid。
task的pid_namespace描述
task_struct
中结构体部分结构如下:
- struct task_struct {
-
…..
-
pid_t pid;
-
pid_t tgid;
-
struct task_struct *group_leader;
-
struct list_head thread_group;
-
…..
-
}
而session
id和进程组id没有直接包含在task_struct内部,而是存放在signal结构体中
- struct signal_struct {
-
….....
-
struct task_struct *curr_target;
-
union {
-
pid_t pgrp __deprecated;
-
pid_t __pgrp;
-
};
-
union {
-
pid_t session __deprecated;
-
pid_t __session;
-
};
-
….....
-
}
对应的修改函数有:set_task_session,set_task_pgrp。
pid的管理
对应的pid_namespace定义如下:
- [include/linux/pid_namespace.h]
-
//每个命名空间都包含一个所有pid的分配图,系统中各个命名空间互不交叉
-
struct pid_namespace {
-
struct kref kref;//进程的引用计数
-
struct pidmap pidmap[PID_ENTRIES];//pid分配图
-
int last_pid;
-
struct task_struct *child_reaper;//当前命名空间中waitpid角色的task
-
struct kmem_cache *pid_cachep;
-
int level;//当前pid_namespace的深度
-
struct pid_namespace *parent;//父pid_namespace
-
}
init_ns定义如下:
- [kernel/pid.c]
-
struct pid_namespace init_pid_ns = {
-
.kref = {
-
.refcount = ATOMIC_INIT(2),
-
},
-
//可用pid:4KB 4*1024*8=32768
-
.pidmap = {
-
.pidmap = {
-
[ 0 … PIDMAP_ENTRIES – 1] = {
-
ATOMIC_INIT(BITS_PER_PAGE),NULL}
-
},
-
.last_pid = 0,
-
.level = 0,
-
.child_reaper = &init_task,//init_task监听该命名空间的所有task
-
}
pid的管理主要集中在两个数据结构:struct
pid为内核形式的pid,struct
upid代表在特定命名空间中可见的信息,定义如下:
- [include/linux/pid.h]
-
struct upid
-
{
-
int nr;//真正的pid值
-
struct pid_namespace *ns;//该nr属于哪个pid_namespace
-
struct hlist_node pid_chain;//所有upid的hash链表 find_pid
-
}
-
//一个pid可以属于不同的级别,每一级别又包含一个upid
-
struct pid
-
{
-
atomic_t count;//引用计数
-
struct hlist_head tasks[PIDTYPE_MAX];//该pid被使用的task链表
-
struct rcu_head rcu;//互斥访问
-
int level;//该pid所能到达的最大深度
-
struct upid numbers[1];//每一层次(level)的upid
-
}
一个进程可以在多个命名空间中,但是每个命名空间的local
id却不相同,numbers则表示每一层level的upid实例,这里的数组只包含一个元素,如果系统只有一个进程,这是可行的,但如果包含多个进程的话,就需要进行分配更多的空间,这个放在结构体中最后一个元素就是方便扩容。
其中PIDTYPE_MAX定义如下:
- enum pid_type
-
{
-
PIDTYPE_PID,
-
PIDTYPE_PGID,
-
PIDTYPE_SID,
-
PIDTYPE_MAX
-
}
线程组id没有包含在内,因为它与thread
group leader的pid一样,没有必要放在里面。
一个任务可以包含多个命名空间,task_struct的结构体中显示如下:
- struct task_struct {
-
…...
-
struct pid_link pids[PIDTYPE_MAX];
-
…...
-
}
pid_link就是链接所有的pid:
- struct pid_link {
-
struct hlist_node node;//由于每个task包含多个pid(多个命名空间可见),指向的是自己
-
struct pid *pid;
-
}
而实现upid中的数值nr到pid对象的hash映射如下:
- static struct hlist_head *pid_hash;//双向hash链表
-
pid_hash是一个hlist_head数组,大小根据机器的内存配置,从16到4096,初始化代码如下:
-
[kernel/pid.c]
-
void __init pidhash_init(void)
-
{
-
int i, pidhash_size;
-
//#define PAGE_SHIFT 12
-
unsigned long megabytes = nr_kernel_pages >> (20-PAGE_SHIFT);
-
-
pidhash_shift = max(4,fls(megabytes * 4));
-
pidhash_shift = min(12,pidhash_shift);
-
//16项到4096项
-
pidhash_size = 1 << pidhash_shift;
-
-
….....
-
pid_hash = alloc_bootmem(pidhash_size*sizeof(*(pid_hash)));
-
if(!pid_hash)
-
panic(“Could not alloc pidhash”);
-
for(i=0;i< pidhash_size;++i)
-
INIT_HLIST_HEAD(&pid_hash[i]);
-
}
阅读(4845) | 评论(0) | 转发(1) |