In college days, I heard about the init process. It is the root of process tree and first process started by the kernel and so on. I wanted to see what happens if we kill it. So I tried a lot but no success. Let’s kill the init process and observe what is happening

Note: Fedora switched systemd a while back, if you running Fedora you will find process “systemd” is with pid 1 instead of init.

Image

Now, Years later I was reading Linux Kernel Development I was going through kernel sources mentioned in the book and all of a sudden I found the answer.

Note: File Paths mentioned here are relative to kernel source tree. I am using linux kernel 2.6.34.

In kernel/fork.c, in function copy_process(…) I found this

/*
* Siblings of global init remain as zombies on exit since they are
* not reaped by their parent (swapper). To solve this and to avoid
* multi-rooted process trees, prevent global and container-inits
* from creating siblings.
*/
if ((clone_flags & CLONE_PARENT) && current->signal->flags & SIGNAL_UNKILLABLE)
return ERR_PTR(-EINVAL);

Mark the above code as Listing 1. We know that operating system keep information about a process in process control block (PCB). In Linux, PCB is implemented as structure called task_struct declared in include/linux/sched.h. task_struct store all the important information about a process like it’s pid, memory mapping, signal mask etc…

Now, task_struct contains pointer to structure signal_struct named signal. Inside signal_struct field flags (unsigned int) refers to signal flags for the given process

struct task_struct {
…................
signal_struct *signal
…................
}

Form include/linux/sched.h
struct signal_struct {
…....................
unsigned int flags;      /* see SIGNAL_* flags below*/
…....................
}</pre>
#define SIGNAL_UNKILLABLE       0x00000040 /* for init: ignore fatal signals */

The above code (marked listing 1) prevents init from creating any siblings. In the above code current refers to the pointer  to task_struct for current process. init is the only process for which current->signal->flags is set to SIGNAL_UNKILLABLE. So, if init tries to create siblings using clone syscall, -EINVAL is returned.

Let’s see what happens when we remove the SIGNAL_UNKILLABLE flag from init. To do this I wrote a loadable kernel module. To learn how to write a loadable kernel module refer to

1. The Linux Kernel Module Programming Guide by Peter Salzman.

He also has a great tutorial on gdb you can find it on this link.

2. Book: Linux Device Drivers from O’REILLY.

Excerpt from the code.

/* Description: Remove UNKILLABLE flag from init */
…............
…............
static int hello_init(void)
{
struct task_struct *task;
    struct task_struct *init_ptr = NULL;
for (task = current; task != &init_task; task = task->parent) {
       init_ptr = task;
}
/* init_ptr now points to init, and task now points to init's parent i.e. swapper with pid 0 */
printk(KERN_ALERT "I am %s with pid %i\n", task->comm, task->pid);
printk(KERN_ALERT "I am %s with pid %i\n", init_ptr->comm, init_ptr->pid);
printk(KERN_ALERT "0x%x\n", init_ptr->signal->flags);

/* remove the UNKILLALBE flag */
init_ptr->signal->flags = init_ptr->signal->flags & ~SIGNAL_UNKILLABLE;
printk(KERN_ALERT "0x%x\n", init_ptr->signal->flags);
return 0;
}
…................
…................

When the above code is compiled, module hello.ko is generated. When we issue

“insmod hello.ko” command module initialization function hell_init is called.

When we insert the module using insmod command, current points to task_struct of insmod process, so in the for loop we keep on we keep on moving up the process tree until we encounter init_task as show in the picture below.

for (task = current; task != &init_task; task = task->parent) {
init_ptr = task;
}

Screen Shot 2013-08-16 at 8.20.04 PM

So, with first iteration we move from insmod to bash and so on until we reach init_task. Despite it’s name init_task is parent of init process and has pid of 0. It’s statically allocated and is not visible in user space.

Screen Shot 2013-08-16 at 9.14.20 PM

After that I am able to kill init using command “kill -9 1”. The moment I do kernel panics. I get core dump using kdump. Below is the dmesg output.

DMESG Output

NOTE: Since wordpress.com doesn’t allow you to create iFrames and manual code, We faced lot of issues while formatting this post. There are still issues with the alignment. We regret the inconvenience caused to you. Soon we will be moving to a personal domain and work for the same has already been started.

References

1. Manpages: man 7 signal, man ps

2. Linux Kernel Development By Robert Love

3. Linux Kernel Sources 2.6.34

4. Tools you may find useful: ctags, cscope, kdump.

Pages : Follow flossstuff on Twitter      

About these ads