“task_struct”和“pid_namespace”之间有什么关系



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

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

所以:

  1. 在此上下文中,命名空间是什么?
  2. task_structpid_namespace之间有什么关系?(我已经认为这与pid_t有关,但不知道如何(

一些参考资料:

  • pid_namespace的定义
  • task_struct的定义
  • upid的定义(另见其下方pid(

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

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

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

是的,就是这样,使用此命名空间可以重新启动PID 编号并获取自己的"1"过程。这可以被视为 进程标识符树中的"chroot"。当您 需要在日常工作中处理PID,并且被困在4位数字上 数字。。。

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

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

这篇 LWN 文章介绍了查看 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. 首先使用 dup_task_struct 复制task_struct
  2. 父进程的命名空间也使用 copy_namespaces 进行复制。这也为子结构创建了一个新的nsproxy结构,它的 nsproxy 指针指向这个新创建的结构
  3. 对于非 INIT 进程(原始全局 PID,即启动时生成的第一个进程(,使用 alloc_pid 分配PID结构,该结构实际上为新fork的进程分配了新的 PID 结构。此函数的简短片段:

    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(存储在字段中pid task_struct

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

还有很多,但我希望这至少应该给你一些领先优势。

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

相关内容

  • 没有找到相关文章

最新更新