LinuxPid Namespace简介及其引发的问题
Posted 林多
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LinuxPid Namespace简介及其引发的问题相关的知识,希望对你有一定的参考价值。
Pid Namespace简介及其引发的问题
Linux有六种命名空间:
- Mount namespace
- UTS namespace
- IPC namespace
- PID namespace
- Network namespace
- User namespace
这六种命名空间,用来隔离某种资源。通过命名空间,linux可以支持容器的实现。这里只关注PID Namspace。
PID namespace
- 引入PID Namespace之前,Linux系统上进程的PID是全局唯一的。同一个系统上,不会存在两个相同PID的正在运行的进程。
引入PID Namespace后:
- 在不同PID namespace下,可能存在多个拥有相同PID的进程。
- 同一个进程,在不同的PID Namespace下拥有不同的PID。
下面是linux论坛,对于PID namespace的概述。和上面说的意思大致相同。
The PID namespace allows for creating sets of tasks, with each such set looking like a standalone machine with respect to process IDs. In other words, tasks in different namespaces can have the same IDs.
- 那么对于用户空间来讲,能够感知到PID namespace么?
一般来讲,在实际的应用场景面,一个容器内有且只有一个PID命名空间,对于容器内的程序来讲,它自身的PID即是Global PID。通过getpid的方法,就是当前PID namespace下的命名空间。所以,PID命名空间对 User-space是透明的
- PID在Linux kernel中的定义(v5.0.21)
/*
* struct upid is used to get the id of the struct pid, as it is
* seen in particular namespace. Later the struct pid is found with
* find_pid_ns() using the int nr and struct pid_namespace *ns.
*/
struct upid
int nr; // 当前PID命名空间下的PID
struct pid_namespace *ns; // PID命名空间
;
struct pid
atomic_t count; // 引用技术
unsigned int level; // pid所在的命名空间层级
/* lists of tasks that use this pid */
struct hlist_head tasks[PIDTYPE_MAX];
struct rcu_head rcu; // rcu helper
struct upid numbers[1]; // 存储pid namespace的数组
;
PID Namespace引起的问题
- PID namspace在 2.6.24 kenerl中引入,在非容器的Linux系统中基本不会启动多个PID Namespace(即只使用一个命名空间)。
思考下面的场景,会带来的问题:
linux kernel提供了一种驱动,这种驱动用来记录用户层发生的动作。用户层通过打开驱动节点,写入动作信息。用户层的进程PID,由kenerl层通过 current(current task)来识别并记录。
// kernel层记录用户层进程的PID
// tgid,thread group id在linux kernel中表示进程的PID
pid_t pid = current->tgid;
-
传统的linux系统(非容器环境),因为pid是全局的,上面的场景不会出现 pid记录错误的问题。
-
容器环境下 ,上面的场景会导致PID记录错误的问题。原因是current->tgid保持的是第0层(即第一个命名空间)PID Namespace下的进程PID,而非当前命名空间。
-
如何解决?获取进程所在命名空间下的PID即可。
// pid_t pid = current->tgid; // 不适用于容器环境
pid_t pid = task_tgid_vnr(current);
- task_tgid_vnr,该函数也是getpid的实现。它用于获得进程所在命名空间下的tgpid(即pid),关于其实现
SYSCALL_DEFINE0(getpid)
return task_tgid_vnr(current);
//task_tgid_vnr是getpid的实现
static inline pid_t task_tgid_vnr(struct task_struct *tsk)
return __task_pid_nr_ns(tsk, __PIDTYPE_TGID, NULL);
pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
struct pid_namespace *ns)
pid_t nr = 0;
rcu_read_lock();
if (!ns)
ns = task_active_pid_ns(current);
if (likely(pid_alive(task)))
if (type != PIDTYPE_PID)
if (type == __PIDTYPE_TGID)
type = PIDTYPE_PID;
task = task->group_leader;
nr = pid_nr_ns(rcu_dereference(task->pids[type].pid), ns);
rcu_read_unlock();
return nr;
pid_t pid_nr_ns(struct pid *pid, struct pid_namespace *ns)
struct upid *upid;
pid_t nr = 0;
if (pid && ns->level <= pid->level)
// 获取当前命名空间的描述符
upid = &pid->numbers[ns->level];
if (upid->ns == ns)
// 返回PID Namespace对应的pid
nr = upid->nr;
return nr;
以上是关于LinuxPid Namespace简介及其引发的问题的主要内容,如果未能解决你的问题,请参考以下文章