리눅스에서 부팅 시 특정 작업을 하거나 특정 프로그램이 돌도록 하고 싶을 때가 있다. 그럴 경우 어떻게 해야하는지 차근차근 알아보자. 

본 포스팅은 우분투 16.04 기준으로 작성되었다. 하지만 다른 리눅스 배포판도 비슷한 구조를 가지고 있을 것이다.


리눅스 부팅 과정

일단 리눅스 부팅 과정을 간략하게 알아보자.


BIOS

메인보드에 있는 BIOS라고 불리는 펌웨어가 컴퓨터 부팅 시 필요한 처음 동작들을 처리한다. 이 BIOS는 POST(Power On Self Test)라고 불리는 동작을 통해서 부팅에 필요한 하드웨어들이 잘 연결되어 있는지, 하드웨어들이 잘 동작하는지 등을 확인한다. 그리고 부트 가능한 장치들을 사전에 설정된 부팅 우선순위(Periperial Booting Priority)에 따라 차례대로 각각의 장치의 MBR(Master Boot Record) 혹은 부트섹터라고 불리는 첫번째 섹터의 마지막 2바이트를 확인해서 부트섹터의 시그니쳐가 맞는지 확인한 후 시그니쳐(0x55AA)가 맞을 경우 해당 장치로 부팅을 시도한다. 그리고 MBR의 첫번째 바이트로 점프해서 기계어 Instruction을 실행한다.
최근에는 구식의 BIOS를 대체한 UEFI라는 녀석이 여기에 위치하는 경우도 있다.

MBR

부팅하도록 설정된 장치의 첫번째 섹터를 MBR(Master Boot Record)라고 하며, 여기에 나머지 부팅 절차들을 밟기 위한 명령어들이 있다. 하드웨어에서 한 섹터는 512바이트이며, 컴퓨터들은 하위 호환성을 유지하면서 발전했기 때문에, 저장장치의 용량이 커져도 이는 동일하다. 이 MBR에 있는 코드에는 리눅스 부트로더인 GRUB(혹은 레가시 시스템에서는 LILO)이 들어있다.

GRUB

GRUB은 Grand Unified Bootloader의 약자이며, MBR에 GRUB의 기계어 Instruction들이 있다. 하나의 저장장치에 여러개의 OS가 설치되어 있을 수 있으므로, GRUB은 어떤 OS로 부팅을 할 것인지 사용자가 설정할 수 있도록 UI를 제공한다. 사용자가 원하는 OS를 선택하면, 이 부트로더는 저장장치의 다른 섹터에 있는 커널 코드들을 메모리에 로드한 뒤, 커널 코드로 점프하여 커널의 기계어 Instruction들을 실행하도록 한다.

커널

드디어 리눅스 운영체제의 핵심부인 커널이 메모리에 올라오게 되었고, 이제 커널은 부팅을 마무리 짓기 위한 마무리를 지어야 한다.  grub 설정파일에 정의된 루트 경로에 부분에 루트 파일시스템을 마운트하고, /sbin/init 프로그램을 실행한다. init 프로세스는 해당 OS에서 실행된 첫번째 프로세스로 pid가 1이다. 우분투 최신 버전의 경우 init 프로세스 대신 systemd이라는 시스템 데몬 프로세스가 그 자리를 대신하기도 한다. 하지만 전체적인 맥락은 비슷하다.


Init

Init 프로세스는 해당 시스템에 저장된 런 레벨에 맞게 동작한다. 런 레벨은 보통 0부터 6까지의 숫자로 이루어져 있으며 시스템을 어떤 방식으로 부팅하는지를 결정한다. systemd로 제어되는 시스템의 경우 더 많은 종류의 런 레벨을 갖고 있으며, 리눅스 메뉴얼 페이지에서 man systemd.special명령어로 확인이 가능하다.


0 - 시스템 종료


1 - 싱글 유저 모드.

 계정이 하나만 존재한다. 시스템 복원모드라고도 불리며, 기본적으로 관리자 권한의 쉘을 얻게 된다. 파일시스템을 점검하거나 관리자 암호 변경 시 사용한다. S 모드와의 차이점은, 이 모드는 멀티 유저 모드에서 싱글 유저 모드로 스위치 된 경우 이 모드이다. 점검을 위해 싱글 유저 모드로 들어간 경우라고 인식된다.

S - 싱글 유저 모드

1번 모드와 동일하나, 부팅 시 부터 싱글 유저 모드 인 경우이다.

2 - 멀티유저모드, NFS비활성화. 

여러개의 계정을 사용할 수 있으며, NFS(Network File System)사용이 불가능하다. 3번 모드와 같지만 네트워크 사용이 불가능하다.

3 - 풀 멀티유저 모드.

CLI를 가지며 멀티 유저를 지원한다.

4 - 사용안함. 

기본적으료 사용하지 않는 모드이며, 사용자 정의하여 원하는 모드로 사용 가능하다.

5 - X11

3번 모드와 같이 멀티 유저를 지원하는 일반모드이지만 GUI를 지원한다는게 큰 차이점이다.

6 - 재시작

시스템을 재시작한다.


런레벨 프로그램

Init 프로세스가 런 레벨을 결정하면, 각각의 런 레벨에 맞는 서비스들을 실행해야 한다. 이때 각각 런 레벨 별 실행해야하는 서비스들의 디렉토리 경로는 다음과 같다.

레벨 S - /etc/rcS.d
레벨 0 - /etc/rc0.d
레벨 1 - /etc/rc1.d
레벨 2 - /etc/rc2.d
레벨 3 - /etc/rc3.d
레벨 4 - /etc/rc4.d
레벨 5 - /etc/rc5.d
레벨 6 - /etc/rc6.d

여기서 rc는 Run Command의 약자라고 한다.

/etc/rc0.d 디렉토리를 보면 다음과 같이 보인다.
 

root@ubuntu:/etc# tree rc1.d/
rc1.d/
├── K01alsa-utils -> ../init.d/alsa-utils
├── K01bluetooth -> ../init.d/bluetooth
├── K01cups-browsed -> ../init.d/cups-browsed
├── K01docker -> ../init.d/docker
├── K01irqbalance -> ../init.d/irqbalance
├── K01kerneloops -> ../init.d/kerneloops
├── K01lightdm -> ../init.d/lightdm
├── K01mysql -> ../init.d/mysql
├── K01saned -> ../init.d/saned
├── K01speech-dispatcher -> ../init.d/speech-dispatcher
├── K01thermald -> ../init.d/thermald
├── K01ubuntu-fan -> ../init.d/ubuntu-fan
├── K01ufw -> ../init.d/ufw
├── K01uuidd -> ../init.d/uuidd
├── K01vmware-tools-thinprint -> ../init.d/vmware-tools-thinprint
├── K01whoopsie -> ../init.d/whoopsie
├── K01xinetd -> ../init.d/xinetd
├── K02avahi-daemon -> ../init.d/avahi-daemon
├── K02cgroupfs-mount -> ../init.d/cgroupfs-mount
├── K02cups -> ../init.d/cups
├── K04rsyslog -> ../init.d/rsyslog
├── README
├── S01killprocs -> ../init.d/killprocs
└── S02single -> ../init.d/single


K로 시작하는 녀석들은 해당 서비스들을 종료하기 위한 스크립트로, 심볼릭 링크 원본의 스크립트를 stop인자와 함께 실행한다.
S로 시작하는 녀석들은 해당 서비스들을 구동하기 위한 스크립트로, 심볼릭 링크 원본의 스크립트를 start 인자와 함께 실행한다.

뒤에 있는 숫자는 순서를 맞추기 위함이다.


이런식으로 rc?.d 디렉토리들을 확인해보면, 런레벨 2~5와 S레벨의 경우 마지막에 /etc/init.d/rc.local 이라는 스크립트를 실행시킨다.

/etc/init.d/rc.local 스크립트를 살펴보면 내용은, /etc/rc.local 이라는 파일이 있을 경우 실행시킨다고 되어있다.


즉 런레벨 2~5, S의 경우 /etc/rc.local 이라는 스크립트를 부팅 과정 마지막에 실행시킨다는 것이다.


/etc/rc.local 스크립트 실행

Ubuntu16.04의 경우 런 레벨 2~5, S의 경우 /etc/rc.local를 최종적으로 실행시킨다. 해당 파일이 없을 경우 실행하지 않는다. 런레벨 0은 시스템 종료, 6는 재부팅이므로 관계 없고, 런레벨 1의 경우 시스템 복구 모두이기 때문에 복구를 위해서 해당 스크립트를 실행하지 않는것으로 보인다.



리눅스 부팅 스크립트 등록

리눅스 부팅 과정을 알아보면서 알 수 있듯이, 부팅때마다 실행되어야 할 스크립트는 /etc/rc.local에 입력하면 된다.


단, 주의할 사항은 PATH 같은 환경변수가 일반 쉘과 같은 환경으로 설정되지 않을수도 있기 때문에, 환경변수를 설정하고 명령어를 실행하거나, 절대경로로 실행하는 것을 추천한다.


또한 스크립트 실행 도중 에러가 발생했을 때, 표준 출력과 표준 에러를 직접 터미널로 확인하기 어려우므로 파일로 리디렉션(Redirection)하되, 표준 에러도 같이 리디렉션(Redirection)하도록 하는 것을 추천한다.


-------------------------------------

참조

https://www.thegeekstuff.com/2011/02/linux-boot-process/

http://kateee.tistory.com/51

http://nuitstory.tistory.com/500

https://wiki.debian.org/BootProcess

http://cherub.sungkyul.edu/~web/jinboard/files/63_boot.pdf

https://ko.wikipedia.org/wiki/%EC%8B%9C%EB%8F%99_%EC%9E%90%EC%B2%B4_%EC%8B%9C%ED%97%98

http://forensic-proof.com/archives/435

+ Recent posts