zoukankan      html  css  js  c++  java
  • Linux Namespaces机制——实现

    由于Linux内核提供了PIDIPCNS等多个Namespace,一个进程可能属于多个Namespace。为了task_struct的精简,内核引入了struct nsproxy来统一管理进程所属的Namespace,在task_struct中只需存一个指向struct nsproxy的指针就行了。struct nsproxy定义如下:

    struct nsproxy {

    atomic_t count;

    struct uts_namespace *uts_ns;

    struct ipc_namespace *ipc_ns;

    struct mnt_namespace *mnt_ns;

    struct pid_namespace *pid_ns;

    struct net       *net_ns;

    };

    从定义可以看出,nsproxy存储了一组指向各个类型Namespace的指针,为进程访问各个Namespace起了一个代理的作用。由于可能有多个进程所在的Namespace完全一样,nsproxy可以在进程间共享,count字段负责记录该结构的引用数。

    系统预定义了一个init_nsproxy,用作默认的nsproxy

    struct nsproxy init_nsproxy = {

    .count = ATOMIC_INIT(1),

    .uts_ns = &init_uts_ns,

    #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)

    .ipc_ns = &init_ipc_ns,

    #endif

    .mnt_ns = NULL,

    .pid_ns = &init_pid_ns,

    #ifdef CONFIG_NET

    .net_ns = &init_net,

    #endif

    };

    其中除了mnt_ns外均指向系统默认的Namespace

    内核定义了一组函数来管理nsproxy:

    task_nsproxy用于从task_struct指针在RCU保护下获得其中的nsproxy指针。

    put_nsproxy用于减少一个nsproxy的引用数。

    get_nsproxy用于增加一个nsproxy的引用数。

    create_nsproxy用于分配一个新的nsproxy结构。

    下面我们来看系统在clone时的处理。系统调用clone是通过sys_clone实现的,而sys_clone又是通过内核函数do_fork实现的,而do_fork大部分工作又是在copy_process中做的。在copy_process中,有这样的代码:

    if ((retval = copy_namespaces(clone_flags, p)))

    goto bad_fork_cleanup_mm;

    这里我们回过头去看copy_namespaces的代码

    int copy_namespaces(unsigned long flags, struct task_struct *tsk)

    {

    struct nsproxy *old_ns = tsk->nsproxy;

    struct nsproxy *new_ns;

    int err = 0;

    if (!old_ns)

    return 0;

    get_nsproxy(old_ns);

    if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |

    CLONE_NEWPID | CLONE_NEWNET)))

    return 0;

    if (!capable(CAP_SYS_ADMIN)) {

    err = -EPERM;

    goto out;

    }

    /*

     * CLONE_NEWIPC must detach from the undolist: after switching

     * to a new ipc namespace, the semaphore arrays from the old

     * namespace are unreachable.  In clone parlance, CLONE_SYSVSEM

     * means share undolist with parent, so we must forbid using

     * it along with CLONE_NEWIPC.

     */

    if ((flags & CLONE_NEWIPC) && (flags & CLONE_SYSVSEM)) {

    err = -EINVAL;

    goto out;

    }

    new_ns = create_new_namespaces(flags, tsk, tsk->fs);

    if (IS_ERR(new_ns)) {

    err = PTR_ERR(new_ns);

    goto out;

    }

    tsk->nsproxy = new_ns;

    out:

    put_nsproxy(old_ns);

    return err;

    }

    该函数首先检查flags,如果没有指定任何一个需要新建Namespaceflag,直接返回0。否则,做相应的权能检查,然后调用create_new_namespaces为进程创建新的Namespace

    我们再来看create_new_namespaces的代码

    static struct nsproxy *create_new_namespaces(unsigned long flags,

    struct task_struct *tsk, struct fs_struct *new_fs)

    {

    struct nsproxy *new_nsp;

    int err;

    new_nsp = create_nsproxy();

    if (!new_nsp)

    return ERR_PTR(-ENOMEM);

    new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, new_fs);

    if (IS_ERR(new_nsp->mnt_ns)) {

    err = PTR_ERR(new_nsp->mnt_ns);

    goto out_ns;

    }

    new_nsp->uts_ns = copy_utsname(flags, tsk->nsproxy->uts_ns);

    if (IS_ERR(new_nsp->uts_ns)) {

    err = PTR_ERR(new_nsp->uts_ns);

    goto out_uts;

    }

    new_nsp->ipc_ns = copy_ipcs(flags, tsk->nsproxy->ipc_ns);

    if (IS_ERR(new_nsp->ipc_ns)) {

    err = PTR_ERR(new_nsp->ipc_ns);

    goto out_ipc;

    }

    new_nsp->pid_ns = copy_pid_ns(flags, task_active_pid_ns(tsk));

    if (IS_ERR(new_nsp->pid_ns)) {

    err = PTR_ERR(new_nsp->pid_ns);

    goto out_pid;

    }

    new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);

    if (IS_ERR(new_nsp->net_ns)) {

    err = PTR_ERR(new_nsp->net_ns);

    goto out_net;

    }

    return new_nsp;

    out_net:

    if (new_nsp->pid_ns)

    put_pid_ns(new_nsp->pid_ns);

    out_pid:

    if (new_nsp->ipc_ns)

    put_ipc_ns(new_nsp->ipc_ns);

    out_ipc:

    if (new_nsp->uts_ns)

    put_uts_ns(new_nsp->uts_ns);

    out_uts:

    if (new_nsp->mnt_ns)

    put_mnt_ns(new_nsp->mnt_ns);

    out_ns:

    kmem_cache_free(nsproxy_cachep, new_nsp);

    return ERR_PTR(err);

    }
    该函数首先为进程分配一个新的nsproxy(因为有新的Namespace创建),然后调用各个Namespace相关的函数来为进程一一创建新的Namespace(如果flags指定了的话)。具体的各个Namespace相关的创建函数比较复杂,与各自实现相关,就不在这里分析了。

    我们再回到copy_process中,有以下代码:

    if (pid != &init_struct_pid) {

    retval = -ENOMEM;

    pid = alloc_pid(p->nsproxy->pid_ns);

    if (!pid)

    goto bad_fork_cleanup_io;

    if (clone_flags & CLONE_NEWPID) {

    retval = pid_ns_prepare_proc(p->nsproxy->pid_ns);

    if (retval < 0)

    goto bad_fork_free_pid;

    }

    }

    由于从do_fork中调用copy_process时,pid参数是NULL,所以这里肯定满足第一个if条件。在if内,首先为进程在其所在的Namespace分配pid,然后判断clone时是否setCLONE_NEWPID,如果设定了就做进一步的处理。这两个函数都是pid Namespace相关的代码,这里就不去分析了。

    然后有以下代码:

    if (current->nsproxy != p->nsproxy) {

    retval = ns_cgroup_clone(p, pid);

    if (retval)

    goto bad_fork_free_pid;

    }

    由于我们分析的是设定了clone相关flags的情况,那这个if条件肯定满足。在if里面,调用了ns_cgroup_clone,即为不同nsproxy新建了一个cgroup(关于cgroups的分析可以参加本博客前面的文章:http://www.cnblogs.com/lisperl/archive/2012/04/26/2471776.html)。这里就和之前关于cgroups ns子系统的分析关联起来了,内核这里实际上是利用cgroups ns子系统对进程做了一个自动分类,相同nsproxy(即所有Namespace都相同的进程)的进程在一个cgroup,一旦通过clone创建新的Namespace,就会在当前cgroup下创建一个新的cgroup。这样以来,通过cgroup文件系统,在挂载ns 子系统的目录下,我们就可以清楚地看出Namespace的层次关系。

    大家看到这里是不是会有疑问,使用clone相应flags创建新的Namespace是不是必须要cgroups ns子系统的支持?作者可以负责任地告诉你:不需要。

    在nsproxy.h中有以下代码:

    #ifdef CONFIG_CGROUP_NS
    int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid);
    #else
    static inline int ns_cgroup_clone(struct task_struct *tsk, struct pid *pid)
    {
    return 0;
    }
    #endif

    即在没有cgroups的情况下,ns_cgroup_clone实现是不同的。

     

      作者曰:这里只是对Linux Namespaces机制的实现做了一个大体的上分析,具体到各个Namespace的实现并没有去讲,因为非常复杂,尤其是Network Namespace。

    http://www.cnblogs.com/lisperl/archive/2012/05/03/2480573.html

  • 相关阅读:
    【转】VS2010中 C++创建DLL图解
    [转]error: 'retainCount' is unavailable: not available in automatic reference counting mode
    [转]关于NSAutoreleasePool' is unavailable: not available in automatic reference counting mode的解决方法
    【转】 Tomcat v7.0 Server at localhost was unable to start within 45
    【转】Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If
    【转】SVN管理多个项目版本库
    【转】eclipse安装SVN插件的两种方法
    【转】MYSQL启用日志,和查看日志
    【转】Repository has not been enabled to accept revision propchanges
    【转】SVN库的迁移
  • 原文地址:https://www.cnblogs.com/mull/p/4477854.html
Copyright © 2011-2022 走看看