본문 바로가기

IT

[OS] 리눅스 부팅과정에 대하여

​​[OS] 리눅스 부팅과정에 대하여

이대규 (dagui@shinbiro.com)

리눅스에서의 부팅은 도스나 윈도우의 부팅과 상당히 다릅니다. 도스나 윈도우의 부팅은 아주 일상적인 일입니다. 컴퓨터를 쓰고싶으면 전원을 넣고 부팅과정을 거칩니다. 그리고 쓰기 싫으면 꺼 놓죠.

그러나 리눅스에서의 부팅은 간만에 있을까 말까 하는 중요한 사건입니다. (개인용 컴퓨터에 설치해서 사용하는 경우에는 매일 켜고 끄고 하지만..) 리눅스가 유닉스에서 갈라져 나온 운영체제라는 것을 생각해보면 이해가 될 것입니다. 무슨 서버 어쩌구 하는 컴퓨터들은 한번 켜서 서비스를 시작하면 시스템에 문제가 발생했다거나 회사가 망했다거나 하는 경우가 아니면 끌 일이 없을 겁니다. (물론 점검을 위해서, 또는 업그레이드를 위해서 끌 수도 있겠지요.)

이러저러한 이유로 리눅스의 부팅과정은 복잡합니다.



​1. 제1단계: LILO 또는 loadlin

로더라고 하는 프로그램이죠. 이것은 커널(운영체제의 심장이죠)을 메모리에 올려놓는 일을 합니다. 어떤 분은 이 LILO가 멀티부팅을 위한 것으로 생각하시는 분이 있던데, 멀티부팅이 아니더라도 리눅스의 부팅을 위해선 꼭 있어야 합니다.

컴퓨터에 전원이 들어오고 나면 제일 먼저 디스크의 첫 부분을 읽어서 거기 있는 프로그램, 즉 로더를 실행시킵니다.

LILO boot: _

로더는 커널 이미지의 위치를 알고 있어서 스스로 커널이미지를 찾아서 메모리에 올려놓습니다.

LILO boot: linux
Loading linux...

※ 커널 이미지: 커널이 하나의 파일로 디스크에 저장되어 있는 것을 말합니다. 커널은 원래 파일에 저장되어있는 성격의 것이 아니고 컴퓨터가 꺼지기 전까지는 항상 메모리에 존재합니다.
그러니까 디스크에 저장되어있는 파일은 커널 그 자체는 아니고 커널의 내용을 담고 있는 무엇이다... 그런 식으로 받아들이면 되겠습니다.

이것으로 로더의 역할은 끝나는 겁니다. 간단하죠? 그렇지만 기나긴 부팅과정은 이제 시작입니다.



​2. 제2단계: 커널 부팅

그 다음에는 커널이 부팅을 하게 됩니다. 커널 자신에 포함되어있는 기능들을 점검하고, 또 어떤 하드웨어가 설치되어 있나 점검합니다. 좁은 의미에서는 이 2단계를 부팅(boot)이라고 말합니다. 하지만 우리가 사용하는 일반적인 의미의 부팅은 앞의 1단계에서 다음에 나올 3단계까지를 모두 포함하는 것이죠.

그리고 대부분의 리눅스 시스템에서는 압축된 커널 이미지를 사용합니다. 이것은 대개 두 가지 이유일 텐데 첫 번째는 저장될 디스크 공간을 아끼자는 것이죠. 하지만 제 생각에는 두 번째 로딩시간을 줄이자는 것이 더 중요할 것 같습니다. 디스크는 아무리 빠르다고 해도 메모리보다는 느립니다. 그러니까 압축된 이미지를 디스크에서 읽고 메모리 상에서 압축을 풀면 압축 푸는 시간이 있다 해도 오히려 더 빠를 수 있다는 겁니다.

아무튼 압축된 커널이미지는 메모리에 올라간 다음에 지가 알아서 압축을 풉니다. 이때 다음과 같은 메시지가 나옵니다.

Uncompressing Linux....

그 다음에는 아까 말한 대로 커널 내부에 설치되어있는 기능들과 현재 설치되어있는 하드웨어를 점검하고 인식하는 과정이 화면에 나옵니다. 아마 대부분 처음 리눅스를 설치할 때 이 엄청난 메시지들을 보고 기가 죽었던 경험이 있을 겁니다.

이때 화면에 출력된 내용은 dmesg란 명령을 통해서 다시 볼 수 있습니다.

$ dmesg | less

less는 한 화면씩 끊어서 보여주는 것이죠. (한 화면을 넘어가거든요.)
그 다음에는 root 파티션을 마운트합니다. 그러니까 지금까지는 디스크나 파일과는 전혀 상관없는 커널만의 동작입니다. 대개 이때는 루트파티션을 read-only로 마운트합니다.

이렇게 하는 이유는 나중에 3단계 INIT script 중에 디스크 검사하는 과정이 있는데, 안전한 상태에서 디스크 검사를 수행하기 위해서 입니다.

fsck.ext2 -a /dev/hda2

뭐 이런 내용을 본 기억이 날 겁니다. 검사가 끝나면 다시 read-write 모드로 remount하게 됩니다.

그리고 커널은 /sbin/init 이란 프로그램을 실행시킵니다.



​3. 제3단계: INIT

음.. 여기서 간단하게 프로세스(process)란 말을 설명하도록 하겠습니다.

프로세스란 실행중인 프로그램을 말하는 것입니다. 파일에 저장되어있는 프로그램은 뭐 실행파일 이라던가 그런 식으로 말하죠. 이건 단지 기계어 명령들을 파일에 적어 놓은 것에 불과합니다. 그렇지만 이것이 메모리에 올라가서 활동을 하면 프로세스라고 부르는 것입니다.

프로세스가 어떤 다른 프로세스를 새로 생성할 경우(실행시킬 경우), 실행시킨 프로세스를 부모 프로세스(parent)라고 하고, 새로 실행된 프로세스를 자식 프로세스(child)라고 부릅니다.
프로세스는 자기 고유의 번호를 가지고 있습니다. 이걸 PID(process id)라고 말합니다. 그리고 프로세스는 자기를 실행시켜준 부모 프로세스의 번호도 알고 있습니다. PPID(parent process id)라고 하죠.

init은 무조건 PID 1번입니다. 왜냐하면 커널이 제일먼저 실행시키는 프로세스이기 때문입니다. 그 다음에 커널은 또 프로세스를 만들지 않습니다. 이후로 생성되는 프로세스는 모두 init으로부터 나온 프로세스이거나 아니면 init에서 나온 프로세스에서 나온 프로세스... 이런 식입니다. 다시 말해서, init은 모든 프로세스의 조상이라고 할 수 있습니다.

아무튼 간에 커널은 init을 실행시키고 나서는 제어를 init에게 넘깁니다. init이 동작하게 되면 커널은 자기의 본업, 그러니까 프로그램이 특정 기능을 요청하면 처리해 주는 등의 일을 하게 됩니다.

init은 흔히 init script라고 부르는 일련의 작업을 진행하게 됩니다. 여기에 관련된 파일은(시스템마다 다르죠. 여기선 RedHat의 경우를 말합니다.)다음과 같습니다.



/etc/inittab : init의 설정파일

/etc/rc.d/rc.sysconfig : init스크립트에서 제일 먼저 실행되는 것

/etc/rc.d/rc.local : init스크립트에서 제일 마지막에 실행되는 것

/etc/rc.d/init.d/* : 데몬을 실행시키기 위한 스크립트들

/etc/rc.d/rc[0-6].d/* : 각 런레벨에 정의된 데몬들을 실행시키기 위한 것 실제 내용은 /etc/rc.d/init.d의 스크립트로의 링크임

/etc/rc.d/rc : 각 런레벨에 해당되는 스크립트를 실행시키는 스크립트

/var/lock/subsys/* : 현재 실행되고 있는 데몬들의 lock파일들

/var/run/*.pid : 현재 실행되고 있는 데몬들의 PID가 적힌 파일들


이것을 하나 하나 추적하는 것도 재미있습니다. 그렇지만 여기서 다 설명하기에는 너무 내용이 많고 또 관심 없는 분들도 많을 것 같아서.. 생략하겠습니다. 쉘 스크립트에 대해서 좀 공부한 다음에 스스로 분석해 보시기 바랍니다.



​4. INIT 과 Runlevel

먼저 init은 /etc/inittab이란 파일을 읽습니다.

#
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Author:Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
# Modified for RHS Linux by Marc Ewing and Donnie Barnes
#
# Default runlevel. The runlevels used by RHS are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
id:3:initdefault: # 이 줄에서 기본 런레벨을 지정함
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
:
:

여기서 런레벨(runlevel)이란 개념이 나오는데 이것은 말하자면 init스크립트가 진행하는 경로를 말한다고 생각하시기 바랍니다. 0번 부터 6번까지의 길이 있습니다.

0번은 시스템 종료하는 길입니다.

1번은 싱글유저(single user) 모드.. 그러니까 root혼자 쓰는 것입니다. 다른 사용자들이 로그인할 수 없습니다.

2번은 다중사용자 모드인데 NFS지원을 하지 않는 것입니다. 나머지는 3번과 같습니다.

3번은 완전한 다중사용자 모드로 모든 네트워크 기능을 지원하는 것입니다.

4번은 특별히 어떤 의미가 정해진 것이 없습니다.

5번은 3번과 같은데 X윈도우 상에서 로그인할 수 있습니다.

6번은 재부팅 하는 데 쓰는 것입니다.

위의 예에서 initdefault라는 것이 있는 줄을 보면

id:3:initdefault:

3번을 디폴트로 정해 놓은 것을 알 수 있습니다. init은 제일 먼저/etc/rc.d/rc.sysinit에 적어 놓은 명령들을 실행합니다. 여기서는 루트파티션 이외의 파티션을 검사해서 마운트시키는 등의 과정이 있습니다.

그리고 나서 entering runlevel 3 하는 식으로 메시지가 나오면서 runlevel 3번에 해당하는 스크립트들을 실행시킵니다. 이 스크립트들은 /etc/rc.d/rc3.d 디렉토리에 들어 있습니다. 이것들은 주로 데몬 프로세스를 시작하거나 종료시키는(죽이는, kill)역할을 합니다.

$ ls /etc/rc.d/rc3.d
K15gpm@ K60atd@ S20random@ S75keytable@
K20rusersd@ K95nfsfs@ S30syslog@ S80sendmail@
K20rwhod@ S01kerneld@ S40crond@ S85httpd@
K55routed@ S10network@ S50inet@ S99local@

여기서 K로 시작하는 것은 데몬을 죽이는 것(kill 또는 stop)이고 S로 시작하는 것은 데몬을 실행시키는 것입니다. 데몬이란 것은 백그라운드에서 계속 실행되면서 특정한 작업을 하는 프로세스를 말하는 것입니다. 주로 시스템 운영에 관련된 것이나 네트워크 서비스를 하기 위한 것이 많습니다. (MUD 게임도 있죠.)

위의 예를 보면 gpm데몬은 죽이고 http데몬은 시작한다는 것을 알 수 있습니다. 그리고 그 다음의 숫자는 실행 순서를 정해 놓은 것입니다. 그리고 ls -l 명령으로 보면 이 각각의 항목들이 /etc/rc.d/init.d 아래의 스크립트로 링크되어 있는 것을 볼 수 있을 겁니다.

그리고 순서에 따라서 마지막에 S99local이 실행됩니다. (이것은 /etc/rc.d/rc.local 에 링크되어 있습니다.) 일반적으로 부팅과정에 어떤 작업을 추가시키려면 이 파일에 추가시키게 됩니다. 하지만 부팅에 필요한 작업은 미리 정의 되어있는 것으로 충분한 경우가 대부분입니다.

만약 추가해야 할 사항이 있다고 생각되면 자신의 쉘 초기화 파일에서 해주어도 상관없는 작업이 아닌지 생각해 보기 바랍니다. 꼭 부팅과정에서 해주어야 하는 것은, 새로 추가된 데몬을 실행시키는 것입니다.

이것으로 init 스크립트는 끝나게 됩니다. 그리고 나서 init은 /etc/inittab에 정의된 대로 터미널을 개방하고 사용자가 로그인할 수 있도록 합니다. (로그인은 부팅과정에 포함되지 않습니다. 아까 언급한 것처럼 유닉스 시스템에서 부팅과 셧다운은 매우 이례적인 일이지만 로그인과 로그아웃은 매우 일상적인 일이지 않습니까?)



​5. Shutdown

시스템 종료(셧다운)란 것도 특별한 것이 아닙니다. init에 의해서 runlevel 0번이 실행되고 나서 커널이 동작을 멈추는 것입니다.

runlevel 0의 중요한 동작은 두 가지가 있는데, 먼저 실행중인 모든 프로세스를 죽입니다. (/sbin/kill 아시죠? 혹은 /sbin/killall이란 프로그램이 사용되기도 합니다.) 여기에는 부팅 시에 띄웠던 데몬들을 모두 종료시키는 것도 포함이 됩니다.

프로그램을 죽일 때 그냥 죽이는 것이 아니라 약간의 간격을 두고 TERM, QUIT, KILL 순서로 시그널을 보냅니다. 이것은 프로그램이 작업하던 내용을 종료시킬 수 있도록 약간의 시간적 여유를 주기 위한 것입니다.

시그널이란 것은 어떤 프로세스에게 일정한 의미를 가지는 신호를 보내는 것입니다. 예를 들어 프로그램을 만들 때 어떤 시그널을 받으면 현재 작업중인 것을 모두 저장하고 종료하도록 한다든지 할 수 있습니다. (단 KILL 시그널(9번)은 다른 작업을 허용하지 않습니다. 무조건 종료됩니다.)

참고로 vi에디터로 파일을 편집하던 중에 모르고 종료를 시켰다면, 나중에 -r 옵션으로 임시 저장된 파일을 복구할 수 있습니다.

$ vi -r that_file.txt

모든 프로세스가 종료되었으면 마운트된 디스크들을 언마운트 합니다. 만약 실행중인 프로세스가 있다면 디스크는 언마운트 되지 않을 것입니다. 만약에 정상적인 종료 과정이 없이 전원이 꺼진다면 디스크가 언마운트 되지 않은 상태에서 꺼지는 것입니다. 나중에 다시 켤 때에 디스크 검사가 오래 걸릴 것입니다.

이렇게 부팅 과정에 대해 순서대로 살펴봤습니다. 하지만 이 runlevel에 대한 것을 이해하기가 쉽지 않을 것 같다는 생각이듭니다. 가장 쉽게 이해하는 방법은 아까 말씀드린 것처럼 하나하나 파일을 분석하면서 추적해 보는 것입니다.(저도 그렇게 배웠습니다.) 분석해 보고 싶은 생각이 들었다면, 먼저 쉘 스크립트를 어느 정도 배워야 할 것입니다. 그리고 먼저 inittab(5)의 매뉴얼을 읽는 것에서 시작하기 바랍니다.

$ man 5 inittab

그리고 init(8), lilo.conf(5) 도 참고가 될 것입니다.



​​본론: runlevel에 대하여...

1. runlevel 바꾸기(switching)

현재의 runlevel을 바꿀 수도 있습니다. 일단 runlevel 3번으로 시작했으니까 컴퓨터는 계속 runlevel 3번에 있습니다. 컴퓨터를 끄고 싶으면

$ init 0(root만 할 수 있음)
0번 runlevel로 바꾸는 것입니다. 0번은 시스템을 종료하는 것이라고 했죠. 모든 프로세스를 죽이고(kill)디스크를 언마운트하고 끝에 가서는 system halted란 메시지를 출력하고 정지해 버립니다.

$ init 6
0번과 비슷한 과정을 거치는데 끝에 가서 재부팅을 하게 되죠. 아니면 Alt-Ctrl-Del을 눌러도 되는데 이것은 /etc/inittab에서 다음 라인에 의해서 정의된 것입니다.

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

Alt-Ctrl-Del 키를 이용해서 재부팅을 하는 것은 MS-DOS 시절부터 PC에서 사용되던 방식입니다. 이것을 동작하지 않게 하려면 당연히 /etc/inittab에서 수정해 주면 될 것입니다.

$ init 5 라고 하거나 /etc/inittab에서
id:5:initdefault:
처럼 디폴트를 5번으로 지정해 놓으면 X윈도우에서 동작하는 로그인 화면을 볼 수 있습니다.

만약에 runlevel 0번이나 6번을 디폴트로 지정해 놓으면 시스템을 시작하자마자 종료하거나 재부팅이 되는 심각한 상황에 처하게 될 것입니다.

$ init 1
이라고 하면 데몬을 모두 죽이고 싱글유저 모드로 갑니다. 이때 다른 사용자는 로그인 할 수 없습니다. 오직 root 한사람만이 작업을 할 수 있습니다. 시스템을 점검한다던가 문제를 발견했을 때 이렇게 싱글유저 모드로 가서 작업을 하는 것이 좋겠죠.

이때의 root는 모든 작업을 root의 권한으로 할 수 있기는 하지만 멀티유저모드에서의 root 환경과는 좀 다를 것입니다. 먼저 홈 디렉토리가 “/root”가 아니고 “/”입니다.(일반적으로 다른 유닉스 시스템에서는 root의 홈디렉토리가 따로 있지 않고, “/”가 root의 홈디렉토리입니다.) 그러므로 당연히 /root에 들어있는 .profile, .bashrc 등은 하나도 실행되지 않습니다. 이런 초기화 파일들에 설정해 놓은 변수나 alias 들은 하나도 작동하지 않을 것입니다.

다시 원래 runlevel로 돌아가려면
$ init 3 라고 하거나 현재의 쉘(싱글유저모드의 쉘)을 종료시키면 됩니다.

$ exit

​2. runlevel 편집

예를 들자면 runlevel 3번에 http 데몬이 실행되게 되어있는데 이것이 마음에 들지 않는다면 어떻게 할까요? 아니면 다른 데몬을 띄우고 싶다면...

위의 runlevel구조를 잘 이해하고 있다면 직접 /etc/rc.d 아래의 디렉토리에서 작업을 해도 좋을 것입니다. 하지만 좀더 편하게 쉽게 하려면 X상에서 다음과 같이 실행시켜 봅시다. (RedHat만)

$ tksysv

아니면
$ control-panel

을 해서 제어판을 실행시킨 다음에 거기서 runlevel editor란 것을 누릅니다. (이 두 가지는 사실 같은 것입니다.) 그러면 특정 런레벨에 어떤 데몬을 띄울 것인지 말 것인지 등을 추가하거나 삭제할 수 있습니다.



《필자소개》

이대규남은 성균관대 중문과 4학년에 재학중이며 리눅스 사용 경력은 1년 6개월 정도입니다.
처음 리눅스를 배울때는 1년 동안 PC에 리눅스만 깔아 사용하였다고 하며 시간이 있을 때는 인터넷 서핑과 홈페이지 구축작업등을 한다고 합니다.


[원글링크] : https://www.linux.co.kr/home2/board/subbs/board.php?bo_table=lecture&wr_id=838

'IT' 카테고리의 다른 글

[OS] 리눅스 부팅과정에 대하여  (0) 2017.10.30
[Tip] 유용한 vi명령어  (0) 2017.10.27