Kernel task_struct looks like below.I am more interested in two members namely children and sibling , so i have removed other elements from this kernel structure .
struct task_struct{
// some data elements .
struct list_head children;
/* list of my children */
struct list_head sibling;
/* linkage in my parent's children list */
//some data members
};
"children" is a doubly circular linked list of task_struct of the children of process .If I want to access the children from current process , I have to iterate over "children" list using macro "list_for_each" as below :
struct task_struct *task;
struct list_head *list;
list_for_each(list, ¤t->children) {
task = list_entry(list, struct task_struct, sibling); /* task now points to one of current’s children */
}
list_for_each will eventually initialize "list " with next children .Now since we are iterating through children list , we should ideally subtract the offset of "children" list from "list" pointer to get the tast_struct address for the current process .What is the reason we are passing "sibling" here which eventually a different list with different offset? .
Please note : It is working code , all I want to understand is why sibling is used when children pointer should be used to calculate correct offset and hence task_struct address for the children .
Thanks in Advance .
In order to organize data as linked list using struct list_head
you have to declare list root and declare list entry for linkage. Both root and child entries are the same type (struct list_head
). children
entry of struct task_struct
entry is a root
. sibling
entry of struct task_struct
is a list entry
. To see the differences, you have to read code, where children
and sibling
are used. Usage of list_for_each
for children
means what children
is a root
. Usage of list_entry
for sibling
means what sibling
is a list entry
.
You can read more about linux kernel lists here.
Question: What is the reason we are passing "sibling" here which eventually a different list with different offset?
Answer:
If the list was created this way:
list_add(&subtask->sibling, ¤t->children);
Then
list_for_each(list, ¤t->children)
Will initialize list pointers to sibling
, so you have to use sibling
as parameter to list_entry. That's how linux kernel lists API designed.
But, If the list was created in another (wrong) way:
list_add(&subtask->children, ¤t->sibling);
Then you have to iterate the list this (wrong) way:
list_for_each(list, ¤t->sibling)
And now you have to use children
as parameter for list_entry
.
Hope, this helps.
Following is the pictorial representation that might help someone in future. The top box represents a parent, and the bottom two boxes are it's children
Here is a picture in addition to the previous answers. The same process can be both a parent and a child (as Parent1 on the picture), and we need to distinguish between these two roles.
Intuitively, if children
of Parent0 would point to children
of Parent1, then Parent0.children.next->next
(green circle on the picture), which is the same as Parent1.children.next
, would point to a child of Parent1 instead of a next child of Parent0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With