초기에 컴퓨터를 부트스트랩(bootstrapping) 한다고 하면 부트 프로그램이 포함된 종이 테이프를 공급하거나 프론트 패널 address/data/control 스위치를 사용하여 부트 프로그램을 직접 로딩하는 것을 의미했다. 오늘날 컴퓨터에는 부팅 과정을 단순화시키는 장치들이 장착되어 있지만 꼭 그렇게 단순한 것 같지는 않다.
리눅스 부팅 과정을 보다 높은 시각에서 조망해야지만 전체적으로 볼 수 있다. 그런 다음 각각의 단계를 자세히 살펴봐야겠다. 곳곳에 첨부한 소스 자료가 커널 트리를 연구하는데 도움이 될 것이다.
그림 1은 리눅스 부트 프로세스 개요도이다.
시스템이 처음 부팅되거나 리셋되면 프로세서는 잘 알려진 위치에 코드를 실행한다. PC의 경우, basic input/output system (BIOS)이다. 이것은 마더보드의 플래시 메모리에 저장된다. 임베디드 시스템에 있는 CPU는 리셋 벡터를 호출하여 플래시/ROM의 알려진 주소에서 프로그램을 시작한다. 두 경우 모두 결과는 같다. PC가 훨씬 유연하고, BIOS는 어떤 장치들이 부팅 후보인지를 결정해야 한다.
부팅 장치를 찾으면 첫 번째 단계 부트 로더는 RAM으로 로딩되어 실행된다. 이 부트 로더는 길이는 512 바이트 미만이고 하는 일은 두 번째 단계의 부트 로더를 로딩하는 것이다.
제 2 단계 부트 로더가 RAM에 있고 실행되면 스플래시 스크린이 디스플레이 되고 리눅스와 초기 RAM 디스크 옵션(임시 루트 파일 시스템)이 메모리에 로딩된다. 이미지가 로딩될 때 2 단계 부트 로더는 제어를 커널 이미지로 전달하고 커널은 압축이 해제되어 초기화 된다. 이 때 2 단계 부트 로더는 시스템 하드웨어를 검사하고 첨부된 하드웨어 장치들을 열거하고, 루트 장치를 마운트 하고 필요한 커널 모듈을 로딩한다. 완료되면 첫 번째 사용자-공간 프로그램(init
)이 시작되고 고급 시스템 초기화가 이루어진다.
여기까지가 리눅스 부팅 과정을 간략하게 묘사한 것이다. 이제 리눅스 부팅 과정을 보다 자세하게 살펴보도록 하자.
시스템 시작 단계는 리눅스가 부팅되는 하드웨어에 기반하고 있다. 임베디드 플랫폼에서, 부트스트랩 환경은 시스템이 켜지거나 리셋될 때 사용된다. U-Boot, RedBoot, MicroMonitor 등이 그 예이다. 임베디드 플랫폼에는 일반적으로 부트 모니터가 장착된다. 이 프로그램들은 목표 하드웨어의 플래시 메모리의 특별한 영역에 있고 리눅스 커널 이미지를 플래시 메모리로 다운로드 하는 방식을 제공하고 이를 실행한다. 리눅스 이미지를 저장하고 부팅하는 기능 외에도 이 부트 모니터는 시스템 테스트와 하드웨어 초기화를 수행한다. 임베디드 환경에서 이러한 부트 모니터들은 제 1 부트 로더와 제 2 부트 로더를 관장한다.
BIOS 기능에 따라 사용처도 다르며, BIOS는 두 부분으로 구성된다. POST 코드와 런타임 서비스이다. POST가 완료된 후에 이것은 메모리에서 플러시(flush)되지만 BIOS 런타임 서비스는 그대로 남아있고 해당 운영 체계에서 사용할 수 있다.
운영 체계를 부팅하기 위해서 BIOS 런타임은 complementary metal oxide semiconductor (CMOS)에서 정의된 선호도 순으로 활성화 되고 부팅 가능한 장치를 찾는다. 부트 장치는 플로피 디스크, CD-ROM, 하드 디스크 파티션, 네트워크 상의 장치, USB 플래시 메모리 스틱이 될 수도 있다.
일반적으로 리눅스는 하드 디스크에서 부팅되고, Master Boot Record (MBR)에는 주 부트 로더가 포함되어 있다. MBR은 512-바이트 섹터이고 디스크의 첫 번째 섹터(sector 1 of cylinder 0, head 0)에 위치해 있다. MBR이 RAM이 로딩된 후에 BIOS는 제어권을 여기에 넘겨준다.
MBR에 있는 주 부트 로더는 512-바이트 이미지로서 프로그램 코드와 작은 파티션 테이블이 포함되어 있다. (그림 2) 첫 번째 446 바이트는 주 부트 로더이고 여기에는 실행 코드와 에러 메시지 텍스트가 포함된다. 그 다음 64 바이트는 파티션 테이블인데, 네 개의 파티션(각 16 바이트)의 레코드가 포함되어 있다. MBR은 매직 넘버(0xAA55)로 정의된 2 바이트로 끝난다. 매직 넘버는 MBR의 밸리데이션에 사용된다.
주 부트 로더의 작업은 2차 부트 로더(stage 2)를 찾아 로딩하는 것이다. 액티브 파티션을 찾기 위해 파티션 테이블을 검사한다. 액티브 파티션을 찾으면 테이블에 남아있는 파티션들을 검사하여 이들이 모두 비활성 상태인지를 확인한다. 확인이 되면 액티브 파티션의 부트 레코드는 장치에서 RAM으로 읽혀지고 실행된다.
2차 또는 제 2 단계 부트 로더는 커널 로더라고 불린다. 이 단계에서의 작업은 리눅스 커널과 선택적인 초기 RAM 디스크를 로딩하는 것이다.
GRUB의 좋은 점은 리눅스 파일 시스템을 알고 있다는 점이다. LILO 처럼 디스크 상의 미가공 섹터를 사용하는 대신 GRUB은 ext2 또는 ext3 파일 시스템에서 리눅스 커널을 로딩할 수 있다. 2 단계 부트 로더를 3 단계 부트 로더로 만든다. Stage 1 (MBR)은 리눅스 커널 이미지를 포함하고 있는 특정 파일 시스템을 알고 있는 stage 1.5 부트 로더를 부팅한다. reiserfs_stage1_5
(Reiser 저널링 파일 시스템에서 로딩함) 또는 e2fs_stage1_5
(ext2 또는 ext3 파일 시스템에서 로딩함)가 좋은 예이다. stage 1.5 부트 로더가 로딩 및 실행되면 stage 2 부트 로더가 로딩될 수 있다.
stage 2가 로딩되면서 GRUB은 사용 할 수 있는 커널 리스트를 디스플레이 한다. (/etc/grub.conf
에서 정의됨. /etc/grub/menu.lst
와 /etc/grub.conf
의 링크 포함). 여러분은 커널을 선택하고 추가 커널 매개변수로 이를 수정할 수도 있다. 부트 프로세스에 보다 직접적인 관여를 하려면 명령행 쉘을 사용할 수도 있다.
Stage 2 부트 로더가 메모리에 있는 상태에서 파일 시스템이 참조되고 디폴트 커널 이미지와 initrd
이미지가 메모리로 로딩된다. 이미지가 준비되면 stage 2 부트 로더는 커널 이미지를 호출한다.
bzImage (i386 이미지 용)가 호출되면 start 어셈블리 루틴(그림 3의 주 흐름도 참조)의 ./arch/i386/boot/head.S
에서 시작
한다. 이 루틴은 기본적인 하드웨어 설정을 수행하고 ./arch/i386/boot/compressed/head.S
에 있는 startup_32
루틴을 호출한다. 이 루틴은 기본 환경(스택 같은)을 설정하고 Block Started by Symbol (BSS)을 청소한다. 커널은 decompress_kernel
(./arch/i386/boot/compressed/misc.c
에 있음)이라고 하는 C 함수를 호출하여 압축이 풀린다.
새로운 startup_32
함수(swapper 또는 process 0)에서, 페이지 테이블이 초기화되고 메모리 페이징이 실행된다. CPU 유형이 검사되고 아울러 FPU (floating-point unit)도 검사된다. start_kernel
함수가 호출되면(init/main.c
) 일반(non-architecture specific) 리눅스 커널이 된다.
start_kernel
을 호출하면 초기화 함수의 긴 리스트가 호출되어 인터럽트를 설정하고 보다 구체적인 메모리 설정이 이루어지고 초기 RAM 디스크를 로딩한다. kernel_thread
(arch/i386/kernel/process.c
)를 호출하면 init
함수가 시작되는데 이는 최초의 사용자 공간 프로세스이다. 마지막으로 유휴 태스크가 시작되고 스케줄러가 주도권을 잡는다. (cpu_idle
호출 후). 인터럽트가 실행되면서 선점 스케줄러는 주기적으로 주도권을 잡아 멀티태스킹을 수행한다.
커널이 부팅되는 동안 stage 2 부트 로더에 의해 메모리로 로딩된 initial-RAM disk (initrd
)가 RAM으로 복사 및 마운트 된다. initrd
는 RAM에서 임시 루트 파일 시스템의 역할을 하고 커널이 물리적 디스크를 마운트 하지 않고도 완전히 부팅될 수 있도록 한다. 주변 기기와 인터페이싱 할 때 필요한 모듈이 initrd
의 일부가 될 수 있기 때문에 커널은 매우 작아질 수 있지만 그래도 여전히 많은 하드웨어 설정들을 지원한다. 커널이 부팅된 후에 루트 파일 시스템이 (pivot_root
를 통해) 회전되고 이곳에서 initrd
루트 파일 시스템이 언마운트(unmount)되어 실제 루트 파일 시스템이 마운트 된다.
커널이 부팅 및 초기화 된 후에 커널은 최초의 사용자 공간(user-space) 애플리케이션을 시작한다. 이것은 표준 C 라이브러리로 컴파일 된 첫 번째 프로그램이다. 이전에는 어떤 표준의 C 애플리케이션도 실행되지 않았다.
데스크탑 리눅스 시스템에서 시작된 첫 번째 애플리케이션은 일반적으로 /sbin/init
이다. 하지만 꼭 이럴 필요는 없다. 임베디드 시스템은 init
(/etc/inittab
을 통해 설정됨)에서 제공하는 초기화를 거의 필요로 하지 않는다. 많은 경우에, 필요한 임베디드 애플리케이션을 시작하는 간단한 쉘 스크립트를 호출하곤 한다.
리눅스가 그러하듯, 리눅스 부트 프로세스도 매우 유연하고 많은 프로세서와 하드웨어 플랫폼을 지원한다. 초기에는 loadlin 부트 로더가 있었다. LILO 부트 로더는 부팅 기능을 확장했지만 파일 시스템 인식 부분에서 부족했다. GRUB과 같은 최신 부트 로더가 생기면서 많은 파일 시스템(Minix 부터 Reiser 까지)에서 리눅스를 부팅할 수 있게 되었다.
Note
- IBM DeveloperWorks -
http://www.ibm.com/developerworks/kr/library/l-linuxboot/
[u-boot] u-boot의 i2c command 사용에 대한 문서 (0) | 2011.03.10 |
---|