I'm trying to understand more about process 0
, such as, whether it has a memory descriptor (non-NULL
task_struct->mm
field) or not, and how is it related to the swap or idle process. It seems to me that a single 'process 0' is created on the boot cpu, and then an idle thread is created for every other cpu by idle_threads_init
, but I didn't find where the first one( I assume that is the process 0
) was created.
Update
In light of the live book that tychen referenced, here is my most up-to-date understanding regarding process 0
(for x86_64), can someone confirm/refute the items below?
init_task
typed task_struct
is statically defined, with the task's kernel stack init_task.stack = init_stack
, memory descriptor init_task.mm=NULL
and init_task.active_mm=&init_mm
, where the stack area init_stack
and mm_struct
init_mm
are both statically defined.active_mm
is non-NULL means process 0
is a kernel process. Also, init_task.flags=PF_KTHREAD
.init_stack
as kernel stack. This makes the current
macro meaningful (for the first time since machine boots up), which makes fork()
possible. After this point, the kernel literally runs in process 0
's conext.start_kernel
-> arch_call_rest_init
-> rest_init
, and inside this function, process 1&2
are forked. Within the kernel_init
function which is scheduled for process 1
, a new thread (with CLONE_VM
) is made and hooked to a CPU's run queue's rq->idle
, for every other logical CPU.tid 0
(not only tgid
). Usually threads share tgid
but have distinct tid
, which is really Linux's process id
. I guess it doesn't break anything because idle threads are locked to their own CPUs.kernel_init
loads the init
executable (typically /sbin/init
), and switches both current
->mm
and active_mm
to a non-NULL mm_struct
, and clears the PF_KTHREAD
flag, which makes process 1
a legitimate user space process. While process 2
does not tweak mm
, meaning it remains a kernel process, same as process 0
.rest_init
, do_idle
takes over, which means all CPU has an idle process.init_*
objects/labels such as init_task
/init_mm
/init_stack
are all used by process 0
, and not the init process
, which is process 1
.In-kernel process can be created using kthread_create (or kthread_run , which is similar to kthread_create , but also starts the thread). User-space program can be called using call_usermodehelper. Creating generic user-space process in the kernel is discouraged (and actually unclear).
The kernel itself is not a process but a process manager. The process/kernel model assumes that processes that require a kernel service use specific programming constructs called system calls .
Overview. The Linux® kernel is the main component of a Linux operating system (OS) and is the core interface between a computer's hardware and its processes. It communicates between the 2, managing resources as efficiently as possible.
Once the kernel has started, it starts the init process. Historically this was the "SysV init", which was just called "init".
Basically the kernel virtualizes the common hardware resources of the computer to provide each process with its own virtual resources. This makes the process seem as it is the sole process running on the machine. The kernel is also responsible for preventing and mitigating conflicts between different processes. This schematically represented below:
The basic functioning of each of the 1st three subsystems is elaborated below: The Process Scheduler: This kernel subsystem is responsible for fairly distributing the CPU time among all the processes running on the system simultaneously.
Whenever a command is issued in unix/linux, it creates/starts a new process. For example, pwd when issued which is used to list the current directory location the user is in, a process starts. Through a 5 digit ID number unix/linux keeps account of the processes, this number is call process id or pid. Each process in the system has a unique pid.
The kernel at least has one runnable process, which is known as the idle task, swapper, init_task and sched. They are different names of the same process whose pid is 0.
We really start Linux kernel from start_kernel
, and the process 0/idle starts here too.
In the begin of start_kernel
, we call set_task_stack_end_magic(&init_stack)
. This function will set the stack border of init_task
, which is the process 0/idle.
void set_task_stack_end_magic(struct task_struct *tsk)
{
unsigned long *stackend;
stackend = end_of_stack(tsk);
*stackend = STACK_END_MAGIC; /* for overflow detection */
}
It's easy to understand that this function get the limitation address and set the bottom to STACK_END_MAGIC as a stack overflow flag. Here is the structure graph.
The process 0 is statically defined . This is the only process that is not created by kernel_thread
nor fork
.
/*
* Set up the first task table, touch at your own risk!. Base=0,
* limit=0x1fffff (=2MB)
*/
struct task_struct init_task
#ifdef CONFIG_ARCH_TASK_STRUCT_ON_STACK
__init_task_data
#endif
= {
#ifdef CONFIG_THREAD_INFO_IN_TASK
.thread_info = INIT_THREAD_INFO(init_task),
.stack_refcount = REFCOUNT_INIT(1),
#endif
.state = 0,
.stack = init_stack,
.usage = REFCOUNT_INIT(2),
.flags = PF_KTHREAD,
.prio = MAX_PRIO - 20,
.static_prio = MAX_PRIO - 20,
.normal_prio = MAX_PRIO - 20,
.policy = SCHED_NORMAL,
.cpus_ptr = &init_task.cpus_mask,
.cpus_mask = CPU_MASK_ALL,
.nr_cpus_allowed= NR_CPUS,
.mm = NULL,
.active_mm = &init_mm,
......
.thread_pid = &init_struct_pid,
.thread_group = LIST_HEAD_INIT(init_task.thread_group),
.thread_node = LIST_HEAD_INIT(init_signals.thread_head),
......
};
EXPORT_SYMBOL(init_task);
Here are some important thins we need to make it clearly.
INIT_THREAD_INFO(init_task)
sets the thread_info
as the graph above.init_stack
is defined as belowextern unsigned long init_stack[THREAD_SIZE / sizeof(unsigned long)];
where THREAD_SIZE equal to
#ifdef CONFIG_KASAN
#define KASAN_STACK_ORDER 1
#else
#define KASAN_STACK_ORDER 0
#endif
#define THREAD_SIZE_ORDER (2 + KASAN_STACK_ORDER)
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
so the default size is defined.
.mm = NULL,
.active_mm = &init_mm,
Let's look back at start_kernel
, the rest_init
will initialize kernel_init
and kthreadd
.
noinline void __ref rest_init(void)
{
......
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
......
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
......
}
kernel_init
will run execve
and then go to user space, change to init
process by running , which is process 1.
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
kthread
becomes the daemon process to manage and schedule other kernel task_struts
, which is process 2.
After all this, the process 0 will become idle process and jump out rq
which means it will only run when the rq
is empty.
noinline void __ref rest_init(void)
{
......
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);
}
void cpu_startup_entry(enum cpuhp_state state)
{
arch_cpu_idle_prepare();
cpuhp_online_idle(state);
while (1)
do_idle();
}
Finally, here is a good gitbook for you if you want to get more understanding of Linux kernel.
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