`task_struct`和`pid_namespace`之间有什么关系?

时间:2014-11-06 12:23:51

标签: linux-kernel scheduled-tasks scheduler linux-namespaces

我正在研究一些内核代码并尝试理解数据结构是如何链接在一起的。我知道调度程序如何工作的基本思路,以及PID是什么。但是我不知道命名空间在这个上下文中是什么,并且无法弄清楚所有这些是如何一起工作的。

我已经阅读了一些解释(包括O'Reilly“理解Linux内核”的部分内容),并了解可能是同一个PID进入两个进程,因为一个已终止并且ID已重新分配。但我无法弄清楚这一切是如何完成的。

所以:

  1. 此上下文中的命名空间是什么?
  2. task_structpid_namespace之间的关系是什么? (我已经认为它与pid_t有关,但不知道如何)
  3. 一些参考文献:

1 个答案:

答案 0 :(得分:8)

也许这些链接可能有所帮助:

  1. PID namespaces in operation
  2. A brief introduction to PID namespaces (this one comes from a sysadmin)
  3. 通过第二个链接后,很明显名称空间是隔离资源的好方法。在包含Linux的任何操作系统中,进程是最重要的资源之一。用他自己的话说

      

    是的,就是这样,使用此命名空间可以重新启动PID   编号并获得自己的“1”过程。这可以看作是一个   进程标识符树中的“chroot”。这对你来说非常方便   需要在日常工作中处理pids并且卡在4位数   数...

    因此,您可以创建自己的私有流程树,然后将其分配给特定用户和/或特定任务。在这个树中,进程不必担心PID与此“容器”之外的PID冲突。因此,将这棵树完全交给另一个“root”用户就好了。这位优秀的家伙用一个很好的小例子来解释这些事情做得很棒,所以我不会在这里重复一遍。

    就内核而言,我可以给你一些指导来帮助你入门。我不是这里的专家,但我希望这在某种程度上可以帮助你。

    此LWN article描述了查看PID的旧方法和更新方法。用它自己的话说:

      

    struct pid中描述了任务可能具有的所有PID。此结构包含ID值,具有此任务的任务列表   ID,引用计数器和要存储的散列列表节点   用于更快搜索的哈希表。关于列表的更多单词   任务。基本上任务有三个PID:进程ID(PID),即   进程组ID(PGID)和会话ID(SID)。 PGID和   SID可以在任务之间共享,例如,当两个或更多时   任务属于同一个组,因此每个组ID的地址都超过   一项任务。使用PID命名空间,这种结构变得具有弹性。现在,   每个PID可能有多个值,每个值在一个值中有效   命名空间。也就是说,任务可以在一个命名空间中具有1024的PID,并且   在另一个256。所以,前struct pid会发生变化。这是怎么回事   struct pid在引入PID命名空间之前看起来像是:

    struct pid {
     atomic_t count;                          /* reference counter */
     int nr;                                  /* the pid value */
     struct hlist_node pid_chain;             /* hash chain */
     struct hlist_head tasks[PIDTYPE_MAX];    /* lists of tasks */
     struct rcu_head rcu;                     /* RCU helper */
    };
    
         

    这就是它现在的样子:

    struct upid {
       int nr;                            /* moved from struct pid */
       struct pid_namespace *ns;          /* the namespace this value
                                           * is visible in */
       struct hlist_node pid_chain;       /* moved from struct pid */
    };
    
    struct pid {
       atomic_t count;
       struct hlist_head tasks[PIDTYPE_MAX];
       struct rcu_head rcu;
       int level;                     /* the number of upids */
       struct upid numbers[0];
    };
    
         

    如您所见,struct upid 现在表示PID值 - 它存储在哈希值中并具有PID值。要将struct pid转换为PID,反之亦然,可以使用一组帮助程序   task_pid_nr()pid_nr_ns()find_task_by_vpid()

    虽然有点过时,但这些信息足以让您入门。这里需要提到一个更重要的结构。它是struct nsproxy。这个结构是所有命名空间与其关联的进程相关的焦点。它包含指向此进程的子进程将使用的 PID命名空间的指针 。使用task_active_pid_ns找到当前进程的PID名称空间。

    struct task_struct内,我们有一个名为nsproxy的命名空间代理指针,它指向此进程的struct nsproxy结构。如果您跟踪创建新流程所需的步骤,您可以找到task_structstruct nsproxystruct pid之间的关系。

    Linux中的新进程总是从现有进程中分离出来,然后使用execve(或exec系列中的类似函数)替换它的映像。因此,作为do_fork的一部分,调用copy_process

    作为复制父进程的一部分,会发生以下重要事项:

    1. task_struct首先使用dup_task_struct重复。
    2. 父进程的命名空间也使用copy_namespaces进行复制。这也为子项创建了一个新的nsproxy结构,它的nsproxy指针指向这个新创建的结构
    3. 对于非INIT进程(原始全局PID 也称为引导时生成的第一个进程),使用实际分配的PID分配alloc_pid结构新fork过程的新PID结构。此功能的简短片段:

      nr = alloc_pidmap(tmp);
      if(nr<0)
         goto out_free;
      pid->numbers[i].nr = nr;
      pid->numbers[i].ns = tmp;
      
    4. 这会为upid结构填充一个新的PID以及它当前所属的命名空间。

      此外,作为copy process函数的一部分,这个新分配的PID然后通过函数task_struct链接到相应的pid_nr,即它的全局ID(这似乎是原始的PID nr)来自INIT命名空间)存储在pid中的字段task_struct中。

      copy_process的最后阶段,通过task_struct内的pid字段,在pid_link与此新task_struct结构之间建立链接attach_pid

      还有更多内容,但我希望至少能给你一些启发。

      注意:我指的是最新的(截至目前)内核版本即。 3.17.2。