Computer-Science

Installing/Reading Linux Kernel

1. Whatโ€™s in the linux kernel

Linux File Tree

init: start up code

kernel: cpu-indendent code for basic system management

arch : cpu-dependent code for basic system management

fs: file system code

mm : memory management code

net: network handling code

drivers: device handling code

include : include files

ipc: code for inter-process communication

2. Linux starting location: init/main.c: start_kernel()

3. Background

  1. Linux is the first program that runs when the system boots.

  2. Linux begins at init/main.c/start_kernel().

  3. Linux uses printk (not โ€œprintfโ€) to display messages.

  4. All booting messages are displayed by Linux with printk and you can find the corresponding Linux code that prints each boot message.

  5. All boot messages are stored in the kernel buffer and you can display this buffer with dmesg command.

  6. Find Linux code that prints the first boot message.

4. Exercise

1. Install Gentoo Linux on virtual machine.

1.0) Download Virtualbox from Internet and install.

https://www.virtualbox.org/wiki/Downloads

1.1) Download Gentoo virtualbox files (gentoo.zip) and un-compress it.

1.2) Run VirtualBox, and click File>Import and go to the gentoo directory. Select Gentoo2.ovf. Uncheck USB controller. Select Import. This will install Gentoo Linux on your virtual box.

1.3) Run Gentoo.

If you have an error, run VirutalBox as administrator and try again. For USB error, simply disable usb controller in โ€œSettingโ€ tab. For Hyper-V error (Raw-mode is unavailable), turn off Hyper-V feature in control panel>program and feature>window feature. Select My Linux. Login as root and hit Enter for the password prompt. If VirtualBox still cannot open the session, you need to look at the log file (right click on Gentoo VM and select โ€œlog fileโ€) and see what is the error and fix it. (In some cases, you may need to download and install virtualbox extension package.)

1.4) Make a simple program, ex1.c, with vi which displays โ€œhello worldโ€. Compile and run it.

2. Go to linux-2.6.25.10 directory and find all the files referred in Section 1 such as main.c, fork.c, entry_32.S, etc.

main.c

main.c๋Š” init ํด๋”์— ์žˆ์œผ๋ฉฐ ๋ฆฌ๋ˆ…์Šค์˜ ์‹œ์ž‘์ ์ด๋‹ค. start_kernel() ํ•จ์ˆ˜๋„ main.c์— ์œ„์น˜ํ•œ๋‹ค.

fork.c

fork.c๋Š” kernel ํด๋”์— ์žˆ์œผ๋ฉฐ ์ด ํด๋”์—๋Š” ํ”„๋กœ์„ธ์Šค์™€ ๊ด€๋ จ๋œ ํŒŒ์ผ์ด ์žˆ๋‹ค.

entry_32.S

find / -name entry_32.S -type f

find ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด entry_32.S์˜ ์œ„์น˜๋ฅผ ์ฐพ์•„๋ณด๋ฉด entry_32.S๋Š” โ€œarch/x86/kernelโ€์™€ โ€œarch/powerpc/kernelโ€์— ์žˆ๋‹ค. ์ด ์ค‘ โ€œx86โ€ ํด๋”์—๋Š” ์•„ํ‚คํ…์ฒ˜๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋“ค์ด ๋‹ด๊ฒจ์žˆ์œผ๋ฉฐ โ€œx86โ€์€ ์ธํ…”์˜ 32๋น„ํŠธ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

3. Find the location of start_kernel().

To find a string โ€œstart_kernelโ€, go to the linux top directory (linux-2.6.25.10) and do

$ grep -nr "start_kernel" * | more

Use "space" to move down the screen, q to exit

Once you found the file that has โ€œstart_kernelโ€, use vi to read the file.

In vi, type /start_kernel to search for the first instance of โ€œstart_kernelโ€.

For the next string, simply type โ€œ/โ€. Repeat โ€œ/โ€ until you find the start_kernel() function.

Use โ€œjโ€ to mode down the cursor, โ€œkโ€ to move up, โ€œ^fโ€ to move one screen down, โ€œ^bโ€ to move
up one screen up.

4. start_kernel() is the first C function run by Linux.
Predict what will be the first message appearing on the screen by analyzing start_kernel().
Note that printk() (not printf) is the function to print something in the kernel.
Confirm your prediction with dmesg > x and vi x.
The kernel remembers the booting message in the system buffer and dmesg displays the content of this buffer to the screen.
dmesg > x will send the booting message to file x.
With vi x you can look at the file x.

start_kernel() ํ•จ์ˆ˜์˜ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์ด ์ค‘ ๊ฐ€์žฅ ๋จผ์ € printk๊ฐ€ ์‚ฌ์šฉ๋œ ๋ถ€๋ถ„์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

printk(KERNEL_NOTICE);
printk(linux_banner);

์ฒซ ๋ฒˆ์งธ ์ถœ๋ ฅ์€ ์œ„ ๋‘ ํ•จ์ˆ˜ ํ˜ธ์ถœ๋กœ KERN_NOTICE๋Š” ์ถœ๋ ฅ์˜ ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•˜๋Š” ๊ธฐํ˜ธ๋กœ ๋‹ค์Œ ์ถœ๋ ฅ์ด ์ •์ƒ์ ์ธ ์ •๋ณด์— ํ•ด๋‹นํ•˜๋Š” ๋กœ๊ทธ์ž„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด linux_banner๊ฐ€ ์ง„์งœ ์ถœ๋ ฅ์ธ๋ฐ ํ•ด๋‹น ๊ฐ’์ด ์ •์˜๋œ init/version.c์œผ๋กœ ๊ฐ€๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฆฌ๋ˆ…์Šค์˜ ๋ฒ„์ „๊ณผ ์ปดํŒŒ์ผ ํ™˜๊ฒฝ์„ ์ถœ๋ ฅํ•˜๋Š” ๋ฌธ์ž์—ด์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

init/version.c:

/* FIXED STRINGS! Don't touch! */
const char linux_banner[] =
    "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
    LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";

๋”ฐ๋ผ์„œ ๋ฆฌ๋ˆ…์Šค๊ฐ€ ๋ถ€ํŒ…๋˜๋ฉด ๋ฆฌ๋ˆ…์Šค์˜ ๋ฒ„์ „๊ณผ ์ปดํŒŒ์ผ ํ™˜๊ฒฝ์ด ๊ฐ€์žฅ ๋จผ์ € ์ถœ๋ ฅ๋  ๊ฒƒ์ด๋‹ค.

dmesg > x๋ฅผ ํ†ตํ•ด ๋ถ€ํŒ… ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•ด๋ณธ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๋ฆฌ๋ˆ…์Šค ๋ฒ„์ „๊ณผ GCC ์ •๋ณด๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ์ถœ๋ ฅ๋œ๋‹ค.

5. Find the location of the following functions called in start_kernel() and briefly explain your guessing about what each function is doing (but do not copy the whole code).

trap_init()

$ grep -nr "trap_init" * | more

linux-2.6.25.10/arch/x86/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œtrap_initโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

arch/x86/kernel/traps_64.c:1127์™€ arch/x86/kernel/traps_32.c:1140์—์„œ void __init trap_init(void)๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi kernel/traps_32.c

์œ„ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด traps_32.c ํŒŒ์ผ์˜ trap_init(void) ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์•˜๋‹ค.

set_trap_gate(0,&divide_error);
set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
set_system_intr_gate(3, &int3); /* int3/4 can be called from all */
set_system_gate(4,&overflow);
set_trap_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);

trap_init() ํ•จ์ˆ˜์—๋Š” ์œ„์™€ ๊ฐ™์ด set ํ•จ์ˆ˜๊ฐ€ ๋‚˜์—ด๋˜์–ด ์žˆ๋‹ค. ์ด๊ฒƒ์œผ๋กœ ์œ ์ถ”ํ•ด๋ณธ๋‹ค๋ฉด, ์‹œ์Šคํ…œ์ฝœ์„ ์œ„ํ•œ ์…‹ํŒ…์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

init_IRQ()

$ grep -nr "init_IRQ" * | more

linux-2.6.25.10/arch/x86/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œinit_IRQโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

arch/x86/kernel/paravirt.c:167์—์„œ void init_IRQ(void)๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi kernel/paravirt.c

$ vi kernel/i8259_64.c

i8259_64.c์˜ native_init_IRQ(void) ํ•จ์ˆ˜์˜

...
set_intr_gate(vector, interrupt[i]);
...

๋ฅผ ๋ณด๋ฉด, interrupt gate๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์œ ์ถ”ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

IRQ๋Š” Interrupt ReQuest์˜ ์•ฝ์ž๋กœ, ์ธํ„ฐ๋ŸฝํŠธ ์‹ ํ˜ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ์— ์“ฐ์ด๋Š” ์ปดํ“จํ„ฐ ๋ฒ„์Šค ๋ผ์ธ์˜ ์ธํ„ฐ๋ŸฝํŠธ ๋™์ž‘์„ ๋งํ•œ๋‹ค.

init_IRQ()๋Š” ์ปค๋„ interrupt subsystem์˜ ํ•˜๋“œ์›จ์–ด ์ผ๋ถ€๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋‹ค.

sched_init()

$ grep -nr "sched_init(void)" *

linux-2.6.25.10/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œsched_initโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

kernel/sched.c:7261์—์„œ void __init sched_init(void)๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi kernel/sched.c

sched_init(void)๋Š” init task๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” CPU ๋ฒˆํ˜ธ๋ฅผ ํ• ๋‹นํ•˜๊ณ  PID HASH TABLE์„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ปค๋„์˜ ๋‚ด๋ถ€ ํƒ€์ด๋จธ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ฒกํ„ฐ ๋ฐ bottom-half handler๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.

time_init()

$ grep -nr "time_init(void)" *

linux-2.6.25.10/arch/x86/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œtime_initโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

arch/x86/kernel/time_32.c:135์™€ arch/x86/kernel/time_64.c:117์—์„œ void __init time_init(void)๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi kernel/time_32.c

$ grep -nr "tsc_init" *

$ vi kernel/tsc_32.c

CMOS์—์„œ ์‹œ๊ฐ„์„ ์ฝ๊ณ  CPU์˜ ์†๋„๋ฅผ ์–ป์–ด๋‚ด๋Š” ๊ฒƒ์œผ๋กœ ์ถ”์ธกํ•  ์ˆ˜ ์žˆ๋‹ค.

console_init()

$ grep -nr "__init console_init(void)" *

linux-2.6.25.10/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œconsole_initโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

drivers/char/tty_io.c:4037์— void __init console_init(void) ํ•จ์ˆ˜๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi drivers/char/tty_io.c

์ปค๋„์˜ ์ง๋ ฌ ์ฝ˜์†” ๋””๋ฐ”์ด์Šค๊ฐ€ ์‚ฌ์šฉํ•˜๋„๋ก ๊ตฌ์„ฑ๋œ ๊ฒฝ์šฐ ์ดˆ๊ธฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.

mem_init()

$ grep -nr "mem_init" *

linux-2.6.25.10/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œmem_initโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

arch/x86/mm/init_64.c:511์™€ arch/x86/mm/init_32.c:569์—์„œ void __init mem_init(void)๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi mm/init_32.c

์œ„ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด init_32.c ํŒŒ์ผ์˜ void __init mem_init(void) ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์•˜๋‹ค.

์ปค๋„์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์„œ๋ธŒ ์‹œ์Šคํ…œ์„ ์ดˆ๊ธฐํ™”๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ํ•จ์ˆ˜๋กœ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ๋‹ค.

rest_init()

$ grep -nr "rest_init" *

linux-2.6.25.10/์—์„œ ์œ„ ๋ช…๋ น์–ด๋กœ โ€œrest_initโ€์„ ์ฐพ์•„๋ณด์•˜๋‹ค.

rest_init() ํ•จ์ˆ˜๋Š” linux-2.6.25.10/init/main.c์— ์œ„์น˜ํ•œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

$ vi init/main.c

์œ„ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด main.c ํŒŒ์ผ์˜ rest_init(void) ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์•˜๋‹ค.

์ดˆ๊ธฐํ™” ๊ธฐ๋Šฅ์— ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œํ•œ ํ›„ ์ปค๋„ thread๋กœ init()์„ ์‹œ์ž‘ํ•˜์—ฌ ์ปค๋„ ๋ถ€ํŒ…์„ ์™„๋ฃŒํ•œ๋‹ค.

6. Why canโ€™t we use โ€œprintfโ€ instead of โ€œprintkโ€ in Linux kernel?