Computer-Science

Lecture 3. PC Hardware, Execution Cycle, Boot Sequence, GRUB Boot Loader, Kernel Compiling

1. PC Hardware

1) computer

Where are the files located? Whatโ€™s in memory?

2) Intel x86 cpu

2. Execution Cycle

3. Boot Sequence

Refer to http://duartes.org/gustavo/blog/post/how-computers-boot-up or Appendix A of โ€œUnderstanding Linux Kernelโ€ book.

Power ON

BIOS(Basic Input Output System) starts

GRUB boot loader starts

OS(Linux) starts

login program starts

4. Kernel Compiling and Rebooting

$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage
$ reboot

5. Replace boot image

............
title=Gentoo Linux
root (hd0,0)
kernel /kernel-genkernel-x86-2.6.23-gentoo-r8 root= ............
initrd ..............
title=My Linux
root (hd0,0)
kernel /boot/bzImage root=..........
initrd .......
$ cp arch/x86/boot/bzImage /boot/bzImage
$ reboot

6. Homework

0) Boot Sequence.

0-1) When you boot the Linux system, the first program that runs is BIOS. Where is this program (the memory location)?

BIOS๋Š” ๋ฉ”๋ชจ๋ฆฌ 0xFFFFFFF0์— ์œ„์น˜ํ•œ๋‹ค.

0-2) BIOS loads and runs the boot loader program (GRUB in Linux). Where is this GRUB program?

GRUB๋Š” boot disk์˜ ์ฒซ๋ฒˆ์งธ sector์— ์กด์žฌํ•œ๋‹ค.

0-3) GRUB loads and runs the Linux executable file. Where is Linux executable file? How GRUB knows the location of Linux executable file?

1) Simple modification of the kernel.

after
   printk(linux_banner);
Add
   printk("hello from me\n");

in start_kernel(). Go to the Linux top directory and compile the kernel and replace the boot image. Reboot with this new kernel, and run dmesg to see if the kernel is printing โ€œhello from meโ€.

$ cd linux-2.6.25.10
$ cd init
$ vi main.c
....... modify start_kernel()
$ cd ..           -- go back to the Linux top directory
$ make bzImage    -- recompile the kernel
$ cp arch/x86/boot/bzImage /boot/bzImage
                  -- copy the new kernel image to boot location
                  -- answer "y"
$ reboot           -- reboot the system with this new kernel and select "My Linux"
......
(select "My Linux")
$ dmesg > x
$ vi x              -- now check if we can see our new message

์‚ฌ์ง„๊ณผ ๊ฐ™์ด start_kernel() ํ•จ์ˆ˜ ๋‚ด์—์„œ printk(linux_banner); ๋‹ค์Œ ์ค„์— printk("HELLO FROM ME!!!\n");๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

๊ทธ ๋‹ค์Œ make bzImage ๋ช…๋ น์–ด๋กœ kernel์„ recompile ํ•ด์ฃผ์—ˆ๋‹ค.

reboot ํ›„, dmesg > x๋ฅผ ํ†ตํ•ด ๋ถ€ํŒ…๋ฉ”์‹œ์ง€์˜ ๋‘๋ฒˆ์งธ ์ค„์„ ํ™•์ธํ•ด๋ณด๋ฉด ์ถ”๊ฐ€ํ•œ โ€œHELLO FROM ME!!!โ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

2) start_kernel() calls trap_init(), and there are many trap_init() functions defined in the kernel code. Make an intelligent guess about which trap_init() would be called and insert some printk() in the beginning of this trap_init() to see if it is really called by the kernel. Use grep in the top directory of the linux source tree to find out the locations of trap_init():

$ grep -nr "trap_init" * | more

๋ฆฌ๋ˆ…์Šค์˜ ํƒ‘ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ grep ๋ช…๋ น์–ด๋กœ โ€œtrap_initโ€ ํ•จ์ˆ˜๋ฅผ ์ฐพ์€ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์—ฌ๋Ÿฌ ํŒŒ์ผ์ด trap_init() ํ•จ์ˆ˜๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€๋งŒ, kernel์— ์ถœ๋ ฅ(printk)๋˜๋„๋ก ์ˆ˜์ •ํ•˜๋ ค๋ฉด, ํ˜„์žฌ ๋ฆฌ๋ˆ…์Šค์˜ ์•„ํ‚คํ…์ฒ˜๊ฐ€ x86 32๋น„ํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— arch/x86/kernel/ ๋””๋ ‰ํ† ๋ฆฌ์˜ traps_32.c ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

vi ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด printk("trap_init function is called by the kernel!!!\n");๋ผ๋Š” ๊ตฌ๋ฌธ์„ trap_init() ํ•จ์ˆ˜์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage

์œ„ ๋ช…๋ น์–ด๋“ค๋กœ ์ˆ˜์ •์‚ฌํ•ญ์„ ์ปดํŒŒ์ผ ํ•ด์ฃผ๊ณ , reboot ํ•˜์˜€๋‹ค.

$ vi dmesg > x
$ vi x

๋ถ€ํŒ… ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด 63๋ฒˆ์งธ ์ค„์—์„œ๋ถ€ํ„ฐ trap_init์ด ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

3) Find also the exact locations of init_IRQ() and insert some printk() in the beginning of init_IRQ() to confirm (actually you insert it in native_init_IRQ).
Do the same thing for init_timers() and time_init().

$ grep -nr "native_init_IRQ(void)" * | more

๋ฆฌ๋ˆ…์Šค์˜ ํƒ‘ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ grep ๋ช…๋ น์–ด๋กœ โ€œinit_IRQโ€ ํ•จ์ˆ˜๋ฅผ ์ฐพ์€ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์—ฌ๋Ÿฌ ํŒŒ์ผ์ด native_init_IRQ(void) ํ•จ์ˆ˜๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€๋งŒ, kernel์— ์ถœ๋ ฅ(printk)๋˜๋„๋ก ์ˆ˜์ •ํ•˜๋ ค๋ฉด, ํ˜„์žฌ ๋ฆฌ๋ˆ…์Šค์˜ ์•„ํ‚คํ…์ฒ˜๊ฐ€ x86 32๋น„ํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— arch/x86/kernel/ ๋””๋ ‰ํ† ๋ฆฌ์˜ i8259_32.c ํŒŒ์ผ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

vi ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด printk("init_IRQ function is called by the kernel!!!\n");๋ผ๋Š” ๊ตฌ๋ฌธ์„ native_init_IRQ(void) ํ•จ์ˆ˜์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.


๋˜ํ•œ, ๋ฆฌ๋ˆ…์Šค์˜ ํƒ‘ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ grep ๋ช…๋ น์–ด๋กœ โ€œinit_timersโ€ ํ•จ์ˆ˜์™€ โ€œtime_initโ€ ํ•จ์ˆ˜๋ฅผ ์ฐพ์€ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

$ grep -nr "init_timers(void)" *
$ grep -nr "__init time_init(void)" *

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์—ฌ๋Ÿฌ ํŒŒ์ผ๋“ค ์ค‘, kernel ํด๋” ํ•˜์œ„์— ์žˆ๋Š” ํŒŒ์ผ์ธ kernel/timer.c์™€ arch/x86/kernel/time_32.c๋ฅผ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค.

$ vi kernel/timer.c
$ vi arch/x86/kernel/time_32.c

vi ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด printk("init_timers function is called by the kernel!!!\n");์™€ printk("time_init function is called by the kernel!!!\n");๋ผ๋Š” ๊ตฌ๋ฌธ์„ ๊ฐ๊ฐ init_timers(void) ํ•จ์ˆ˜์™€ time_init(void) ํ•จ์ˆ˜์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage

์œ„ ๋ช…๋ น์–ด๋“ค๋กœ ์ˆ˜์ •์‚ฌํ•ญ์„ ์ปดํŒŒ์ผ ํ•ด์ฃผ๊ณ , reboot ํ•˜์˜€๋‹ค.

$ vi dmesg > x
$ vi x

๋ถ€ํŒ… ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด init_IRQ, init_timers, time_init ํ•จ์ˆ˜๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœ๋˜๋ฉฐ, ์ถ”๊ฐ€ํ•œ 3์ข…๋ฅ˜์˜ ๋ฌธ๊ตฌ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

4) Modify /boot/grub/grub.conf so that GRUB displays another Linux selection, My Linux2. Set the location of the kernel for this linux as /boot/bzImage2. Prepare two versions of My Linux such that when you select โ€œMy Linuxโ€ the kernel will display โ€œhello from My Linuxโ€, and when you select โ€œMy Linux2โ€, it displays โ€œhello from My Linux2โ€.

$ cd /boot/grub
$ vi grub.conf

์œ„์™€ ๊ฐ™์€ ๋ช…๋ น์–ด๋กœ /boot/grub/grub.conf ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์˜€๋‹ค.

โ€œtitle=My Linuxโ€๊ฐ€ ์žˆ๋Š” ์ค„์— ์ปค์„œ๋ฅผ ๋†“๊ณ , 4์ค„์„ copy ํ•˜๊ธฐ ์œ„ํ•ด 4yy ๋ช…๋ น์–ด๋กœ ๋ณต์‚ฌ ํ›„, p ๋ช…๋ น์–ด๋กœ ๋ถ™์—ฌ๋„ฃ์—ˆ๋‹ค.

๋ถ™์—ฌ๋„ฃ์€ ํ…์ŠคํŠธ ์ค‘ title=My Linux์„ title=My Linux2๋กœ, kernel /boot/bzImage์„ kernel /boot/bzImage2๋กœ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

๊ทธ ๋‹ค์Œ

$ vi init/main.c

start_kernel() ํ•จ์ˆ˜๋กœ ๊ฐ€์„œ Linux_banner ์œ„์— โ€œhello from My Linuxโ€๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก ์ €์žฅํ•˜์˜€๋‹ค.

$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage
cp: overwrite '/boot/bzImage'? y

๋กœ โ€œMy Linuxโ€ kernel์„ ์ปดํŒŒ์ผํ•˜์˜€๋‹ค.

์ดํ›„ โ€œMy Linuxโ€ kernel๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ โ€œMy Linux2โ€ kernel์„ ์ˆ˜์ •ํ•˜์˜€๊ณ , ์ปดํŒŒ์ผํ•˜์˜€๋‹ค.

$ vi init/main.c  # "start_kernel()" ํ•จ์ˆ˜์—์„œ
                  # "hello from My Linux2"๋ฅผ ์ถœ๋ ฅํ•˜๋„๋ก ์ˆ˜์ •
$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage2
$ reboot

๋ถ€ํŒ… ํ™”๋ฉด์—์„œ โ€œMy Linux2โ€๊ฐ€ ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์šฐ์„  โ€œMy Linuxโ€๋กœ ๋จผ์ € ๋ถ€ํŒ…ํ•˜์—ฌ ๋ถ€ํŒ… ๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•ด๋ณธ๋‹ค.

$ dmesg > x
$ vi x

start_kernel ํ•จ์ˆ˜์— ์ถ”๊ฐ€ํ•œ ๋ฉ”์„ธ์ง€๊ฐ€ ์ •์ƒ ์ถœ๋ ฅ๋œ๋‹ค.

โ€œMy Linux2โ€๋กœ ์žฌ๋ถ€ํŒ…ํ•˜์—ฌ ๋ถ€ํŒ…๋ฉ”์„ธ์ง€๋ฅผ ํ™•์ธํ•ด๋ดค์„ ๋•Œ๋„, ์ถ”๊ฐ€ํ•œ โ€œhello from My Linux2โ€๋ผ๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์ •์ƒ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

5) Where is CPU at the end of the boot sequence when it prints โ€œloginโ€ and waits for the user login? Explain your reasoning.

main.c์—์„œ ๋ถ€ํŒ… ์‹œ ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜๋Š” start_kernel์˜ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์„ ๋ณด๋ฉด rest_init();์ด ์žˆ๋‹ค.

rest_init() ํ•จ์ˆ˜ ๋˜ํ•œ init/main.c์— ์ •์˜๋˜์–ด ์žˆ๋‹ค. rest_init() ํ•จ์ˆ˜ ์•ˆ์—์„œ๋Š” cpu_idle();์ด ์ œ์ผ ๋งˆ์ง€๋ง‰์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.

$ grep -nr "cpu_idle" * | more

์˜ ์‹คํ–‰๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด

cpu_idle ํ•จ์ˆ˜๋Š” arch/x86/kernel/process_32.c์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

ํ•จ์ˆ˜ ๋‚ด์šฉ์„ ํ™•์ธํ•ด๋ณด๋‹ˆ, cpu๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ id๋ฅผ ์ž…๋ ฅ๋ฐ›๊ธฐ ์ „๊นŒ์ง€ while๋ฌธ์„ ํ†ตํ•ด์„œ ๋ฌดํ•œ๋ฃจํ”„๋ฅผ ๋Œ๊ฒŒ ๋œ๋‹ค.

์ฆ‰, cpu๋Š” login:๋ฅผ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ ํ›„ ๋ฉˆ์ถฐ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ cpu_idle์˜ ๋ฌดํ•œ๋ฃจํ”„์—์„œ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ์ „๊นŒ์ง€ ๊ณ„์† ๋Œ€๊ธฐ ์ค‘์ธ ๊ฒƒ์ด๋‹ค.