Chinaunix首页 | 论坛 | 博客
  • 博客访问: 141497
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 233
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-17 15:31
个人简介

学习中。。

文章分类

全部博文(21)

文章存档

2015年(1)

2014年(17)

2013年(3)

我的朋友

分类: LINUX

2014-03-31 09:18:06

原文地址:Linux 命名空间(二) 作者:datao0907

User namespace

当需要一个新用户命名空间时,当前用户命名空间就会被复制,并与当前任务的nsproxy相关联,声明如下:

  1. struct user_namespace {
  2.     struct kref         kref;
  3.     struct hlist_head uidhash_table[UIDHASH_SZ];
  4.     struct user_struct *root_user;
  5. }

其中root_user用于记录单个用户资源情况,而uidhash_table将所有user_struct连接起来.

  1. struct user_struct {
  2.     atomic_t __count;//该结构体引用计数
  3.     atomic_t processes;//该用户拥有的进程数量
  4.     atomic_t sigpending;//该用户拥有的悬而未决的信号量数目
  5.     ….
  6.     unsigned long locked_shm;//锁住的共享页个数
  7.     //hash表所维护的信息
  8.     struct hlist_node uidhash_node;
  9.     uid_t uid;
  10.     …...
  11. }

当设置新的用户时,用户命名空间创建如下:

  1. static struct user_namespace *clone_user_ns(struct user_namespace *old_ns)
  2. {
  3.     struct user_namespace *ns;
  4.     struct user_struct *new_user;
  5.     int n;
  6.     
  7.     ns = kmalloc(sizeof(struct user_namespace),GFP_KERNEL);
  8.     if(!ns)
  9.         return ERR_PTR(-ENOMEM);
  10.     
  11.     kref_init(&ns->kref);
  12.     
  13.     for(n=0; n< UIDHASH_SZ;++n)
  14.      INIT_HLIST_HEAD(ns->uidhash_table+n);
  15.     
  16.     /*insert into new root user*/
  17.     ns->root_user = alloc_uid(ns,0);
  18.     if(!ns->root_user) {
  19.         kfree(ns);
  20.         return ERR_PTR(-ENOMEM);
  21.     }

  22.     new_user = alloc_uid(ns,current->uid);
  23.     if(!new_user) {
  24.         free_uid(ns->root_user);
  25.         kfree(ns);
  26.         return ERR_PTR(-ENOMEM);        
  27.     }
  28.         switch(new_user);
  29.         return ns;
  30. }

命名空间增加了pid管理的复杂性,pid命名空间按照树型层次化管理。当一个新的命名空间创建,该命名空间内所有的pid都对其父命名空间可见,但是子命名空间却不能看到父命名空间中pid,这样就意味着有些任务包含有多个pid:父命名空间,子命名空间。这样就产生了两种类型的pid:

Global pid:内核本身包含有效的pid,init任务中可见的全局唯一的pid,也就是系统唯一的pid

Local pid:在该命名空间内部的pid,全局不是唯一的,在不同的命名空间中,可能存在相同的pid

taskpid_namespace描述

task_struct 中结构体部分结构如下:

  1. struct task_struct {
  2.     …..
  3.     pid_t pid;
  4.     pid_t tgid;
  5.     struct task_struct *group_leader;
  6.     struct list_head thread_group;
  7.     …..
  8. }

session id和进程组id没有直接包含在task_struct内部,而是存放在signal结构体中

  1. struct signal_struct {
  2.     ….....
  3.     struct task_struct *curr_target;
  4.     union {
  5.         pid_t pgrp __deprecated;
  6.         pid_t __pgrp;
  7. };
  8.     union {
  9.         pid_t session __deprecated;
  10.         pid_t __session;
  11. };
  12.     ….....
  13. }

对应的修改函数有:set_task_session,set_task_pgrp

pid的管理

对应的pid_namespace定义如下:

  1. [include/linux/pid_namespace.h]
  2. //每个命名空间都包含一个所有pid的分配图,系统中各个命名空间互不交叉
  3. struct pid_namespace {
  4.     struct kref kref;//进程的引用计数
  5.     struct pidmap pidmap[PID_ENTRIES];//pid分配图
  6.     int last_pid;
  7.     struct task_struct *child_reaper;//当前命名空间中waitpid角色的task
  8.     struct kmem_cache *pid_cachep;
  9.     int level;//当前pid_namespace的深度
  10.     struct pid_namespace *parent;//父pid_namespace
  11. }

init_ns定义如下:

  1. [kernel/pid.c]
  2. struct pid_namespace init_pid_ns = {
  3. .kref = {
  4. .refcount = ATOMIC_INIT(2),
  5. },
  6. //可用pid:4KB 4*1024*8=32768
  7. .pidmap = {
  8. .pidmap = {
  9. [ 0 … PIDMAP_ENTRIES – 1] = {
  10. ATOMIC_INIT(BITS_PER_PAGE),NULL}
  11. },
  12. .last_pid = 0,
  13. .level = 0,
  14. .child_reaper = &init_task,//init_task监听该命名空间的所有task
  15. }

pid的管理主要集中在两个数据结构:struct pid为内核形式的pid,struct upid代表在特定命名空间中可见的信息,定义如下:

  1. [include/linux/pid.h]
  2. struct upid
  3. {
  4.     int nr;//真正的pid值
  5.     struct pid_namespace *ns;//该nr属于哪个pid_namespace
  6.     struct hlist_node pid_chain;//所有upid的hash链表 find_pid
  7. }
  8. //一个pid可以属于不同的级别,每一级别又包含一个upid
  9. struct pid
  10. {
  11.     atomic_t count;//引用计数
  12.     struct hlist_head tasks[PIDTYPE_MAX];//该pid被使用的task链表
  13.     struct rcu_head rcu;//互斥访问
  14.     int level;//该pid所能到达的最大深度
  15.     struct upid numbers[1];//每一层次(level)的upid
  16. }

一个进程可以在多个命名空间中,但是每个命名空间的local id却不相同,numbers则表示每一层levelupid实例,这里的数组只包含一个元素,如果系统只有一个进程,这是可行的,但如果包含多个进程的话,就需要进行分配更多的空间,这个放在结构体中最后一个元素就是方便扩容。

其中PIDTYPE_MAX定义如下:

  1. enum pid_type
  2. {
  3.     PIDTYPE_PID,
  4.     PIDTYPE_PGID,
  5.     PIDTYPE_SID,
  6.     PIDTYPE_MAX
  7. }

线程组id没有包含在内,因为它与thread group leaderpid一样,没有必要放在里面。

一个任务可以包含多个命名空间,task_struct的结构体中显示如下:

  1. struct task_struct {
  2.     …...
  3.     struct pid_link pids[PIDTYPE_MAX];
  4.     …...
  5. }

pid_link就是链接所有的pid:

  1. struct pid_link {
  2. struct hlist_node node;//由于每个task包含多个pid(多个命名空间可见),指向的是自己
  3. struct pid    *pid;
  4. }

而实现upid中的数值nrpid对象的hash映射如下:

  1. static struct hlist_head *pid_hash;//双向hash链表
  2. pid_hash是一个hlist_head数组,大小根据机器的内存配置,从16到4096,初始化代码如下:
  3. [kernel/pid.c]
  4. void __init pidhash_init(void)
  5. {
  6.     int i, pidhash_size;
  7.     //#define PAGE_SHIFT 12
  8.     unsigned long megabytes = nr_kernel_pages >> (20-PAGE_SHIFT);
  9.     
  10.     pidhash_shift = max(4,fls(megabytes * 4));
  11.     pidhash_shift = min(12,pidhash_shift);
  12.     //16项到4096项
  13.     pidhash_size = 1 << pidhash_shift;
  14.     
  15.     ….....
  16.     pid_hash = alloc_bootmem(pidhash_size*sizeof(*(pid_hash)));
  17.     if(!pid_hash)
  18.         panic(“Could not alloc pidhash”);
  19.     for(i=0;i< pidhash_size;++i)
  20.      INIT_HLIST_HEAD(&pid_hash[i]);
  21. }


阅读(1700) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~