LinuxPid Namespace简介及其引发的问题

Posted 林多

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LinuxPid Namespace简介及其引发的问题相关的知识,希望对你有一定的参考价值。

Pid Namespace简介及其引发的问题

Linux有六种命名空间:

  1. Mount namespace
  2. UTS namespace
  3. IPC namespace
  4. PID namespace
  5. Network namespace
  6. User namespace

这六种命名空间,用来隔离某种资源。通过命名空间,linux可以支持容器的实现。这里只关注PID Namspace。

PID namespace

  • 引入PID Namespace之前,Linux系统上进程的PID是全局唯一的。同一个系统上,不会存在两个相同PID的正在运行的进程。

引入PID Namespace后:

  1. 在不同PID namespace下,可能存在多个拥有相同PID的进程。
  2. 同一个进程,在不同的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简介及其引发的问题的主要内容,如果未能解决你的问题,请参考以下文章

LinuxPid Namespace简介及其引发的问题

LLVM是什么以及其编程规范中关于using namespace std的思考

Linux Namespace : 简介

容器Cgroup和Namespace特性简介

namespace基础介绍

产品经理打架引发的问题:如何识别需求及其价值