1 |
\chapter{Initialization} |
\chapter{Initialization} |
2 |
\section{start\_kernel Function} \label{init:sk} \index{start\_kernel} |
\section{start\_kernel Function} \label{init:sk} \index{start\_kernel} |
3 |
When a PC is powered on, it initializes all the hardware present in the system and makes sure, every hardware is up and running. So, after initial hardware boot sequence, kernel comes into picture by trasfering control to \textit{start\_kernel} function. This function is defined in the file \url{init/main.c}. Only the first CPU calls start\_kernel(), while all others call function initialize\_secondary()\footnote{defined in \url{arch/i386/kernel/smpboot.c}}. Ihe start\_kernel function performs intialization of the all Operating Systems' Blocks and initializes the data structures used by the kernel. This function calls sub-functions to perform the initialization of IRQ requests, process scheduler, softirq systems, kernel timers, signal mechanism and SMP(symmetric multi-processing) machanism while the \textit{initialize\_secondary} function just copies the stack pointer and EIP values from the Task State Segment(TSS). |
\textbf{\Large W}hen a PC is powered on, it initializes all the hardware present in the system and makes sure, every hardware is up and running. So, after initial hardware boot sequence, kernel comes into picture by transferring control to \textit{start\_kernel} function. This function is defined in the file \url{init/main.c}. Only the first CPU calls start\_kernel(), while all others call function initialize\_secondary()\footnote{defined in \url{arch/i386/kernel/smpboot.c}}. Ihe start\_kernel function performs initialization of the all Operating Systems' Blocks and initializes the data structures used by the kernel. This function calls sub-functions to perform the initialization of IRQ requests, process scheduler, softirq systems, kernel timers, signal mechanism and SMP(symmetric multi-processing) mechanism while the \textit{initialize\_secondary} function just copies the stack pointer and EIP values from the Task State Segment(TSS). |
4 |
\newline \par The code is explained below. |
\\ \par The code is explained below. |
5 |
|
|
6 |
\begin{verbatim} |
\begin{verbatim} |
7 |
|
|
8 |
lock_kernel(); |
lock_kernel(); |
9 |
printk(linux_banner); |
printk(linux_banner); |
10 |
setup_arch(&command_line); |
setup_arch(&command_line); |
11 |
setup_per_cpu_areas(); |
setup_per_cpu_areas(); |
12 |
printk("Kernel command line: %s\n", saved_command_line); |
printk("Kernel command line: %s\n", saved_command_line); |
13 |
parse_options(command_line); |
parse_options(command_line); |
14 |
trap_init(); |
trap_init(); |
15 |
init_IRQ(); |
init_IRQ(); |
16 |
sched_init(); |
sched_init(); |
17 |
softirq_init(); |
softirq_init(); |
18 |
/* timer and memory initialization code */ |
/* timer and memory initialization code */ |
19 |
fork_init(); |
fork_init(); |
20 |
signals_init(); |
signals_init(); |
21 |
smp_init(); |
smp_init(); |
22 |
|
|
23 |
\end{verbatim} |
\end{verbatim} |
24 |
|
|
25 |
\subsection{Macro lock\_kernel} |
\subsection{Macro lock\_kernel} |
26 |
The macro lock\_kernel is defined in in the file \url{include/linux/smp_lock.h}. This is only defined for non-SMP systems, because there are no inter-CPU locks on single CPU systems. On i386 SMP systems, lock\_kernel is an inline function: \url{incllude/asm/i386/smplock.h} |
The macro lock\_kernel is defined in the file \url{include/linux/smp_lock.h}. This is only defined for non-SMP systems, because there are no inter-CPU locks on single CPU systems. On i386 SMP systems, lock\_kernel is an inline function: \url{incllude/asm/i386/smplock.h} |
27 |
\begin{verbatim} |
\begin{verbatim} |
28 |
|
|
29 |
extern __inline__ void lock_kernel(void) |
extern __inline__ void lock_kernel(void) |
30 |
{ |
{ |
31 |
if (!++current->lock_depth) |
if (!++current->lock_depth) |
32 |
spin_lock(&kernel_flag); |
spin_lock(&kernel_flag); |
33 |
} |
} |
34 |
|
|
35 |
\end{verbatim} |
\end{verbatim} |
36 |
|
|
37 |
So on a non-SMP system, the macro expands to 'do\{\} while(0)' and gets optimised away. |
So on a non-SMP system, the macro expands to 'do\{\} while(0)' and gets optimized away. |
38 |
|
|
39 |
\subsection{Functions trap\_init,init\_IRQ} |
\subsection{Functions trap\_init,init\_IRQ} |
40 |
The functions \textit{trap\_init} and \textit{init\_IRQ} are architecture dependant and perform the initilization of IRQ hardware. These functions are defined in the architecture dependant section of kernel code. Let us look at them one by one. |
The functions \textit{trap\_init} and \textit{init\_IRQ} are architecture dependant and perform the initialization of IRQ hardware. These functions are defined in the architecture dependant section of kernel code. Let us look at them one by one. |
41 |
|
|
42 |
Function trap\_init() \label{init:trap_init} |
Function trap\_init() \label{init:trap_init} |
43 |
\textit{File: }\url{arch/i386/kernel/traps.c}\\ \newline |
\textit{File: }\url{arch/i386/kernel/traps.c}\\ \newline |
44 |
This function is used to initialize the IDT with an exception handler functions for each recognized exception. This job is accomplished through the set\_trap\_gate and set\_system\_gate macros. The x86 microprocessors issue 20 different exceptions(0 - 19). The kernel must provide a dedicated exception handler for each exception type. The table in appendix ~\ref{appendix1} shows the exception and its corresponding exception handler along with the signals sent by the exception handlers. |
This function is used to initialize the IDT with an exception handler functions for each recognized exception. This job is accomplished through the set\_trap\_gate and set\_system\_gate macros. The x86 microprocessors issue 20 different exceptions(0 - 19). The kernel must provide a dedicated exception handler for each exception type. The table in appendix ~\ref{appendix1} shows the exception and its corresponding exception handler along with the signals sent by the exception handlers. |
45 |
|
|
46 |
% TODO appendix A |
\\ \par The code is explained below. |
47 |
\newline \par The code is explained below. |
|
48 |
|
\begin{verbatim} |
49 |
\begin{verbatim} |
|
50 |
|
set_trap_gate(0,÷_error); |
51 |
set_trap_gate(0,÷_error); |
set_trap_gate(1,&debug); |
52 |
set_trap_gate(1,&debug); |
set_intr_gate(2,&nmi); |
53 |
set_intr_gate(2,&nmi); |
set_system_gate(3,&int3); /* int3-5 can be called from all */ |
54 |
set_system_gate(3,&int3); /* int3-5 can be called from all */ |
set_system_gate(4,&overflow); |
55 |
set_system_gate(4,&overflow); |
set_system_gate(5,&bounds); |
56 |
set_system_gate(5,&bounds); |
set_trap_gate(6,&invalid_op); |
57 |
set_trap_gate(6,&invalid_op); |
. |
58 |
. |
. |
59 |
. |
. |
60 |
. |
set_trap_gate(19,&simd_coprocessor_error); |
61 |
set_trap_gate(19,&simd_coprocessor_error); |
set_call_gate(&default_ldt[0],lcall7); |
62 |
set_call_gate(&default_ldt[0],lcall7); |
set_call_gate(&default_ldt[4],lcall27); |
63 |
set_call_gate(&default_ldt[4],lcall27); |
|
64 |
|
\end{verbatim} |
65 |
\end{verbatim} |
All these functions map to \_set\_gate macro with appropriate parameters. Please refer to appendix ~\ref{appendix2} for details about different types of gates. The macro \_set\_gate is explained below: |
66 |
All these functions map to \_set\_gate macro with appropriate parameters. Please refer to appendix ~\ref{appendix2} for details about different types of gates. The macro \_set\_gate is explained below: |
|
67 |
|
\\ \par \index{\_set\_gate} |
68 |
%TODO : appendix B |
|
69 |
\newline \par \textbf{Function init\_IRQ()} \index{\_set\_gate} |
\begin{verbatim} |
70 |
|
|
71 |
\begin{verbatim} |
#define _set_gate(gate_addr,type,dpl,addr) \ |
72 |
|
do { \ |
73 |
#define _set_gate(gate_addr,type,dpl,addr) \ |
int __d0, __d1; \ |
74 |
do { \ |
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ |
75 |
int __d0, __d1; \ |
"movw %4,%%dx\n\t" \ |
76 |
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ |
"movl %%eax,%0\n\t" \ |
77 |
"movw %4,%%dx\n\t" \ |
"movl %%edx,%1" \ |
78 |
"movl %%eax,%0\n\t" \ |
:"=m" (*((long *) (gate_addr))), \ |
79 |
"movl %%edx,%1" \ |
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \ |
80 |
:"=m" (*((long *) (gate_addr))), \ |
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ |
81 |
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \ |
"3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \ |
82 |
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ |
} while (0) |
83 |
"3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \ |
|
84 |
} while (0) |
\end{verbatim} |
85 |
|
% TODO Macro explanation |
86 |
\end{verbatim} |
First we shall explain the macro arguments. |
87 |
% TODO Macro explaination |
% TODO Setup TSS and LDT in GDT for each task in appendix 3. |
88 |
First we shall explain the macro arguments. |
|
89 |
% Setup TSS and LDT in GDT for each task |
Function init\_IRQ() \label{init:init_IRQ} |
90 |
|
\textit{File: }\url{arch/i386/kernel/i8259.c}\\ \newline |
91 |
Function init\_IRQ() \label{init:init_IRQ} |
This function is used to setup all interrupt vectors and interrupt gates. Linux kernel uses vectors 0 to 31 for exceptions and nonmaskable interrupts while remaining vectors are software interrupts. Precisely, vectors 32(0x20) to 47(0x2f) are maskable interrupts, caused by IRQs while the remaining vectors ranging from 48 to 255 may be used to identify software interrupts. Also, Linux uses only the 128(0x80) vector, which it uses to implement system calls.[SYSCALL\_VECTOR]. \index{SYSCALL\_VECTOR} |
92 |
\textit{File: }\url{arch/i386/kernel/i8259.c}\\ \newline |
|
93 |
This function is used to setup all interrupt vectors and interrupt gates. Linux kernel uses vectors 0 to 31 for exceptions and nonmaskable interrupts while remaining vectors are software interrupts. Precisely, vectors 32(0x20) to 47(0x2f) are maskable interrupts, caused by IRQs while the remaining vectors ranging from 48 to 255 may be used to identify software interrupts. Also, Linux uses only the 128(0x80) vector, which it uses to implement system calls.[SYSCALL\_VECTOR]. \index{SYSCALL\_VECTOR} |
\\ \par The code is explained below. |
94 |
|
|
95 |
\newline \par The code is explained below. |
\begin{verbatim} |
96 |
|
|
97 |
\begin{verbatim} |
for (i = 0; i < NR_IRQS; i++) { |
98 |
|
/* NR_IRQS 224 |
99 |
for (i = 0; i < NR_IRQS; i++) { |
as per include/asm-i386/irq.h |
100 |
/* NR_IRQS 224 |
/* FIRST_EXTERNAL_VECTOR = 0x20 ie. 32 |
101 |
as per include/asm-i386/irq.h |
as per include/asm-i386/hw_irq.h */ |
102 |
/* FIRST_EXTERNAL_VECTOR = 0x20 ie. 32 |
|
103 |
as per include/asm-i386/hw_irq.h */ |
int vector = FIRST_EXTERNAL_VECTOR + i; |
104 |
|
if (vector != SYSCALL_VECTOR) |
105 |
int vector = FIRST_EXTERNAL_VECTOR + i; |
set_intr_gate(vector, interrupt[i]); |
106 |
if (vector != SYSCALL_VECTOR) |
} |
107 |
set_intr_gate(vector, interrupt[i]); |
set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); |
108 |
} |
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); |
109 |
set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); |
set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); |
110 |
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); |
set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); |
111 |
set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); |
|
112 |
set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); |
\end{verbatim} |
113 |
|
|
114 |
\end{verbatim} |
The interrupts gates corresponding to FIRST\_DEVICE\_VECTOR, RESCHEDULE\_VECTOR, INVALIDATE\_TLB\_VECTOR, CALL\_FUNCTION\_VECTOR are set. These are special IRQ vectors used by the SMP architecture, generally ranging from 0xf0 to 0xff. Refer to \url{include/asm-i386/irq_vectors.h} for more details. |
115 |
|
|
116 |
The interrupts gates corresponding to FIRST\_DEVICE\_VECTOR, RESCHEDULE\_VECTOR, INVALIDATE\_TLB\_VECTOR, CALL\_FUNCTION\_VECTOR are set. These are special IRQ vectors used by the SMP architecture, generally ranging from 0xf0 to 0xff. Refer to \url{include/asm-i386/irq_vectors.h} for more details. |
\subsection{Function sched\_init} |
117 |
|
Process is a basic entity in any unix based system. In a multitasking environment, number of processes are executing on single/multiple CPU/CPUs. Each process gets a fair chance to execute on the CPU depending on the process characteristics. This allocation is done by a special process known as "scheduler". The process scheduler is initialized by calling the function \textit{sched\_init} defined in \url{kernel/sched.c} |
118 |
\subsection{Function sched\_init} |
|
119 |
Process is a basic entity in any unix based system. In a multitasking environment, number of processes are executing on single/multiple CPU/CPUs. Each process gets a fair chance to execute on the CPU depending on the process characteristics. This allocation is done by a special process known as "scheduler". The process scheduler is initialized by calling the function \textit{sched\_init} defined in \url{kernel/sched.c} |
\subsubsection{runqueue data structure} |
120 |
|
Linux Kernel 2.2 had a hardcoded limit on the number of processes. So, an array \textit{tasks} was used to store pointers to all process descriptors. This idea is deprecated in kernel 2.4 and separate lists of tasks in running state and tasks waiting for different O.S. resources are maintained. All the tasks in running state \textit{TASK\_RUNNING} are considered while scheduling. These tasks are defined using runqueues. |
121 |
\subsubsection{runqueue data structure} |
\par The runqueue data structure is explained here in order to understand the initialization code. The scheduler uses an array of runqueues \index{runqueues} as basic data structure defined in \url{kernel/sched.c}. The NR\_CPUS\index{NR\_CPUS}\footnote{defined in \url{include/linux/threads.h}} represents the number of CPUs present in the system. Its value is 32 in SMP mode and 1 in non-SMP mode. |
122 |
Linux Kernel 2.2 had a hardcoded limit on the number of processes. So, an array \textit{tasks} was used to store pointers to all process descriptors. This idea is deprecated in kernel 2.4 and separate lists of tasks in running state and tasks waiting for differnet O.S. resources are maintained. All the tasks in running state \textit{TASK\_RUNNING} are considered while scheduling. These tasks are defined using runqueues. |
\begin{verbatim} |
123 |
\par The runqueue data structure is explained here in order to understand the initialization code. The scheduler uses an array of runqueues \index{runqueues} as basic data structure defined in \url{kernel/sched.c}. The NR\_CPUS\index{NR\_CPUS}\footnote{defined in \url{include/linux/threads.h}} represents the number of CPUs present in the system. Its value is 32 in SMP mode and 1 in non-SMP mode. |
|
124 |
\begin{verbatim} |
struct runqueue { |
125 |
|
spinlock_t lock; |
126 |
struct runqueue { |
unsigned long nr_running, nr_switches, expired_timestamp; |
127 |
spinlock_t lock; |
signed long nr_uninterruptible; |
128 |
unsigned long nr_running, nr_switches, expired_timestamp; |
task_t *curr, *idle; |
129 |
signed long nr_uninterruptible; |
prio_array_t *active, *expired, arrays[2]; |
130 |
task_t *curr, *idle; |
int prev_nr_running[NR_CPUS]; |
131 |
prio_array_t *active, *expired, arrays[2]; |
task_t *migration_thread; |
132 |
int prev_nr_running[NR_CPUS]; |
list_t migration_queue; |
133 |
task_t *migration_thread; |
} ____cacheline_aligned; |
134 |
list_t migration_queue; |
|
135 |
} ____cacheline_aligned; |
static struct runqueue runqueues[NR_CPUS] __cacheline_aligned; |
136 |
|
|
137 |
static struct runqueue runqueues[NR_CPUS] __cacheline_aligned; |
\end{verbatim} |
138 |
|
|
139 |
\end{verbatim} |
The description of the elements of the above structure follows: |
140 |
|
\begin{description} |
141 |
The description of the elements of the above structure follows: |
\item[lock] Spinlock used by the runqueue in order to gain atomic access of the CPU. |
142 |
\begin{description} |
\item[nr\_running] Total number of runnable processes i.e. in TASK\_RUNNING state. |
143 |
\item[lock] Spinlock used by the runqueue in order to gain atomic access of the CPU. |
\item[task\_t curr, idle] Tasks associated with the current runqueue.\footnote{task\_t is typedefinition of task\_struct \url{include/linux/sched.h}} |
144 |
\item[nr\_running] Total number of runnable processes i.e. in TASK\_RUNNING state. |
\item[prio\_array\_t arrays] Priority structure containing the priority bitmap of size BITMAP\_SIZE along with a linked list (queue) of size MAX\_PRIO. |
145 |
\item[task\_t curr, idle] Tasks associated with the current runqueue.\footnote{task\_t is typedefinition of task\_struct \url{include/linux/sched.h}} |
\item[migration\_thread] \index{migration\_thread} Migration thread associated with the runqueue. |
146 |
\item[prio\_array\_t arrays] Priority structure containing the priority bitmap of size BITMAP\_SIZE along with a linked list (queue) of size MAX\_PRIO. |
\item[migration\_queue] \index{migration\_queue} Migration queue associated with the runqueue. |
147 |
\item[migration\_thread] \index{migration\_thread} Migration thread associated with the runqueue.Refer to section ~\ref{psched:structs} for more details. |
\end{description} |
148 |
\item[migration\_queue] \index{migration\_queue} Migration queue associated with the runqueue.Refer to section ~\ref{psched:structs} for more details. |
|
149 |
\end{description} |
\par Each process has a process descriptor associated with it. This process information is stored in \textit{struct task\_struct} defined in \url{include/linux/sched.h}. All process descriptors are linked together by process list and the runqueue list links together process descriptors all the runnable processes. In both cases, the init\_task process descriptor acts as the list header. |
150 |
|
|
151 |
\par Each process has a process descriptor associated with it. This process information is stored in \textit{struct task\_struct} defined in \url{include/linux/sched.h}. All process descriptors are linked together by process list and the runqueue list links together process descriptors all the runnable processes. In both cases, the init\_task process descriptor acts as the list header. |
\subsubsection{Function sched\_init()} \label{init:sched_init} |
152 |
|
\par The function sched\_init initializes the runqueue data structure for all the CPUs. It contains 2 copies of priority array structure which are initialized to active and expired priorities. The INIT\_LIST\_HEAD macro initializes the linked list (queue) of priority structure of size MAX\_PRIO, (value greater than any user task priority \url{include/sched.h} for details) and also clears the priority bitmap. |
153 |
\subsubsection{Function sched\_init()} \label{init:sched_init} |
|
154 |
\par The function sched\_init initializes the runqueue data structure for all the CPUs. It contains 2 copies of priority array structure which are initialized to active and expired priorities. The INIT\_LIST\_HEAD macro initializes the the linked list (queue) of priority structure of size MAX\_PRIO, (value greater than any user task priority \url{include/sched.h} for details) and also clears the priority bitmap. |
\\ \par The code is explained below. |
155 |
|
\begin{verbatim} |
156 |
\newline \par The code is explained below. |
|
157 |
\begin{verbatim} |
for (i = 0; i < NR_CPUS; i++) { |
158 |
|
prio_array_t *array; |
159 |
for (i = 0; i < NR_CPUS; i++) { |
rq = cpu_rq(i); |
160 |
prio_array_t *array; |
rq->active = rq->arrays; |
161 |
rq = cpu_rq(i); |
rq->expired = rq->arrays + 1; |
162 |
rq->active = rq->arrays; |
spin_lock_init(&rq->lock); |
163 |
rq->expired = rq->arrays + 1; |
INIT_LIST_HEAD(&rq->migration_queue); |
164 |
spin_lock_init(&rq->lock); |
for (j = 0; j < 2; j++) { |
165 |
INIT_LIST_HEAD(&rq->migration_queue); |
array = rq->arrays + j; |
166 |
for (j = 0; j < 2; j++) { |
for (k = 0; k < MAX_PRIO; k++) { |
167 |
array = rq->arrays + j; |
INIT_LIST_HEAD(array->queue + k); |
168 |
for (k = 0; k < MAX_PRIO; k++) { |
__clear_bit(k, array->bitmap); |
169 |
INIT_LIST_HEAD(array->queue + k); |
/* refer to include\asm-i386/bitops.h */ |
170 |
__clear_bit(k, array->bitmap); |
} |
171 |
/* refer to include\asm-i386/bitops.h */ |
} |
172 |
} |
__set_bit(MAX_PRIO, array->bitmap); |
173 |
} |
} |
174 |
__set_bit(MAX_PRIO, array->bitmap); |
|
175 |
} |
\end{verbatim} |
176 |
|
The sched\_init function also starts a process in SMP mode by setting up the a runqueue on current CPU, its "curr" and "idle" pointers to itself. Then it initializes all the timers by calling the function init\_timervecs \footnote{init\_timervecs is defined in \url{kernel/timer.c}} and initializes the bottom halves associated with the task queue (TQUEUE\_BH) and immediate queue (IMMEDIATE\_BH). The function \textit{wake\_up\_process} is explained in the chapter ~\ref{sched:all}. The function init\_bh is used to install a bottom half handler into one of the 32 available slots. The bottom half is executed when mark\_bh is invoked. Refer to section ~\ref{int:bh} for more information about \texttt{bottom halves}. |
177 |
\end{verbatim} |
|
178 |
The sched\_init function also starts a process in SMP mode by seting up the a runqueue on current CPU, its "curr" and "idle" pointers to itself. Then it initializes all the timers by calling the function init\_timervecs \footnote{init\_timervecs is defined in \url{kernel/timer.c}} and initializes the bottom halves associated with the task queue (TQUEUE\_BH) and immediate queue (IMMEDIATE\_BH). The function \textit{wake\_up\_process} is explained in the chapter ~\ref{sched:all}. The function init\_bh is used to install a bottom half handler into one of the 32 available slots. The bottom half is executed when mark\_bh is invoked. Refer to section ~\ref{int:bh} for more information about \texttt{bottom halves}. |
\\ \par The code is explained below. |
179 |
|
\begin{verbatim} |
180 |
\newline \par The code is explained below. |
|
181 |
\begin{verbatim} |
rq = this_rq(); |
182 |
|
rq->curr = current; |
183 |
rq = this_rq(); |
rq->idle = current; |
184 |
rq->curr = current; |
wake_up_process(current); |
185 |
rq->idle = current; |
|
186 |
wake_up_process(current); |
init_timervecs(); |
187 |
|
init_bh(TQUEUE_BH, tqueue_bh); |
188 |
init_timervecs(); |
init_bh(IMMEDIATE_BH, immediate_bh); |
189 |
init_bh(TQUEUE_BH, tqueue_bh); |
|
190 |
init_bh(IMMEDIATE_BH, immediate_bh); |
\end{verbatim} |
191 |
|
\subsection{Function softirq\_init} |
192 |
\end{verbatim} |
The concept of tasklets, softirqs are introduced from kernel version 2.4 and the primary tasklet\_struct is defined in \url{include/linux/interrupt.h}. Don't forget to read the properties of tasklets in the include file. |
193 |
\subsection{Function softirq\_init} |
\par Softirqs were introduced that take advantage of multiple processors in an SMP, and allow each CPU to run a softirq. Softirqs are thus, multithreaded analogue of Bottom Halves\index{bottom half} which can run on multiple CPUs at once. The deprecated bottom-halves are re-implemented using softirqs. Both bottom-halves and softirqs are statically registered. The 2.4 Linux kernel also introduce tasklets, which are dynamically registrable softirqs, which are guaranteed to only run on one CPU at a time. Refer section ~\ref{int:tasklets} for more information about \texttt{Tasklets}. |
194 |
The concept of tasklets, softirqs are introduced from kernel version 2.4 and the primary tasklet\_struct is defined in \url{include/linux/interrupt.h}. Don't forget to read the properties of tasklets in the include file. |
|
195 |
\par Softirqs were introduced that take advantage of multiple processors in an SMP, and allow each CPU to run a softirq. Softirqs are thus, multithreaded analogue of Bottom Halves\index{bottom half} which can run on multiple CPUs at once. The deprecated bottom-halves are reimplemented using softirqs. Both bottom-halves and softirqs are statically registered. The 2.4 Linux kernel also introduce tasklets, which are dynamically registrable softirqs, which are guaranteed to only run on one CPU at a time. Refer section ~\ref{int:tasklets} for more information about \texttt{Tasklets}. |
\subsubsection{Function softirq\_init()} \label{init:softirq_init} |
196 |
|
\textit{File: }\url{kernel/softirq.c}\\ \newline |
197 |
\subsubsection{Function softirq\_init()} \label{init:softirq_init} |
The softirq\_init function initializes all the tasklets \index{tasklets} by calling \textit{tasklet\_init} function. A global array \textbf{struct tasklet\_struct bh\_task\_vec[32]} is used to initialize all the tasklets. These tasklets are associated with first 32 (0x00-0x1F) IRQ(software interrupt) lines. The function \textit{tasklet\_init} associates the function bh\_action as the IRQ handler for all IRQs from 0 to 31. |
198 |
\textit{File: }\url{kernel/softirq.c}\\ \newline |
\par The function open\_softirq initializes the softirqs related to TASKLET\_SOFTIRQ and HI\_SOFTIRQ. The function pointers tasklet\_action and tasklet\_hi\_action are stored in an array related to softirq action (consists of function and data). |
199 |
The softirq\_init function initializes all the tasklets \index{tasklets} by calling \textit{tasklet\_init} function. A global array \textbf{struct tasklet\_struct bh\_task\_vec[32]} is used to initialize all the tasklets. These tasklets are associated with first 32 (0x00-0x1F) IRQ(software interrupt) lines. The function \textit{tasklet\_init} associates the function bh\_action as the IRQ handler for all IRQs from 0 to 31. |
|
200 |
\par The function open\_softirq initializes the softirqs related to TASKLET\_SOFTIRQ and HI\_SOFTIRQ. The function pointers tasklet\_action and tasklet\_hi\_action are stored in an array related to softirq action (consists of function and data). |
\\ \par The code is explained below. \index{softirq\_vec} |
201 |
|
|
202 |
\newline \par The code is explained below. \index{softirq\_vec} |
\begin{verbatim} |
203 |
|
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp; |
204 |
\begin{verbatim} |
\end{verbatim} |
205 |
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp; |
|
206 |
\end{verbatim} |
\begin{verbatim} |
207 |
|
|
208 |
\begin{verbatim} |
for (i=0; i<32; i++) |
209 |
|
tasklet_init(bh_task_vec+i, bh_action, i); |
210 |
for (i=0; i<32; i++) |
|
211 |
tasklet_init(bh_task_vec+i, bh_action, i); |
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); |
212 |
|
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); |
213 |
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); |
|
214 |
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); |
\end{verbatim} |
215 |
|
|
216 |
\end{verbatim} |
\subsection{Function fork\_init} |
217 |
|
Function fork\_init() \label{init:fork_init} \index{max\_threads} |
218 |
\subsection{Function fork\_init} |
\textit{File: }\url{kernel/fork.c}\\ \newline |
219 |
Function fork\_init() \label{init:fork_init} \index{max\_threads} |
The fork\_init function calls \textit{kmem\_cache\_create} \footnote{Refer to \url{mm/slab.c} for more details of slab and memory allocation} to initialize the slab cache related to task\_structs. It also determines the maximum number of threads depending upon the physical memory available. The name parameter passed is \texttt{task\_struct} which can be found in the file \url{/proc/slabinfo}. The \textit{max\_threads} value is used to set the resource limits for the \textit{init\_task}.\footnote{The init\_task is defined in a special file containing only data \url{arch/i386/kernel/init_task.c} which calls the macro \textit{INIT\_TASK} \index{init\_task} defined in \url{include/linux/init_task.h}} |
220 |
\textit{File: }\url{kernel/fork.c}\\ \newline |
|
221 |
The fork\_init function calls \textit{kmem\_cache\_create} \footnote{Refer to \url{mm/slab.c} for more detailes of slab and memory allocation} to initialize the slab cache related to task\_structs. It also determines the maximum number of threads depending upon the physical memory available. The name parameter passed is \texttt{task\_struct} which can be found in the file \url{/proc/slabinfo}. The \textit{max\_threads} value is used to set the resource limits for the \textit{init\_task}.\footnote{The init\_task is defined in a special file containing only data \url{arch/i386/kernel/init_task.c} which calls the macro \textit{INIT\_TASK} \index{init\_task} defined in \url{include/linux/init_task.h}} |
\begin{verbatim} |
222 |
|
|
223 |
\begin{verbatim} |
task_struct_cachep = |
224 |
|
kmem_cache_create("task_struct", |
225 |
task_struct_cachep = |
sizeof(struct task_struct),0, |
226 |
kmem_cache_create("task_struct", |
SLAB_HWCACHE_ALIGN, NULL, NULL); |
227 |
sizeof(struct task_struct),0, |
if (!task_struct_cachep) |
228 |
SLAB_HWCACHE_ALIGN, NULL, NULL); |
panic("fork_init(): cannot create task_struct SLAB cache"); |
229 |
if (!task_struct_cachep) |
|
230 |
panic("fork_init(): cannot create task_struct SLAB cache"); |
max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8; |
231 |
|
|
232 |
max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8; |
init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2; |
233 |
|
init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2; |
234 |
init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2; |
|
235 |
init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2; |
\end{verbatim} |
236 |
|
|
237 |
\end{verbatim} |
\begin{verbatim} |
238 |
|
|
239 |
\begin{verbatim} |
/* Please, Note |
240 |
|
* value for mempages is set in the function setup_arch() |
241 |
/* Please, Note |
* in arch/i386/kernel/setup.c |
242 |
* value for mempages is set in the function setup_arch() |
*/ |
243 |
* in arch/i386/kernel/setup.c |
#define THREAD_SIZE (2*PAGE_SIZE) /* include/asm-i386/thread_info.h */ |
244 |
*/ |
/* Effectively, max_threads = mempages / 2*8 ; */ |
245 |
#define THREAD_SIZE (2*PAGE_SIZE) /* include/asm-i386/thread_info.h */ |
|
246 |
/* Effectively, max_threads = mempages / 2*8 ; */ |
\end{verbatim} |
247 |
|
|
248 |
\end{verbatim} |
\subsection{Function signals\_init} |
249 |
|
The signals\_init also calls function \textit{kmem\_cache\_create} to initialize the slab cache related to signals and to create a signals related cache. The code can be found in \url{kernel/signal.c}. The name parameter passed is \texttt{sigqueue} which can be found in the file \url{/proc/slabinfo}. |
250 |
\subsection{Function signals\_init} |
|
251 |
The signals\_init also calls function \textit{kmem\_cache\_create} to initialize the slab cache related to signals and to create a signals related cache. The code can be found in \url{kernel/signal.c}. The name parameter passed is \texttt{sigqueue} which can be found in the file \url{/proc/slabinfo}. |
\begin{verbatim} |
252 |
|
|
253 |
\begin{verbatim} |
sigqueue_cachep = |
254 |
|
kmem_cache_create("sigqueue", |
255 |
sigqueue_cachep = |
sizeof(struct sigqueue), |
256 |
kmem_cache_create("sigqueue", |
__alignof__(struct sigqueue), |
257 |
sizeof(struct sigqueue), |
SIG_SLAB_DEBUG, NULL, NULL); |
258 |
__alignof__(struct sigqueue), |
if (!sigqueue_cachep) |
259 |
SIG_SLAB_DEBUG, NULL, NULL); |
panic("signals_init(): cannot create sigqueue SLAB cache"); |
260 |
if (!sigqueue_cachep) |
|
261 |
panic("signals_init(): cannot create sigqueue SLAB cache"); |
\end{verbatim} |
262 |
|
|
263 |
\end{verbatim} |
\subsection{Function init\_idle} \label{init:idle} \index{init\_idle} |
264 |
\subsection{Function smp\_init} |
The init\_idle function is called to create idle task on the processor. The idle task has highest priority and runs with the TASK\_RUNNING state. The function is called here, for the processor performing the boot setup. The function is also called for all the secondary processors when they are initialized in the \textit{do\_boot\_cpu} function, called by \textit{smp\_init}. |
265 |
The smp\_init is architecture dependant function used to perform SMP initialization of all CPUs. The function code invokes functions smp\_boot\_cpus, do\_boot\_cpus from \url{arch/i386/kernel/smpboot.c}. These functions perform basic SMP related initialization. Refer to section \ref{smp:boot} for more details on SMP initialization and scheduling. |
\begin{verbatim} |
266 |
|
void __init init_idle(task_t *idle, int cpu) |
267 |
\begin{verbatim} |
{ |
268 |
|
runqueue_t *idle_rq = cpu_rq(cpu), *rq = cpu_rq(idle->thread_info->cpu); |
269 |
/* From init/main.c for SMP mode */ |
unsigned long flags; |
270 |
|
|
271 |
static void __init smp_init(void) |
__save_flags(flags); |
272 |
{ |
__cli(); |
273 |
smp_boot_cpus(); |
double_rq_lock(idle_rq, rq); |
274 |
smp_threads_ready=1; |
|
275 |
smp_commence(); |
idle_rq->curr = idle_rq->idle = idle; |
276 |
} |
deactivate_task(idle, rq); |
277 |
|
idle->array = NULL; |
278 |
\end{verbatim} |
idle->prio = MAX_PRIO; |
279 |
|
idle->state = TASK_RUNNING; |
280 |
\par After completing all the initialization stuff, what does the kernel do? Correct, it sits idle. The function \textit{cpu\_idle}\label{init:idle} is architecture dependant and is defined in \url{arch/i386/kernel/process.c}. The kernel waits for some process to get scheduled using \textit{schedule()} function. |
idle->thread_info->cpu = cpu; |
281 |
\begin{verbatim} |
double_rq_unlock(idle_rq, rq); |
282 |
|
set_tsk_need_resched(idle); |
283 |
while(1) { |
__restore_flags(flags); |
284 |
while (!need_resched()) |
|
285 |
idle(); |
/* Set the preempt count _outside_ the spinlocks! */ |
286 |
schedule(); |
idle->thread_info->preempt_count = (idle->lock_depth >= 0); |
287 |
} |
} |
288 |
|
\end{verbatim} |
289 |
\end{verbatim} |
The interupts are disbled when the idle task is initialized and reenabled when the task is running. The idle task has following properties: |
290 |
|
\begin{enumerate} |
291 |
|
\item The idle task is not part of the processor runqueue. |
292 |
|
\item The talk is removed from the list of running tasks. |
293 |
|
\item The priority is set to MAX\_PRIO. |
294 |
|
\item \textit{goodness()} [priority function] should be never run on the idle\_task. |
295 |
|
\item The task as RUNNING state. |
296 |
|
\item idle task in NOT part of pid hashing table. |
297 |
|
\end{enumerate} |
298 |
|
|
299 |
|
\subsection{Function smp\_init} |
300 |
|
The smp\_init is architecture dependant function used to perform SMP initialization of all CPUs. The function code invokes functions smp\_boot\_cpus, do\_boot\_cpus from \url{arch/i386/kernel/smpboot.c}. These functions perform basic SMP related initialization. Refer to section \ref{smp:boot} for more details on SMP initialization and scheduling. |
301 |
|
|
302 |
|
\begin{verbatim} |
303 |
|
|
304 |
|
/* From init/main.c for SMP mode */ |
305 |
|
|
306 |
|
static void __init smp_init(void) |
307 |
|
{ |
308 |
|
smp_boot_cpus(); |
309 |
|
smp_threads_ready=1; |
310 |
|
smp_commence(); |
311 |
|
} |
312 |
|
|
313 |
|
\end{verbatim} |
314 |
|
|
315 |
|
\par After completing all the initialization stuff, what does the kernel do? Correct, it sits idle. The function \textit{cpu\_idle} ~\ref{init:idle} is architecture dependant and is defined in \url{arch/i386/kernel/process.c}. The kernel waits for some process to get scheduled using \textit{schedule()} function. |
316 |
|
\begin{verbatim} |
317 |
|
|
318 |
|
while(1) { |
319 |
|
while (!need_resched()) |
320 |
|
idle(); |
321 |
|
schedule(); |
322 |
|
} |
323 |
|
|
324 |
|
\end{verbatim} |
325 |
|
|