리눅스 시작 프로세스

리눅스 시작 프로세스는 리눅스 부팅 시에 수행되는 다단계 초기화 과정이다. 이것은 리눅스가 파생된 BSD 및 다른 유닉스 계열 부팅 프로세스와 비슷하다.

리눅스를 부팅하는 것은 펌웨어 초기화, 부트 로더의 실행, 리눅스 커널 이미지의 로딩과 시작 그리고 다양한 시작 스크립트들과 데몬들의 실행을 포함하는 여러 단계와 여러 소프트웨어 구성 요소들과 관련된다. 이 단계들 각각에서는 다른 방식과 접근법이 존재한다; 예를 들면 GRUB, LILO, SYSLINUX 또는 Loadlin는 부트 로더로서 사용될 수 있지만 시작 스크립트들도 전통적인 init 스타일 또는 (systemd나 Upstart 같은 현대의 대체재들을 통해 수행될 수 있는) 시스템 설정으로도 가능하다.

개요 
리눅스 시작 프로세스의 초기 단계들은 컴퓨터 구조에 매우 의존적이다. IBM PC 호환의 하드웨어가 리눅스가 일반적으로 사용되는 가장 흔한 구조이다; 이러한 시스템들에서 바이오스가 중요한 역할을 하는데, 다른 시스템들에서도 똑같은 방식이 사용되는 것은 아니다. 다음의 예시에서 IBM PC 호환 하드웨어는:
 바이오스는 실제 하드웨어 플랫폼 특유의 시작 태스크를 수행한다. 하드웨어가 열거되고 부트에 필요한 하드웨어가 정확히 초기화되면 바이오스는 설정된 부트 장치에서 부트 코드를 로드하고 실행한다.
 부트 로더는 종종 사용자에게 가능한 부트 옵션의 메뉴를 보여주기도 하며 선택하지 않은 채 일정 시간 이상 지나면 자동으로 선택되는 기본 옵션을 갖고 있다. 선택이 되면 부트 로더는 커널을 메모리로 로드하고, 몇몇 파라미터들을 제공하며 제어를 넘겨준다.
 커널은 자체적으로 압축을 해제하고 필수 하드웨어와 메모리 페이징 같은 시스템 함수들을 설치하며 시스템 설치의 대부분을 수행하는 start_kernel()를 호출한다(인터럽트, 나머지의 메모리 관리, 장치와 드라이버 초기화 등). 그 후 idle 프로세스, 스케줄러 그리고 사용자 공간에서 실행되는 init 프로세스를 각각 시작한다.
 init은 셸에 의해 실행되는 스크립트들(sysv, bsd, runit) 또는 바이너리 구성 요소로 실행되는 설정 파일(systemd, upstart)로 이루어진다. init은 특정한 레벨(sysv, bsd) 또는 대상(systemd)을 갖는데 각각은 서비스들(daemons)의 특정한 집합으로 이루어져 있다. 이것들은 다양한 비 운영 체제 서비스들과 구조들 그리고 사용자 환경 구성을 제공한다. 일반적인 서버 환경은 웹 서버, 데이터베이스 서비스 그리고 네트워킹을 시작한다.
 일반적인 데스크탑 환경은 디스플레이 매니저라고 불리는 데몬을 실행하며, 이것은 기본적인 그래픽 스택과 로그인 매니저를 제공하는 그래픽 환경을 시작한다. 사용자가 비밀번호를 입력하고 나면 세션 매니저가 세션을 시작한다. 세션은 완전한 데스크탑 환경을 구성할 수 있는 UI 요소들 같은 프로그램들의 집합이다.
종료 시에, 모든 init은 모든 사용자 공간 기능을 종료하기 위해 호출된다. init은 그 후 종료되며 커널은 자신의 종료를 수행한다.

부트 로더 단계 
부트 로더 단계는 컴퓨터 구조에 따라 다르다. 초기 단계들은 운영 체제에 관련 없었기 때문에, 바이오스 기반 부트 과정은 마스터 부트 레코드 코드가 리얼 모드에서 실행되고 첫 번째 단계 부트 로더가 로드되었을 때 실행될 것이라고 여겨진다. UEFI 시스템에서 리눅스 커널 같은 페이로드는 직접적으로 실행된다. 그래서 부트 로더가 필요치 않다. 아래는 몇몇 유명한 부트 로더들에 대한 개략적인 설명이다.
 LILO는 파일 시스템 레이아웃을 이해하지 않는다. 대신 설정 파일 (/etc/lilo.conf)이 현재 시스템에 생성되는데, 이것은 커널과 램 디스크(initrd 또는 initramfs)의 위치에 관한 raw 오프셋 정보를 매핑한다. 부트 파티션과 커널 경로명 같은 데이터 그리고 필요한 경우 커스터마이징된 옵션들을 포함하는 설정 파일은 MBR 부트섹터에 부트로더 코드와 함께 쓰여진다. 이 부트섹터가 읽어지고 바이오스에 의해 제어가 넘어오면, LILO는 메뉴 코드를 로드하고 그 후 리눅스 커널을 계산하고 로드하기 위해 사용자 입력과 함께 저장된 값들을 함께 사용한다.
 GRUB 1은 런타임 시에 자신의 설정 파일에 접근하기 위해서 common 파일 시스템을 읽기 위한 로직을 포함한다. 이것은 GRUB 1에게 파일 시스템에서 자신의 설정 파일을 읽을 수 있는 능력을 주는데, 런타임 시에 설정을 바꾸고 오프셋에 의존하지 않는 사람이 읽을 수 있는 포맷으로 디스크와 파티션을 명시할 수 있게 한다. 또한 명령 줄 인터페이스를 포함하는데, 이것은 잘못 설정될 시에 GRUB을 수정하는 것을 더 쉽게 만들어 준다.
 GRUB 2는 GRUB 1과 다른 점이 다양한 운영 체제들과 자동화된 설정들의 자동화된 탐지가 가능하며 두 단계를 가진다는 것이다. 첫 단계 로더(stage1)는 MBR의 바이오스에 의해서 또는 파티션 부트 섹터의 다른 부트 로더에 의해서 로드되고 실행된다. 이것이 하는 일은 설정이 추후에 읽어질 수 있는 다양한 파일 시스템들을 발견하고 접근하는 것이다. 옵션으로 중간 단계 로더(stage1.5)는 두 번째 단계 로더가 인접하지 않거나 파일 시스템 또는 하드웨어가 두번째 단계 로더에 접근하기 위해 특별한 처리를 요구하는 경우에 로드되고 실행된다. 두 번째 단계 로더(stage2)는 마지막에 로드되며 GRUB 시작 메뉴를 표시한다. 메뉴가 선택되고 추가적인 인자가 주어지면 GRUB은 커널을 메모리에 로드하고 제어를 넘긴다. GRUB 2는 또한 다른 부트 로더의 체임 로딩이 가능하다.
 SYSLINUX/ISOLINUX는 FAT 파일 시스템에서 전체 리눅스 설치를 부팅하는데 특화된 부트 로더이다. 이것은 종종 플로피 디스크, 라이브 USB 그리고 다른 경량 부트 시스템을 부트하는데 사용된다. ISOLINUX은 일반적으로 리눅스 라이브 CD와 부팅 가능한 설치 CD들에서 사용된다.
 Loadlin은 런타임 시에 실행 중인 도스 또는 윈도우 9x 커널을 리눅스 커널로 대체할 수 있는 부트 로더이다. 이 부팅 방식은 리눅스가 다양한 하드웨어 장치들을 위한 드라이버를 갖기 때문에 현재는 그다지 사용되지 않지만 모바일 장치에서는 아직 볼 수 있다.

커널 국면 
리눅스에서 커널은 메모리 관리, 태스크 스케줄링, 입출력, 프로세스 간 통신 그리고 전체적인 시스템 제어 같은 모든 운영 체제 프로세스들을 다룬다. 이것은 두 단계로 로드되는데 첫 단계에서 커널은 (압축된 이미지 파일로서) 메모리에 로드되며 압축 해제되고 몇몇의 중요한 함수들이 설치된다. 제어는 이후 주요 커널 시작 프로세스로 넘어간다. 커널이 완전하게 동작 가능해지면 커널은 실행 시킬 init 프로세스(각각 요구되는 사용자 환경과 로그인을 위한 사용자 공간과 프로세스들을 설치하는)를 찾는다. 커널 자체는 이후 idle 상태로 가며 다른 프로세스의 호출을 기다린다.

커널 로딩 단계 
로드된 커널은 일반적으로 이미지 파일이며 zImage 또는 bzImage 포맷으로 압축되어 있다. 헤드 부분의 루틴은 최소한의 하드웨어 설치, 이미지를 완전하게 메모리로 압축 해제 그리고 설정된 경우 램 디스크를 알아 놓는다. 그 후 ./arch/i386/boot/head 와 startup_32 () (x86 기반 프로세서의 경우) 프로세스를 통해 커널 스타트업을 시작한다.

커널 스타트업 단계 
 Source: IBM description of Linux boot process
커널에서의 스타트업 함수는 메모리 관리(페이징 테이블과 메모리 페이징)를 수립하고, CPU의 종류와 부동소수점 가용성 같은 추가적인 기능을 탐지하며 이후 start_kernel()를 호출함으로써 아키텍처에 명시적이지 않은 리눅스 커널 기능으로 전환된다.

start_kernel은 넓은 범위의 초기화 함수들을 실행한다. 이것은 인터럽트 핸들링(IRQs)을 설치하고, 나아가 메모리를 설정하며, Init 프로세스(첫번째 사용자 영역 프로세스)를 시작한다. 이후 cpu_idle()를 통해 유휴 태스크를 시작한다. 특히 커널 스타트업 프로세스는 또한 부트 단계에서 임시 루트 파일 시스템으로써 이전에 로드된 초기 램 디스크("initrd")를 마운트한다. initrd는 드라이버 모듈들이 다른 장치들(예를 들면 하드 디스크) 그리고 드라이버들과의 의존성 없이 메모리에서 직접적으로 로드되게 한다. 이 몇몇 드라이버들의 분리는 통계적으로 커널과 initrd에서 로드된 다른 드라이버들로 컴파일된다. 루트 파일 시스템은 이후 임시 루트 파일 시스템을 언마운트하고 이것을 실제의 것으로 대체하는 pivot_root() 에 대한 호출을 통해서 전환된다. 임시 루트 파일 시스템에 의해서 사용되는 메모리는 그 때 되찾아 진다.

커널은 장치들을 초기화하고 부트 로더에 의해서 읽기 전용으로 명시된 루트 파일 시스템을 마운트하며, 시스템에 의해 실행되는 첫 번째 프로세스로 디자인된 Init (/sbin/init)을 실행한다(PID=1). 파일 시스템을 마운팅하는 과정 중에서 커널에 의해서 그리고 Init 과정 중 Init에 의해 메시지가 보여진다. 또한 옵션으로 Initrd를 실행해서 설치와 장치 관련 사항들이 루트 파일 시스템이 마운트되기 전에 처리될 수 있게 한다.

레드햇에 따르면, 이 단계에서 자세한 커널 과정은 다음과 같이 요약될 수 있다:
 "커널이 로드되면, 즉시 컴퓨터의 메모리를 초기화하고 설정하며 시스템에 부착된 다양한 하드웨어를 설정하는데 여기에는 모든 프로세서들, I/O 서브시스템들 그리고 저장 장치들이 포함된다. 이후 메모리의 미리 결정된 위치에서 압축된 initrd 이미지를 찾은 후에 압축을 해제하고 마운트하며 필요한 모든 드라이버들을 로드한다. 다음으로 initrd 디스크 이미지를 언마운팅하고 디스크 이미지가 가져간 모든 메모리를 풀어주며, LVM 또는 소프트웨어 RAID 같은 파일 시스템과 관련된 가상 장치들을 초기화한다. 커널은 이후 루트 디바이스를 생성하고 루트 파티션을 읽기 전용으로 마운트하고 사용되지 않은 메모리를 풀어준다. 이 지점에서 커널은 메모리에 로드되고 동작 가능해 진다. 그러나 의미 있는 입력을 시스템에 허용하는 사용자 애플리케이션이 존재하지 않기 때문에 많은 일을 할 수는 없다. 이 지점에서 인터럽트가 활성화되며 스케줄러는 선점적인 멀티 태스킹을 제공하기 위해서 시스템의 전체적인 관리의 제어를 가지며, init 프로세스는 사용자 공간에서 사용자 환경을 계속 부팅하기 위해 남아 있는다.

초기 사용자 공간 
initramfs, 또한 초기 사용자 공간으로도 불리는 이것은 리눅스 커널 버전 2.5.46 이후로 사용 가능하며, 스타트업 과정 동안 커널이 수행되기 이전에 가능한 많은 함수들을 대체하기 위한 목적으로 사용된다. 초기 사용자 공간의 일반적인 사용은 주요 사용자 공간 파일 시스템을 로드하기 위해 어떤 장치 드라이버들이 필요한지를 탐지하고 임시 파일 시스템에서 이것들을 로드하는 것이다.

Init 프로세스

SysV init 
Init이 하는 일은 커널이 완전히 실행되기 시작할 때 "그렇게 되어야 하는 모든 것을 얻는" 것이다. 근본적으로 이것은 전체 사용자 공간을 수립하고 가동한다. 이것은 파일 시스템을 검사하고 마운트하며 필요한 사용자 서비스를 시작하고 궁극적으로 시스템 스타트업이 완료될 때 사용자 환경으로 전환하는 것을 포함한다. 이것은 유닉스와 BSD의 init 과정들과 비슷하지만 몇몇 차이점이 존재한다. 표준 리눅스 시스템에서 Init은 런레벨로 알려진(0부터 6의 값을 갖는) 파라미터와 함께 실행되며 어떤 서브시스템이 동작할 지를 결정한다. 각 런레벨은 자신만의 스크립트를 가지며 주어진 런레벨에서 해야할  일이 적어져 있다. Init 스크립트들은 일반적으로  "/etc/rc..."와 같은 이름을 갖는 디렉토리에 존재한다. init의 상위 수준 설정 파일은 /etc/inittab에 존재한다.

시스템 부트 동안 디폴트 런레벨이 /etc/inittab에 명시되어 있는지를 검사하고 존재하지 않는다면 시스템 콘솔을 통해서 런레벨을 요청한다. 이후 주어진 런레벨 하에서 관련된 부트 스크립트들을 진행하고(모듈들의 로딩, 루트 파일 시스템의 무결성 검사 등) 완전 읽기-쓰기 접근으로 리마운팅하며 네트워크를 설정한다.

명시된 모든 과정들이 진행된 후에, init은 휴면 상태에 진입하고 세 이벤트 중 하나가 일어나기를 기다린다: 종료를 위한 과정의 시작, 정전 신호, 런레벨을 바꾸기 위한 /sbin/telinit을 통한 요청.

이것은 SysV-스타일 init에서 적용된다.

systemd 
Systemd의 개발자들은 유닉스 시스템 V와 BSD 운영 체제에서 나온 리눅스 init 시스템을 대체하려는 것에 초점을 두었다. init과 비슷하게 systemd는 다른 데몬들을 관리하는 데몬이다. Systemd를 포함한 모든 데몬들은 백그라운드 프로세스들이다. Systemd는 부팅 시에 시작되는 첫번째 데몬이며 셧다운 시에 종료되는 마지막 데몬이다.

Systemd를 초기에 개발한 소프트웨어 엔지니어들은 여러 방식들에서 init 데몬의 효율성을 능가하려고 했다. 이들은 시스템 부팅 동안에 과정들이 더 동시에 또는 병렬로 진행되게 해서 소프트웨어 프레임워크를 향상시키고 셸의 오버헤드를 감소시키려고 했다.

각 데몬들을 위한 systemd의 초기화 명령어들은 셸 스크립트가 아닌 설정 파일에 저장된다. 프로세스 간 통신을 위해서, systemd는 유닉스 도메인 소켓과 D-Bus를 동작하는 데몬들에게 사용 가능케 했다. Systemd는 또한 적극적인 병렬화가 가능하다.

Upstart 
전통적인 init 과정은 근본적으로 단지 전력이 공급된 이후에 컴퓨터를 정상적인 실행 상태로 되돌리고 셧다운 이전에 서비스들을 정상적으로 종료시키는 것을 다룬다. 결과적으로 설계는 엄격하게 동기적이며 현재 작업이 완료되기 전까지는 추후의 작업을 막는다. 이 작업들은 반드시 이전에 정의되어야 한다. 이것은 현대의 데스크탑 컴퓨터에서 다양한 비 스타트업 작업들을 다루지 못하게 한다.

Upstart는 비동기적으로 동작한다; 이것은 부팅 시에 작업들과 서비스들의 시작 그리고 종료 시에 이것들의 정지를 다루며 또한 시스템이 돌아가는 동안 이 작업들을 감시한다.

Sysvinit을 통한 쉬운 이행과 완벽한 하위 호환성이 명백한 설계의 목표이다; 그에 따라 Upstart는 수정되지 않은 sysvinit을 실행할 수 있다. 이 방식으로 이것은 적절한 실행을 위해 일반적으로 완전한 이행을 가정하고 요구하는 그리고 과거와 현재의 스타트업 방식들이 혼합된 환경을 지원하지 않는 대부분의 다른 init 대체재들과는 다르다.

Upstart는 확장으로 inictl, 싱글 이벤트 또는 이벤트 브릿지(많은 복잡한 이벤트들을 통합하기 위한)들의 사용을 통해 자신의 이벤트 모델을 가능케 한다. 디폴트로 Upstart는 소켓, dbus, udev, file 그리고 dconf 이벤트들의 브릿지들을 포함한다.

runit 
Runit은 유닉스 계열 운영 체제들을 위한 init 구조로서 운영 체제를 통해 초기화, 감독 그리고 프로세스들의 종료를 담당한다. 이것은 리눅스, 맥, BSD 등에서 돌아가는 "seminal" 데몬툴즈 프로세스 감독 툴킷의 "재구현"이다. Runit은 운영 체제의 부트 시간을 줄여줄 수 있는 시스템 서비스의 스타트업의 병렬성을 특징으로 갖는다.

Runit은 init 데몬으로서 모든 다른 프로세스들의 직접적인 또는 간접적인 조상이다. 이것은 부팅 시에 시작되는 첫 번째 프로세스이며 시스템이 종료되기 전까지 실행된다.

같이 보기 
 SYSLINUX
 Windows startup process

각주

외부 링크 
 Greg O'Keefe - From Power Up To Bash Prompt 
 IBM description of Linux boot process at the Wayback Machine (archived May 31, 2008) a developerWorks article by M. Tim Jones
 Bootchart: Boot Process Performance Visualization

부팅
리눅스