pid of Linux process management


foreword

一、struct task_struct

List the members related to pid in the struct task_struct structure:

struct task_struct {
    
    
	......
	pid_t pid;
	pid_t tgid;
	
	/* PID/PID hash table linkage. */
	struct pid_link pids[PIDTYPE_MAX];
	struct list_head thread_group;

	/* namespaces */
	struct nsproxy *nsproxy;
}
/*
 * A structure to contain pointers to all per-process
 * namespaces - fs (mount), uts, network, sysvipc, etc.
 *
 * The pid namespace is an exception -- it's accessed using
 * task_active_pid_ns.  The pid namespace here is the
 * namespace that children will use.
 *
 * 'count' is the number of tasks holding a reference.
 * The count for each namespace, then, will be the number
 * of nsproxies pointing to it, not the number of tasks.
 *
 * The nsproxy is shared by tasks which share all namespaces.
 * As soon as a single namespace is cloned or unshared, the
 * nsproxy is copied.
 */
struct nsproxy {
    
    
	atomic_t count;
	struct uts_namespace *uts_ns;
	struct ipc_namespace *ipc_ns;
	struct mnt_namespace *mnt_ns;
	struct pid_namespace *pid_ns_for_children;
	struct net 	     *net_ns;
	struct cgroup_namespace *cgroup_ns;
};

From the above we can see that the naming of struct pid_namespace is a bit different from the others. Note: The pid namespace is an exception - use task_active_pid_ns to access the pid namespace. The pid namespace here is the namespace that children will use.

task_active_pid_ns obtains struct pid_namespace through struct task_struct:

struct pid_namespace *task_active_pid_ns(struct task_struct *tsk)
{
    
    
	return ns_of_pid(task_pid(tsk));
}
EXPORT_SYMBOL_GPL(task_active_pid_ns);

Get struct pid through struct task_struct:

static inline struct pid *task_pid(struct task_struct *task)
{
    
    
	return task->pids[PIDTYPE_PID].pid;
}
struct pid_link
{
    
    
	struct hlist_node node;
	struct pid *pid;
};
struct task_struct {
    
    
	......
	/* PID/PID hash table linkage. */
	struct pid_link pids[PIDTYPE_MAX];
	......
}

Obtain struct pid_namespace through struct pid, and ns_of_pid() returns the pid namespace assigned the specified pid.

/*
 * ns_of_pid() returns the pid namespace in which the specified pid was
 * allocated.
 *
 * NOTE:
 * 	ns_of_pid() is expected to be called for a process (task) that has
 * 	an attached 'struct pid' (see attach_pid(), detach_pid()) i.e @pid
 * 	is expected to be non-NULL. If @pid is NULL, caller should handle
 * 	the resulting NULL pid-ns.
 */
static inline struct pid_namespace *ns_of_pid(struct pid *pid)
{
    
    
	struct pid_namespace *ns = NULL;
	if (pid)
		ns = pid->numbers[pid->level].ns;
	return ns;
}
struct pid_namespace {
    
    
	struct kref kref;
	struct pidmap pidmap[PIDMAP_ENTRIES];
	struct rcu_head rcu;
	int last_pid;
	unsigned int nr_hashed;
	struct task_struct *child_reaper;
	struct kmem_cache *pid_cachep;
	unsigned int level;
	struct pid_namespace *parent;
#ifdef CONFIG_PROC_FS
	struct vfsmount *proc_mnt;
	struct dentry *proc_self;
	struct dentry *proc_thread_self;
#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
	struct fs_pin *bacct;
#endif
	struct user_namespace *user_ns;
	struct ucounts *ucounts;
	struct work_struct proc_work;
	kgid_t pid_gid;
	int hide_pid;
	int reboot;	/* group exit code if this pidns was rebooted */
	struct ns_common ns;
};

kref: is a reference counter that represents how many processes this namespace is used in.
The structure has only one member, why should only one member be encapsulated into a structure?
This is to prevent the atomic variable from being accidentally assigned directly, so that the value cannot be directly manipulated after encapsulation, and the developer can operate the atomic variable through the corresponding function interface.

struct kref {
    
    
	atomic_t refcount;
};

For example, the atomic variable must be initialized by calling the kref_init function:

/**
 * kref_init - initialize object.
 * @kref: object in question.
 */
static inline void kref_init(struct kref *kref)
{
    
    
	atomic_set(&kref->refcount, 1);
}

pidmap[]: Record the PID usage of the current system.
last_pid: Record the PID value last assigned to the process.
child_reaper: Each PID namespace has an init process similar to the global, and its role is the same as the init process, a local init process. For example: the init process calls wait on the orphan process, and the local init process of the namespace also completes the work. child_reaper holds a pointer to the task_struct of the local init process.
pid_cachep: pointer to struct pid cache, sla(u)b allocator pointer, slab object is struct pid.

struct pid_namespace *ns;
struct pid *pid = kmem_cache_alloc(ns->pid_cachep, GFP_KERNEL);

parent: is a pointer to the parent namespace.
level: Indicates the depth of the current namespace in the namespace hierarchy. The level of the initial namespace is 0, the subspace level of the namespace is 1, the subspace level of the next layer is 2, and so on. The calculation of the level is more important, because the IDs in the higher-level namespaces are visible to the lower-level namespaces. With a given level setting, the kernel can infer how many IDs the process is associated with. level = 0, representing the global namespace.

2. Introduction to struct pid

2.1 struct pid

// linux-4.10.1/include/linux\pid.h

struct pid
{
    
    
	atomic_t count;
	unsigned int level;
	/* lists of tasks that use this pid */
	struct hlist_head tasks[PIDTYPE_MAX];
	struct rcu_head rcu;
	struct upid numbers[1];
};

struct pid is the kernel's internal representation of a PID:

count: Represents the number of tasks currently using this struct pid.
level: Indicates the number of namespaces that can see the process, that is, the level of the process ID namespace to which the process belongs. level = 0, representing the global namespace.
tasks: is the list of tasks currently using this struct pid. tasks is an array where each array item is a hash table header corresponding to a type of process ID. Because each process ID may belong to several processes, all task_struct instances that share the same given process ID are linked through this hash table.
numbers is an array of struct upid instances, each array item corresponds to a namespace. There is only one array item here, and when there is only one, it means that the process only belongs to the global namespace. But this array is located at the end of the structure, so that more memory space can be allocated, that is, more members can be added to the numbers array, so that the process can belong to multiple local namespaces.

Where PIDTYPE_MAX represents the PID type:

enum pid_type
{
    
    
	PIDTYPE_PID,	//进程的进程号
	PIDTYPE_PGID,	//进程组领头进程的进程号
	PIDTYPE_SID,	//会话领头进程的进程号
	PIDTYPE_MAX		//表示进程号类型的数目 == 3
};

A process may be visible in multiple namespaces. And the local ID in each namespace is also different

Note: struct pid can be found by find_pid_ns() using int nr (number of local process PID) and struct pid_namespace *ns.

2.2 struct upid

struct upid {
    
    
	/* Try to keep pid_chain in the same cacheline as nr for find_vpid */
	int nr;
	struct pid_namespace *ns;
	struct hlist_node pid_chain;
};

struct upid represents the visible information of a specific namespace, which is used to obtain the numerical id of struct pid, which is seen in a specific namespace.
nr: The value of the process ID, which saves the local PID value of the process.
ns: Pointer to the namespace to which this process ID belongs.
pid_chain: hash table node, hash table overflow linked list.

All struct upid instances are kept in a hash table

Note: The tasks of struct pid are that struct hlist_head is the head of the hash table, and the struct hlist_node in struct upid is the node of the hash table.

2.3 pid_t pid/tgid

struct pid is the kernel representation of the process ID, and pid_t pid is a numerical PID visible to user space. The kernel provides auxiliary functions for converting between the two.

(1)
pid_t pid is the global process ID. The global ID is the unique ID number in the kernel itself and the initialization namespace. The init process started during system startup belongs to the initial namespace. For each ID type, there is a given global ID, guaranteed to be unique across the system.

struct task_struct {
    
    
	......
	pid_t pid;
	pid_t tgid;
	......
}

Both pid and tgid in the task_struct structure are global IDs.

(2)
The application layer calls the getpid function (returns the PID of the current process), and actually gets the pid_t tgid of the struct task_struct structure.
If a process is not using threads (that is, the process has only one main thread), the pid of the process is the same as the tgid. If a process has multiple threads, the pids of these threads are different, but the tgid is the same.
The pid_t pid of each task in the kernel is different.

/**
 * sys_getpid - return the thread group id of the current process
 *
 * Note, despite the name, this returns the tgid not the pid.  The tgid and
 * the pid are identical unless CLONE_THREAD was specified on clone() in
 * which case the tgid is the same in all threads of the same group.
 *
 * This is SMP safe as current->tgid does not change.
 */
SYSCALL_DEFINE0(getpid)
{
    
    
	return task_tgid_vnr(current);
}
static inline pid_t task_tgid_vnr(struct task_struct *tsk)
{
    
    
	return pid_vnr(task_tgid(tsk));
}
static inline struct pid *task_tgid(struct task_struct *task)
{
    
    
	return task->group_leader->pids[PIDTYPE_PID].pid;
}

As you can see from the source code, the pid of the thread group leader, that is, tgid, is obtained.

(3)
The application layer calls the gettid function to obtain the thread ID, that is, to obtain the thread ID (TID) of the thread, which is actually to obtain the pid_t pid of the struct task_struct structure.

/* Thread ID - the internal kernel "pid" */
SYSCALL_DEFINE0(gettid)
{
    
    
	return task_pid_vnr(current);
}
static inline pid_t task_pid_vnr(struct task_struct *tsk)
{
    
    
	return __task_pid_nr_ns(tsk, PIDTYPE_PID, 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)
			task = task->group_leader;
		nr = pid_nr_ns(rcu_dereference(task->pids[type].pid), ns);
	}
	rcu_read_unlock();

	return nr;
}
EXPORT_SYMBOL(__task_pid_nr_ns);
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)
			nr = upid->nr;
	}
	return nr;
}
EXPORT_SYMBOL_GPL(pid_nr_ns);

(4)

For the application layer, in a multithreaded process, all threads have the same PID, but each thread has a unique TID. This is because the application layer calls the getpid function to return pid_t tgid, and the application layer calls the gettid function to return pid_t pid. In the kernel, for multithreading of the same process, the pid_t tgid is the same, but the pid_t pid is different.
insert image description here

(5)
int nr is the process ID number of the local space. Local IDs belong to a specific namespace and are not global. For each local ID type, it is valid within the namespace to which it belongs, but IDs with the same type and the same value may appear in different namespaces.

If the member level in struct pid = 0, it means that there is no local namespace, and the struct upid numbers[1] of struct pid has only one structure, then the local int nr in struct upid is equal to the pid_t of the task (struct task_struct). pid .

Note: In the kernel, the global process ID is generally described by the pid_t type. The local process ID is described by int type.
The pid_t type is actually an int type.

typedef int					__kernel_pid_t;
typedef __kernel_pid_t		pid_t;

Third, the generation of PID

3.1 _do_fork

(1) fork(): This function is a system call that can copy an existing process to create a brand new process and generate a task_struct.
(2) pthread_create(): This function is a function in Glibc, and then calls the clone() system call to create a thread (also called a lightweight process) and generate a task_struct.
(3) kthread_create(): Create a new kernel thread and generate a task_struct.

In fact, these three APIs will eventually call _do_fork(), the difference is that the parameters passed to _do_fork() are different (clone_flags).

In _do_fork(), alloc_pid() will be called to generate struct pid, and a numerical pid (pid_t pid) will also be generated as the return value of _do_fork() function.

_do_fork()
	-->copy_process()
		-->alloc_pid()  

	-->get_task_pid()
	-->pid_vnr()
	-->put_pid()

Generate struct pid:

struct task_struct *p;
struct pid *pid = alloc_pid(p->nsproxy->pid_ns_for_children)

Generate pid_t pid:

struct task_struct *p = copy_process();
struct pid * pid = get_task_pid(p, PIDTYPE_PID);
long nr = pid_vnr(pid);
put_pid(pid);
return nr;
pid_t pid_vnr(struct pid *pid)
{
    
    
	return pid_nr_ns(pid, task_active_pid_ns(current));
}
EXPORT_SYMBOL_GPL(pid_vnr);
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)
			nr = upid->nr;
	}
	return nr;
}
EXPORT_SYMBOL_GPL(pid_nr_ns);

3.2 alloc_pid

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;

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

	(2)
	tmp = ns;
	pid->level = ns->level;
	for (i = ns->level; i >= 0; i--) {
    
    
		nr = alloc_pidmap(tmp);
		if (nr < 0) {
    
    
			retval = nr;
			goto out_free;
		}

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

	......

	(3)
	upid = pid->numbers + ns->level;
	......
	for ( ; upid >= pid->numbers; --upid) {
    
    
		hlist_add_head_rcu(&upid->pid_chain,
				&pid_hash[pid_hashfn(upid->nr, upid->ns)]);
		upid->ns->nr_hashed++;
	}

	return pid;
	......
}

(1)
Use the sla(u)b cache allocator to allocate a struct pid structure instance.

(2)
When a new process is created, the process may be visible in multiple namespaces. For each local namespace, a local PID must be generated, namely struct upid, pid->numbers[i] represents a local PID (struct upid), where pid->numbers[i].nr = nr, nr is Local numeric PID, allocated using alloc_pidmap. The kernel uses a bitmap to allocate local numerical PIDs, where each numerical PID has a bit identification, allocates an idle numerical PID, sets a bit in the bitmap from 0 to 1, releases a numerical PID, and sets the bitmap to 1. The corresponding bit is set to 0 from 1.

(3)
Add each struct upid (pid_chain) in struct pid to its hash table. All struct upids have their data updated in step (2).

3.2 init_task_pid

static inline void
init_task_pid(struct task_struct *task, enum pid_type type, struct pid *pid)
{
    
    
	 task->pids[type].pid = pid;
}

4. PID related API

4.1 find_pid_ns

The struct pid instance is obtained through the process's local numerical PID and the pid_namespace associated with the local numerical PID.
Mainly using the characteristics of the container_of macro, struct upid is a structure member of struct pid.

struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
{
    
    
	struct upid *pnr;

	hlist_for_each_entry_rcu(pnr,
			&pid_hash[pid_hashfn(nr, ns)], pid_chain)
		if (pnr->nr == nr && pnr->ns == ns)
			return container_of(pnr, struct pid,
					numbers[ns->level]);

	return NULL;
}
EXPORT_SYMBOL_GPL(find_pid_ns);

4.2 pid_task

Find struct task_struct through struct pid, and get the first struct task_struct instance in the pid->tasks[type] hash table. This struct task_struct instance is in the usage linked list of struct pid, and the index of the starting element of the searched linked list is the value of the parameter type.

struct task_struct *pid_task(struct pid *pid, enum pid_type type)
{
    
    
	struct task_struct *result = NULL;
	if (pid) {
    
    
		struct hlist_node *first;
		first = rcu_dereference_check(hlist_first_rcu(&pid->tasks[type]),
					      lockdep_tasklist_lock_is_held());
		if (first)
			result = hlist_entry(first, struct task_struct, pids[(type)].node);
	}
	return result;
}
EXPORT_SYMBOL(pid_task);
struct pid_link
{
    
    
	struct hlist_node node;
	struct pid *pid;
}

struct task_struct {
    
    
	......
	/* PID/PID hash table linkage. */
	struct pid_link pids[PIDTYPE_MAX];
	......
}

It also uses the characteristics of the container_of macro.

#define hlist_entry(ptr, type, member) container_of(ptr,type,member)

container_of: Obtain the first address of the entire structure variable through the first address of a member in the structure variable

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({
      
      			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})

Summarize

There is only one struct pid for each process, but there can be multiple struct upids, because a process can be in multiple local namespaces.

References

Linux kernel 4.10.0
goes deep into the Linux kernel architecture

おすすめ

転載: blog.csdn.net/weixin_45030965/article/details/126394485