【问题标题】:What is the relation between `task_struct` and `pid_namespace`?`task_struct` 和 `pid_namespace` 之间有什么关系?
【发布时间】:2015-01-02 22:57:09
【问题描述】:

我正在研究一些内核代码并试图了解数据结构是如何链接在一起的。我知道调度程序如何工作的基本概念,以及 PID 是什么。但是我不知道在这种情况下命名空间是什么,也无法弄清楚所有这些是如何协同工作的。

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

所以:

  1. 在这种情况下,什么是命名空间?
  2. task_structpid_namespace 之间是什么关系? (我已经猜到它与pid_t 有关,但不知道如何)

一些参考资料:

【问题讨论】:

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


    【解决方案1】:

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

    1. PID namespaces in operation
    2. A brief introduction to PID namespaces (this one comes from a sysadmin)

    通过第二个链接后,很明显命名空间是隔离资源的好方法。在包括 Linux 在内的任何操作系统中,进程都是最重要的资源之一。用他自己的话说

    是的,就是这样,使用这个命名空间可以重新启动 PID 编号并获得您自己的“1”流程。这可以看作是一个 进程标识符树中的“chroot”。当您使用时,它非常方便 需要在日常工作中处理 pids 并且卡在 4 位数字上 数字……

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

    就内核而言,我可以为您提供一些指导以帮助您入门。我不是这里的专家,但我希望这对您有所帮助。

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

    一个任务可能拥有的所有 PID 都在struct pid 中描述。此结构包含 ID 值,具有此的任务列表 ID、引用计数器和哈希列表节点存储在 哈希表用于更快的搜索。关于列表的更多信息 任务。基本上,一个任务具有三个 PID:进程 ID (PID)、 进程组 ID (PGID) 和会话 ID (SID)。 PGID 和 SID 可以在任务之间共享,例如,当两个或更多 任务属于同一个组,所以每个组ID地址多于 一项任务。使用 PID 命名空间,这个结构变得有弹性。现在, 每个 PID 可能有多个值,每个值在一个中有效 命名空间。也就是说,一个任务在一个命名空间中的 PID 可能为 1024,并且 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 名称空间的指针,该进程的子进程将使用该名称空间。当前进程的 PID 命名空间使用 task_active_pid_ns 找到。

    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 也就是启动时产生的第一个进程),使用 alloc_pid 分配一个 PID 结构,它实际上为新的forked 进程。此函数的简短 sn-p:

      nr = alloc_pidmap(tmp);
      if(nr<0)
         goto out_free;
      pid->numbers[i].nr = nr;
      pid->numbers[i].ns = tmp;
      

    这通过给它一个新的 PID 以及它当前所属的命名空间来填充 upid 结构。

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

    copy_process 的最后阶段,通过task_struct 中的pid_link 字段通过函数attach_pid,在task_struct 和这个新的pid 结构之间建立链接。

    还有很多其他内容,但我希望这至少可以给你一些先机。

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

    【讨论】:

      猜你喜欢
      • 2016-01-31
      • 2013-10-30
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 2017-09-15
      • 2016-11-05
      相关资源
      最近更新 更多