Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1378587
  • 博文数量: 370
  • 博客积分: 10654
  • 博客等级: 中将
  • 技术积分: 4396
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-07 15:44
文章分类

全部博文(370)

文章存档

2012年(36)

2011年(195)

2010年(139)

分类:

2010-10-26 09:32:49

                            命名空间(2)

   子系统此前的全局属性现在封装到命名空间中,每个进程关联到一个选定的命名空间。每个可以感知命名空间的内核子系统都必须提供一个数据结构,将所有通过命名空间形式提供的对象集中起来。struct nsproxy用于汇集指向特定于子系统的命名空间包装器的指针:

  1. <nsproxy.h> 
  2. struct nsproxy {  
  3.         atomic_t count;  
  4.         struct uts_namespace *uts_ns;  
  5.         struct ipc_namespace *ipc_ns;  
  6.         struct mnt_namespace *mnt_ns;  
  7.         struct pid_namespace *pid_ns;  
  8.         struct user_namespace *user_ns;  
  9.         struct net *net_ns;  
  10. }; 

当前内核的以下范围可以感知到命名空间。

UTS命名空间包含了运行内核的名称、版本、底层体系结构类型等信息。UTS是UNIX Timesharing System的简称。

保存在struct ipc_namespace中的所有与进程间通信(IPC)有关的信息。

已经装载的文件系统的视图,在struct mnt_namespace中给出。

有关进程ID的信息,由struct pid_namespace提供。

struct user_namespace保存的用于限制每个用户资源使用的信息。

struct net_ns包含所有网络相关的命名空间参数。读者在第12章中会看到,为使网络相关的内核代码能够完全感知命名空间,还有许多工作需要完成。

当我讨论相应的子系统时,会介绍各个命名空间容器的内容。在本章中,我们主要讲解UTS和用户命名空间。由于在创建新进程时可使用fork建立一个新的命名空间,因此必须提供控制该行为的适当的标志。每个命名空间都有一个对应的标志:

  1. <sched.h> 
  2. #define CLONE_NEWUTS    0x04000000      /* 创建新的utsname组 */  
  3. #define CLONE_NEWIPC    0x08000000      /* 创建新的IPC命名空间  */  
  4. #define CLONE_NEWUSER   0x10000000      /* 创建新的用户命名空间   */  
  5. #define CLONE_NEWPID    0x20000000      /* 创建新的PID命名空间  */  
  6. #define CLONE_NEWNET    0x40000000      /* 创建新的网络命名空间   */ 

每个进程都关联到自身的命名空间视图:

  1. <sched.h> 
  2. struct task_struct {  
  3. ...  
  4. /* 命名空间 */  
  5.  
  6.         struct nsproxy *nsproxy;  
  7. ...  

因为使用了指针,多个进程可以共享一组子命名空间。这样,修改给定的命名空间,对所有属于该命名空间的进程都是可见的。

请注意,对命名空间的支持必须在编译时启用,而且必须逐一指定需要支持的命名空间。但对命名空间的一般性支持总是会编译到内核中。 这使得内核不管有无命名空间,都不必使用不同的代码。除非指定不同的选项,否则每个进程都会关联到一个默认命名空间,这样可感知命名空间的代码总是可以使 用。但如果内核编译时没有指定对具体命名空间的支持,默认命名空间的作用则类似于不启用命名空间,所有的属性都相当于全局的。

init_nsproxy定义了初始的全局命名空间,其中维护了指向各子系统初始的命名空间对象的指针:

  1. <kernel/nsproxy.c> 
  2. struct nsproxy init_nsproxy = INIT_NSPROXY(init_nsproxy);  
  3.  
  4. <init_task.h> 
  5. #define INIT_NSPROXY(nsproxy) { \  
  6.         .pid_ns = &init_pid_ns, \  
  7.         .count = ATOMIC_INIT(1), \  
  8.         .uts_ns = &init_uts_ns, \  
  9.         .mnt_ns = NULL, \  
  10.         INIT_NET_NS(net_ns) \  
  11.         INIT_IPC_NS(ipc_ns) \  
  12.         .user_ns = &init_user_ns, \  

UTS命名空间

UTS命名空间几乎不需要特别的处理,因为它只需要简单量,没有层次组织。所有相关信息都汇集到下列结构的一个实例中:

  1. <utsname.h> 
  2. struct uts_namespace {  
  3.         struct kref kref;  
  4.         struct new_utsname name;  
  5.  
  6. }; 

kref是一个嵌入的引用计数器,可用于跟踪内核中有多少地方使用了struct uts_namespace的实例(回想第1章,其中讲述了更多有关处理引用计数的一般框架信息)。uts_namespace所提供的属性信息本身包含 在struct new_utsname中:

  1. <utsname.h> 
  2. struct new_utsname {  
  3.         char sysname[65];  
  4.         char nodename[65];  
  5.         char release[65];  
  6.         char version[65];  
  7.         char machine[65];  
  8.         char domainname[65];  
  9. }; 

各个字符串分别存储了系统的名称(Linux...)、内核发布版本、机器名,等等。使用uname工具可以取得这些属性的当前值,也可以在/proc/sys/kernel/中看到:

  1. wolfgang@meitner> cat /proc/sys/kernel/ostype  
  2. Linux  
  3. wolfgang@meitner> cat /proc/sys/kernel/osrelease  
  4. 2.6.24 

初始设置保存在init_uts_ns中:

  1. init/version.c  
  2. struct uts_namespace init_uts_ns = {  
  3. ...  
  4.         .name = {  
  5.                 .sysname = UTS_SYSNAME,  
  6.                 .nodename = UTS_NODENAME,  
  7.                 .release = UTS_RELEASE,  
  8.                 .version = UTS_VERSION,  
  9.                 .machine = UTS_MACHINE,  
  10.                 .domainname = UTS_DOMAINNAME,  
  11.         },  
  12. }; 

相关的预处理器常数在内核中各处定义。例如,UTS_RELEASE在中定义,该文件是连编时通过顶层Makefile动态生成的。

请注意,UTS结构的某些部分不能修改。例如,把sysname换成Linux以外的其他值是没有意义的,但改变机器名是可以的。

内核如何创建一个新的UTS命名空间呢?这属于copy_utsname函数的职责。在某个进程调用fork并通过CLONE_NEWUTS标志指 定创建新的UTS命名空间时,则调用该函数。在这种情况下,会生成先前的uts_namespace实例的一份副本,当前进程的nsproxy实例内部的 指针会指向新的副本。如此而已!由于在读取或设置UTS属性值时,内核会保证总是操作特定于当前进程的uts_namespace实例,在当前进程修改 UTS属性不会反映到父进程,而父进程的修改也不会传播到子进程。

用户命名空间

用户命名空间在数据结构管理方面类似于UTS:在要求创建新的用户命名空间时,则生成当前用户命名空间的一份副本,并关联到当前进程的nsproxy实例。但用户命名空间自身的表示要稍微复杂一些:

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

如前所述,kref是一个引用计数器,用于跟踪多少地方需要使用user_namespace实例。对命名空间中的每个用户,都有一个struct user_struct的实例负责记录其资源消耗,各个实例可通过散列表uidhash_table访问。

对我们来说user_struct的精确定义是无关紧要的。只要知道该结构维护了一些统计数据(如进程和打开文件的数目)就足够了。我们更感兴趣的 问题是:每个用户命名空间对其用户资源使用的统计,与其他命名空间完全无关,对root用户的统计也是如此。这是因为在克隆一个用户命名空间时,为当前用 户和root都创建了新的user_struct实例:

  1. kernel/user_namespace.c  
  2. static struct user_namespace *clone_user_ns(struct 
    user_namespace *old_ns)  
  3. {  
  4.         struct user_namespace *ns;  
  5.         struct user_struct *new_user;  
  6. ...  
  7.         ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);  
  8. ...  
  9.         ns->root_user = alloc_uid(ns, 0);  
  10.  
  11.         /* 将current->user替换为新的 */  
  12.         new_user = alloc_uid(ns, current->uid);  
  13.  
  14.         switch_uid(new_user);  
  15.         return ns;  

alloc_uid是一个辅助函数,对当前命名空间中给定UID的一个用户,如果该用户没有对应的user_struct实例,则分配一个新的实 例。在为root和当前用户分别设置了user_struct实例后,switch_uid确保从现在开始将新的user_struct实例用于资源统 计。实质上就是将struct task_struct的user成员指向新的user_struct实例。

请注意,如果内核编译时未指定支持用户命名空间,那么复制用户命名空间实际上是空操作,即总是会使用默认的命名空间。
阅读(1120) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-10-26 18:24:40

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com