5

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

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

所以:

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

一些参考资料:

4

1 回答 1

12

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

  1. 运行中的 PID 命名空间
  2. PID命名空间的简单介绍(这个来自一个系统管理员)

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

是的,就是这样,使用这个命名空间可以重新启动 PID 编号并获得您自己的“1”进程。这可以被视为进程标识符树中的“chroot”。当您需要在日常工作中处理 pid 并且被 4 位数字卡住时,它非常方便......</p>

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

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

这篇 LWN文章描述了查看 PID 的旧方法和新方法。用它自己的话来说:

一个任务可能具有的所有 PID 都在struct 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 now 表示 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_struct关系。struct nsproxystruct pid

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

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

  1. task_struct首先使用dup_task_struct.
  2. 父进程的命名空间也使用copy_namespaces. 这也为孩子创建了一个新nsproxy结构,它的 nsproxy 指针指向这个新创建的结构
  3. 对于非 INIT 进程(原始全局 PID也就是启动时产生的第一个进程),PID分配一个结构,使用该结构实际​​上为新ed 进程alloc_pid分配一个新的 PID 结构。fork此函数的简短片段:

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

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

此外,作为copy process函数的一部分,这个新分配的 PID 然后链接到相应的task_structvia 函数pid_nr,即它的全局 ID(从 INIT 命名空间看起来是原始 PID nr)存储pidtask_struct.

在最后阶段,通过领域内的功能与这个新结构copy_process之间建立了联系。task_structpidpid_linktask_structattach_pid

它还有很多,但我希望这至少能给你一些先机。

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

于 2014-11-10T06:02:17.510 回答