Objective
The last few nights in the process related to the kernel principle, just see the PID this block, it does not seem very complicated, but the introduction of the PID namespace added some data structure, it does not seem so clear, referring to the Linux kernel architecture this book, after reading the feeling has not understood. So on the Internet to find some articles for reference, which I found a good quality of the article, why say that the quality is good mainly because the author in the blog post and did not paste code a bucket, and did not follow the regular code analysis, but to a tracing method to restore the entire PID framework, After reading this article feel very good, so with this article, this article is not original, just on this basis will own understanding re-comb, the related chart was redrawn, added some of the meaning of the data structure expression. For a link to this article, refer to Appendix A
The design of PID frame
The design of a framework takes many factors into account, and it is believed that readers who have analyzed the Linux kernel will find that a large number of data structures of the kernel are connected by hash tables and chain tables, and the most important purpose is to find them. Imagine a good framework, you should take into account the speed of retrieval, but also consider the division of functions. There are several factors that need to be considered in the PID framework.
- How to quickly find the corresponding PID via task_struct
- How to find the corresponding task_struct quickly by PID
- How to quickly assign a unique PID
These are some of the basic factors that need to be considered when designing a PID framework. It is these factors that will complicate the design of the PID framework.
The original PID frame
Consider the simple point first, a process corresponding to a PID
struct task_struct{ ..... pid_t pid; .....}
is not very easy, back to the above, to see if it conforms to the design principles of the PID framework, through the task_struct to find the PID, very convenient, but through the PID find task_struct How to do? Well, based on this structure is certainly not enough to meet the demand, then continue to improve it.
Note: The above design comes from the design of the Linux 2.4 kernel
Introducing Hlist and PID bitmaps
struct task_struct *pidhash[PIDHASH_SZ];struct pidmap { atomic_t nr_free; //表示当前可用的pid个数 void *page; //用来存放位图};
This is very convenient, and then look at the PID framework design some of the factors are satisfied, how to assign a unique PID, continuous increment? , then the assigned PID will need to be reclaimed until the maximum value of the PID is assigned, and then continue from the beginning if the previously assigned process is finished. Well, this may be a good idea, but is it necessary to mark those PID to be available? This seems like a solution, but given that the solution is to be put into the kernel, the guys who develop Linux will certainly want to optimize it in nearly any way, and, indeed, they use a PID bitmap, But the basic idea has not changed, also need to mark the PID is usable, but uses the PID bitmap the way to save the memory more. Imagine that by setting each bit to 0 or 1, it can be used to indicate whether it is available, and the 1th bit of 0 and 1 is used to indicate whether the PID is 1 usable. And so on. To this end a seemingly good PID framework design is completed, is the overall effect of the whole framework at present.
PID framework after introduction of PID type
Readers who are familiar with Linux should know that a process is not just a process PID, but also a process group ID, and a session ID, (for process groups and sessions refer to the relationship between processes) so when the PID type is introduced, the framework becomes the following.
struct task_struct{ .... pid_t pid; pid_t session; struct task_struct *group_leader; ....}structsignal{ .... pid_t __pgrp; 2.6.24
For the process group ID, the signal needs to know this ID, which can be used to control a set of processes, so this ID appears in the signal structure. So until now, the framework is not so complicated, but there is a need to be clear whether the session The ID or group ID does not actually occupy the PID resource, because the session ID is the same as the group ID of the lead process group, and the group ID is the same as the PID of the lead process in the process group.
PID framework after introduction of process PID namespace
As the kernel continues to add new kernel features, especially the introduction of the PID namespace mechanism, which leads to the concept of a namespace in the PID, and the concept of a hierarchy of namespaces exists, the high level can be seen by the lower level, which leads to high-level processes with multiple PID, For example, under the default namespace, a new namespace is created, which is called Level1, and the default namespace is called Level0, which runs a process in level1 with a PID of 1 in Level1 because the high-level PID Namespace need to be seen by the low-level PID namespace, so this process in level0 will have another PID, for XXX. Use the concept of the PID bitmap mentioned above, imagine, for each PID namespace should have a pidmap, the Level1 process mentioned above has two PID one is 1, the other is xxx, where PID is 1 is in the Level1 pidmap to allocate, PID is assigned in Level0 Pidmap. The picture below is a frame of the whole pidnamespace.
When a PID namespace is introduced, a PID is not just a numeric value, but also includes the namespace where the PID resides, the parent namespace, the namespace of the Pidmap, the PID of the namespace, and so on. Therefore, the kernel has a package of PID, which is encapsulated into a struct PID, A struct named PID, which is defined as follows:
enumpid_type{pidtype_pid, Pidtype_pgid, Pidtype_sid, Pidtype_max};structpid{unsigned intLevel//The level at which the PID resides /* Lists of the tasks that use this pid * / structHlist_head Tasks[pidtype_max];//a hash table, and three table headers, respectively, PID header, Process Group ID table header, Session ID table header, followed by specific introduction structUpid numbers[1];//This PID corresponds to the namespace, a PID not only to include the current PID, but also contains the parent namespace, the default size is 1, so it is in the root namespace};structUpid {//wrapper a struct that is abstracted from a namespace intNr//pid PID values in this namespace structPid_namespace *ns;//The corresponding namespace structHlist_node Pid_chain;//Connect all namespaces corresponding to a PID via Pidhash.};structPid_namespace {structKref Kref;structPidmap Pidmap[pidmap_entries];//As mentioned above, a PID namespace should have its own pidmap intLast_pid;//Last assigned PID unsigned intnr_hashed;structTask_struct *child_reaper;//This PID namespace corresponds to the init process, because if the parent process hangs up need to find the adoptive father Ah, here to find out who to structKmem_cache *pid_cachep;unsigned intLevel//namespace hierarchy where structPid_namespace *parent;//Parent namespace, building a hierarchical relationship of namespaces structUser_namespace *user_ns;structWork_struct proc_work; kgid_t Pid_gid;intHide_pid;intReboot/ * Group exit code if this pidns is rebooted * / unsigned intProc_inum;};//There are also some complex members, and the discussion here is not
After the introduction of PID namespace, it really becomes very complicated, and a lot of data structures can not understand. How the process is associated with a struct PID, the kernel in order to unified management PID, process group ID, session ID, the three types of IDs, which is now task_ The struct is associated with three struct-PID, and the type of the struct PID is distinguished. So the kernel introduces an intermediate structure to associate the task_struct with the PID 1:3. Its structure is as follows:
structpid_link{structHlist_node node;structPID *pid;};structtask_struct{, ... pid_t pid; "."structPid_link Pids[pidtype_max]; .............}structpid{unsigned intLevel//The level at which the PID resides /* Lists of the tasks that use this pid * / structHlist_head Tasks[pidtype_max];//a hash table, and three table headers, respectively, PID header, process group ID header, session ID header, for associating with Task_struct structUpid numbers[1];//This PID corresponds to the namespace, a PID not only to include the current PID, but also contains the parent namespace, the default size is 1, so it is in the root namespace}; The Tasks hash table using the PID is associated with the Hlist_node in the PIDs structure in the task_struct.
So far, a well-established PID framework has been built, and the effect of the framework is as follows:
Where the process a,b,c is a process group, A is the leader process, so B, and C of the task_struct struct Pid_link members of the node field is adjacent to process a corresponding to the struct PID tasks[1]. struct Upid is correlated by Pid_hash and PID values so that the UPID structure of all namespaces can be found quickly by PID values, and numbers is the last member of a struct PID. Use a mutable array to indicate how many namespaces this PID structure currently has.
Note: A bug fixed in 2016/08/28, Pid_hash key is not NR, should be the pid_hashfn(upid->nr, upid->ns)]
only one to determine a UPID structure (specifically refer to the implementation of PID_HASHFN), so that the corresponding UPID structure can be found through the NR and NS.
To validate our framework, here are some PID-related functions that validate the framework with the implementation of the function.
Process PID-related API analysis gets three types of PID structure
Static inline structPID *task_pid (structTask_struct *task) {returnTask->pids[pidtype_pid].pid;} /* * without tasklistorRCU Lock it is not safe toDereference * The result ofTask_pgrp/task_session evenifTask = = Current, * We can race withAnother thread doing sys_setsid/sys_setpgid. */Static inline structPID *task_pgrp (structTask_struct *task) {returnTask->group_leader->pids[pidtype_pgid].pid;}Static inline structPID *task_session (structTask_struct *task) {returnTask->group_leader->pids[pidtype_sid].pid;}
Get PID values for a namespace in a PID structure
pid_t pid_nr_ns(structstruct pid_namespace *ns){ struct upid *upid; 0; //判断传入的pid namespace层级是否符合要求 if (pid && ns->level <= pid->level) { //去到对应pid namespace的strut upid结构 if//判断命名空间是否一致 //获取pid数值 } return nr;}
Let's see how to assign a PID.
structPID *alloc_pid (struct pid_namespace *ns)//pid allocation to rely on and PID namespace, that is, which PID is the PID namespace{struct PID *pid; Enum Pid_type type;int I, nr; struct Pid_namespace *tmp; struct Upid *upid; Assign a PID structure PID = Kmem_cache_alloc (Ns->pid_cachep,Gfp_kernel);if(!pid) goto out; TMP = NS; Pid->level = ns->level; Initialize level//recursion to upper levels for PID assignment and initialization for (i = ns->level; i >=0; I--) {NR = Alloc_pidmap (TMP); From the current PID namespace to know the global PID namespace, each level is assigned a PIDif(Nr <0) goto Out_free; Pid->numbers[i].nr = nr; Initialize the UPID structure PID->NUMBERS[I].NS = tmp; TMP = tmp->parent; Recursive to Father PID namespace}if(Unlikely (Is_child_reaper (PID))) {//If the INIT process requires some setup, prepare it forprocDirectoryif(Pid_ns_prepare_proc (NS)) goto Out_free; } Get_pid_ns (NS); Atomic_set (&pid->count,1); for ( Type = 0; type < Pidtype_max; + +type)//Initialize the hlist structure in the PID Init_hlist_head(&pid->tasks[ type]);Upid = pid->numbers + ns->level; Locate the UPID structure SPIN_LOCK_IRQ (&pidmap_lock) of the current namespace;if(! (Ns->nr_hashed &pidns_hash_adding)) goto Out_unlock; for (; Upid >= pid->numbers;--upid) {Hlist_add_head_rcu (&upid->pid_chain, &pid_hash[pid_hashfn (UPID->NR, Upid->ns)]); Set up the Pid_hash to correlate PID and PID namespace upid->ns->nr_hashed++; } SPIN_UNLOCK_IRQ (&pidmap_lock); out: Return PID;Out_unlock: Spin_unlock_irq (&pidmap_lock); Put_pid_ns (NS);Out_free: while (++i <= ns->level) free_pidmap (pid->numbers + i); Kmem_cache_free (Ns->pid_cachep, PID); PID =NULL; Goto out;}
Appendix A reference Links
Process ID of the Linux kernel process management
Linux Kernel principle-pid namespace