struct pid在内核内部用于标识一个“进程”,表示一个独立的任务(task)、进程组、会话,为了快速索引可以用hash表来组织。

下面我们基于Linux4.20看下进程的pid是如何分配的,首先看下进程pid相关的数据结构:

进程数据结构中的namespace成员:
struct task_struct
{
	… …
	/* Namespaces:  */
	struct nsproxy        *nsproxy;
	… …
	pid_t             pid;
	… …
}

进程PID的创建流程,在copy_process函数中:

//进程pid的创建:如果不是进程0的pid就需要创建一个pid
static __latent_entropy struct task_struct *copy_process(
					unsigned long clone_flags,
					unsigned long stack_start,
					unsigned long stack_size,
					int __user *child_tidptr,
					struct pid *pid,
					int trace,
					unsigned long tls,
					int node)
{
	……
	// 如果入参clone_flag中配置CLONE_NEWNS, CLONE_NEWUTS, CLONE_NEWIPC,
	// CLONE_NEWPID, CLONE_NEWNET, CLONE_NEWCGROUP 就为当前进程创建一个
	// 新的nsproxy

	retval = copy_namespaces(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_mm;
	… …
	// 如果配置CLONE_NEWPID,会调用create_pid_namespace从pid_ns_cachep中分配
	// 一个pid_namespace, 并将调用进程的pid_namespace设置父节,ns->level设置为父
	// 节点的level+1,并初始化其它成员
	if (pid != &init_struct_pid) {
		pid = alloc_pid(p->nsproxy->pid_ns_for_children);
		if (IS_ERR(pid)) {
			retval = PTR_ERR(pid);
			goto bad_fork_cleanup_thread;
		}
	}
	……
	/* ok, now we should be set up.. */
	p->pid = pid_nr(pid);
	……
}

大体流程已经清楚,下面看下pid分配的具体流程,下面看下进程ID的具体分配函数:

struct pid *alloc_pid(struct pid_namespace *ns)
{
	struct pid *pid;
	enum pid_type type;
	int i, nr;
	struct pid_namespace *tmp;
	struct upid *upid;
	int retval = -ENOMEM;

	pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);
	if (!pid)
		return ERR_PTR(retval);

	tmp = ns;
	// 进程0的level为1,每个设置CLONE_NEWPID的进程,都会比其父进程的level增1
	pid->level = ns->level;
    
    // 从level层开始到0层,依次在每一个level申请一个nr,并记录在对应的numbers[i]中
	for (i = ns->level; i >= 0; i--) {
		int pid_min = 1;

		idr_preload(GFP_KERNEL);
		spin_lock_irq(&pidmap_lock);

		/*
		 * init really needs pid 1, but after reaching the maximum
		 * wrap back to RESERVED_PIDS
		 */
		if (idr_get_cursor(&tmp->idr) > RESERVED_PIDS)
			pid_min = RESERVED_PIDS;

		/*
		 * Store a null pointer so find_pid_ns does not find
		 * a partially initialized PID (see below).
		 */
        // 从pid_min和pid_max中分配一个未使用的id
		nr = idr_alloc_cyclic(&tmp->idr, NULL, pid_min,
				      pid_max, GFP_ATOMIC);
		spin_unlock_irq(&pidmap_lock);
		idr_preload_end();

		if (nr < 0) {
			retval = (nr == -ENOSPC) ? -EAGAIN : nr;
			goto out_free;
		}

		pid->numbers[i].nr = nr;
		pid->numbers[i].ns = tmp;
		tmp = tmp->parent;
	}

	if (unlikely(is_child_reaper(pid))) {
		if (pid_ns_prepare_proc(ns))
			goto out_free;
	}

	get_pid_ns(ns);
	atomic_set(&pid->count, 1);
	for (type = 0; type < PIDTYPE_MAX; ++type)
		INIT_HLIST_HEAD(&pid->tasks[type]);

	upid = pid->numbers + ns->level;
	spin_lock_irq(&pidmap_lock);
	if (!(ns->pid_allocated & PIDNS_ADDING))
		goto out_unlock;
	for ( ; upid >= pid->numbers; --upid) {
		/* Make the PID visible to find_pid_ns. */
		idr_replace(&upid->ns->idr, pid, upid->nr);
		upid->ns->pid_allocated++;
	}
	spin_unlock_irq(&pidmap_lock);

	return pid;

out_unlock:
	spin_unlock_irq(&pidmap_lock);
	put_pid_ns(ns);

out_free:
	spin_lock_irq(&pidmap_lock);
	while (++i <= ns->level) {
		upid = pid->numbers + i;
		idr_remove(&upid->ns->idr, upid->nr);
	}

	/* On failure to allocate the first pid, reset the state */
	if (ns->pid_allocated == PIDNS_ADDING)
		idr_set_cursor(&ns->idr, 0);

	spin_unlock_irq(&pidmap_lock);

	kmem_cache_free(ns->pid_cachep, pid);
	return ERR_PTR(retval);
}

 

 

 

Logo

更多推荐