Kernel - 1
Kernel land is full of wonders and treasure and is the land of freedom in its truest form. As I left my home town - peaceful user land to embark on this adventure, I thought keeping a journal to record the various extraodinary phenomenons of the mystical land is a good way to keep my sanity from drifting away…
The Elders’ Scrolls
Many legendary adventurers have explored this sacred land before and provided useful scrolls (maps/directions) for new commers. I found these scrolls to be useful:
- Linux Kernel Development
- Linux Kernel Workbook
- The Guild’s Map - I follow v4.4
It’s been around two weeks since I ventured into the unknown. Here’s my first journal entry…
Journal Entry #1 - Around Two Weeks
Tapping Into The Kernel
To get started with inner workings of the kernel, writing kernel modules is a good idea.
Here’s a template of a kernel module, closely resembling one provided by the Linux Kernel Workbook.
mymodule.c
#include <linux/init.h>
#include <linux/module.h>
static int __init my_init(void) {
printk(KERN_INFO "Loading mymodule\n"); //Remember to add \n to make `dmesg` prints messages as expected
return 0;
}
static void __exit my_exit(void) {
printk(KERN_INFO "Cleaning up mymodule\n")
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("Sample Hello World Module");
MODULE_AUTHOR("Rishi Agrawal <rishi.b.agrawal@gmail.com>");
MODULE_LICENSE("GPL");
Makefile
MODULE_FILENAME=mymodule
obj-m += $(MODULE_FILENAME).o
KO_FILE=$(MODULE_FILENAME).ko
export KROOT=/lib/modules/$(shell uname -r)/build
modules:
@$(MAKE) -C $(KROOT) M=$(PWD) modules
clean:
@$(MAKE) -C $(KROOT) M=$(PWD) clean
rm -rf Module.symvers modules.order
insert: modules
sudo insmod $(KO_FILE)
sudo dmesg -c
remove:
sudo rmmod $(MODULE_FILENAME)
sudo dmesg -c
What Kernel Functions/Variables are Available in Kernel Module?
I’ve found these are generally available to be used in a kernel module code:
- Marked as ‘T’ or ‘D’ in
/proc/kallsyms - Defined in kernel code (provided you
includeappropriate header files)
For example: task_struct is defined in include/linux/sched.h. Therefore, by #include <linux/sched.h>, I can now use it in my kernel module.
Useful Snippets
- Iterating over all
task_struct:#include <linux/sched.h> task_struct *ptr; for_each_process(ptr){ printk(KERN_INFO "This process' PID is %d", ptr->pid); }Question:
- How do you know about
for_each_process? - the Elders’ scrolls. - I want to see its definition in the kernel code, how? - The Guild’s Map
Notes:
- How do you know about
- Readily available
task_structpointers:current: pointer to the task being executedinit_task: the first task (not a pointer); note that this is not thetask_structof the init process - which is actually thelist_entry(init_task.next, struct task_struct, tasks)- explained below. Another adventurer described his experience with this phenomenon in the kernel land in the Adventurer Camp’s Tavern.
Here’s my useless version of
for_each_process(struct task_struct *ptr)#include <linux/sched.h> #include <linux/list.h> void my_for_each_process(void) { struct task_struct *ptr = &init_task; struct list_head *tasks = &(ptr->tasks); do { //Do something stupid to the task_struct being iterated through ptr ptr = list_entry(tasks->next, struct task_struct, tasks); tasks = &(ptr->tasks); } while ((tasks->next) && ptr != &init_task); // the second check is because task_struct is a circular list }Note: While the kernel’s version starts from the actual init process, this version goes from the
init_task. Therefore, this version has one iteration more than the kernel’s, which is for theinit_task’stask_struct.Above convoluted code simply loops through all the
task_structs in the kernel. It certainly looks more complicated than what a user land inhabitant would expect. Here is how to interpret this phenomenon:task_structis a circular doubly-linked list.- doubly-linked list’s implementation in the kernel is creatively interesting:
- a
task_structcontains a structlist_headnamedtasks. list_headis defined in/include/linux/types.has follows:struct list_head { struct list_head *next, *prev; };list_entry(ptr, type, member)as documented here is a macro that will return the container of alist_headpointer. That’s whylist_entry(init_task.next, struct task_struct, tasks)returns thetask_struct(not pointer) of the init process. I bet most of us user land people have never seen such a thing.
- a
I have also made a few more interesting encounters but decided to end this entry here because it’s late and I need to rest tomorrow’s journey starts early…