【Linux】Pid Namespace简介及其引发的问题

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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
// tgidthread 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;
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux