본문 바로가기

O/S/Linux-CentOS

리눅스 부트 시퀀스

다음은 리눅스 부팅 과정을 요약합니다. 

 

이 글을 읽기 전에 앞서 PC 부팅 시퀀스(http://hermet.pe.kr/102451404)를 먼저 참고하시면 도움이 될 겁니다.

 

기본적인 BIOS를 통해 부팅 준비가 끝나면  하드디스크의 첫 번째 섹터, MBR(Master Boot Record)을 통해 실제 운영체제가 있는 섹터를 읽어들이게 됩니다. (MBR에 대한 자세한 정보는 임베디드 개발자를 위한 파일시스템의 원리와 실습, 한빛미디어 58p-64p 참고 바람)

 

리눅스는 80x86 시스템 부트로더로 LILO(LInux LOader) 또는 GRUB(Grand Unified Bootloader )를 사용하며 유연한 부팅이 가능한데, LILO는 다음과 같은 작업을 수행합니다.  (참고로 BIOS는 부트 로더를 램의 0X00007c00 주소로 읽어 들이며 이 프로그램은 자신을 0x00096a00으로 옮긴 후, 리얼 모드 스택(0x00098000 ~ 0x000969ff)을 설정하고 LILO  부트 로더의 나머지 부분은 0x00096c00 ~ ... 로  읽어 들임.)

 

1. BIOS 함수를 호출하여 "Loading" 메세지 출력

 

2. BIOS 함수를 호출하여 디스크에서 커널 이미지의 초기 영역을 로딩. 커널 이미지의 첫번째 512바이트는 램 0x0009000에 두고, setup() 함수 코드를 로딩하여 램의 0x00090200에 저장. 이는 부트 로더 바로 뒤의 위치이며 링커(Linker)가 이 작업을 수행함

 

3. BIOS 함수를 호출하여 디스크에서 나머지 커널 이미지를 로딩. 낮은 주소로 로딩할 경우 0x00010000(make zImage로 컴파일 하면 작은 커널 이미지), 높은 주소로 로딩할 경우 0x00100000에 저장(make bzImage로 컴파일하면 큰 커널 이미지)

 

4.setup() 코드로 이동 후 수행.

 

 

 

setup은 호환성과 안전성을 높이기 위해 자신의 방식대로 하드웨어 장치를 다시 초기화 합니다.

 

1. 고급 구성 및 전력 인터페이스, ACPI(Advanced Configuration & Power Interface)가 적용된 시스템에서는 시스템의 물리 메모리의 배치를 나타내는 램 테이블을 구축하기 위해 BIOS 루틴을 호출 (커널 부팅 메세지에 "IOS-e820").

 

2. 키보드 반복 간격과 속도 설정.

 

3. 비디오 카드 초기화.

 

4. 디스크 컨트롤러를 다시 초기화 하고 하드 디스크 매개 변수 파악.

 

5. IBM 마이크로 채널 버스(MCA, Micro Channel Bus) 검사.

 

6. PS/2 위치 지정 장치 검사 .

 

7. 고급 전원 관리(Advanced Power Managerment)의  BIOS 지원 여부 검사.

 

8. BIOS가 향상된 디스크 드라이버 서비스(EDD)를 지원하면 적당한 BIOS 함수를 호출하여 시스템에서 이용 가능한 하드 디스크를 나타내는 테이블을 램에 만듬.

 

9. 커널 이미지를 램의 낮은 주소로 로딩했을 경우 이를 물리 주소 0x00001000로 이동. (이유인 즉, 커널 이미지를 플로피 디스크에 저장할 수 있게 하고, 부팅할 때 걸리는 시간을 줄이고 압축된 커널 이미지인 경우 커널 이미지 뒤에 임시버퍼에 압축을 푸는 루틴을 두기 위해)

 

10. 8042 키보드 컨트롤러에 있는 A20 핀을 설정. (A20 핀은 오래된 8088 마이크로프로세서의 물리 주소와 호환되도록 하기 위한 것)

 

11. 임시로 IDT(Interrupt Description Table), GDT(Global Descriptor Table)을 설정

 

12. 부동 소수점 유닛(Floating Point Unit)을 초기화

 

13. 프로그래밍 가능한 타이며(PIC)를 다시 프로그래밍 하여 모든 인터럽트를 마스크. IRQ2는 제외.

 

14. CPU의 cr0 상태 레지스터 PE비트를 설정하여 CPU를 리얼 모드에서 보호 모드로 전환. (Windows 구조와 원리, 한빛미디어, 36 ~ 37 참고)

 

15. startup_32() 어셈블리어 함수로 정프! startup_32는 setup이 종료할 당시 0x00100000 똔느 0x00001000으로 옮겨져 있음.

 

 

 

startup_32 함수는 두 가지가 있으며 각각 압축 전과 후의 커널에 대한 처리를 합니다.

 

우선 압축 전 (arch/i386/boot/compressed/head.S) 함수.

 

1. 세그먼테이션 레지스터와 임스 스택을 초기화.

 

2. eflags 레지스터 내의 모든 비트를 클리어.

 

3. _edata ~ _end 심벌 사이에 있는 커널의 초기화되지 않는 데이터 영역을 0으로 설정.

 

4. decompress_kernel()를 호출하여 커널 이미지 압축 해제. (이 때, "Uncompressing Linux..." 메세지 갱신. 압축 후엔 "OK, booting the kernel.") 커널 이미지가 낮은 주소에 있으면 압축 풀린 커널을 0x00100000로 이동. 높은 주소인 경우 커널 이미지 뒤 임시 버퍼에 압축 해제 후 0x00100000로 이동.

 

 

 

압축 후 (arch/i386/kernel/head.S) .

 

1. 세그먼테이션 레지스터를 최종 값으로 초기화.

 

2. 커널의 bss 세그먼트를 0으로 설정.

 

3. swapper_pg_dir에 있는 임시 커널 페이지 테이블 초기화, ps0의 선형 주소가 같은 물리 주소를 매핑하도록 초기화

 

4. cr3 레지스터를 페이지 전역 디렉토리의 주소로 설정 후, cr0 레지스터의 PG 비트를 1로 설정하여 페이징 활성화.

 

5. 프로세스 0을 위한 커널 모드 스택을 설정.

 

6. eflags 내의 모든 비트 클리어

 

7. setup_idt() 호출하여 IDT를 NULL 인터럽트 핸들러로 채움.

 

8. BIOS에서 얻은 시스템 매개 변수와 운영체제에 전달할 매개 변수를 첫 번째 페이지 프레임에 저장.

 

9. 프로세서 모델 확인.

 

10. gdtr과 idtr 레지스터를 각각 GDT와 IDT 테이블 주소로 설정.

 

11. start_kernel() 호출!

 

 

 

start_kernel은 리눅스 커널의 거의 모든 구성을 초기화하며 여기서는 대표적으로 다음과 같은 일을 합니다. 

("Linux version 2.6.11..." 이 후의 메세지 출력.)

 

 sched_init() - 스케줄러 초기화

 

 build_all_zonelists() - 메모리 지역 초기화

 

 page_alloc_init(), mem_init() - 버디 시스템 할당자 초기화

 

 trap_init(), init_IRQ() - IDT를 최종적으로 초기화

 

 softirq_init() - TASKLET_SOFTIRQ와 HI_SOFTIRQ를 초기화

 

 time_init() - 시스템 날짜 및 시간 초기화

 

 kmem_cache_init() - 슬랩 할당자 초기화

 

 calibrate_delay() - CPU 클록 속도 결정

 

 kernel_thread() - 프로세스 1에 해당하는 커널 스레드 생성

 

 

 

- 참고 : 리눅스 커널의 이해 3rd edition, 한빛 미디어, 827 ~ 831 -