Search

'리눅스'에 해당되는 글 6건

  1. 2019.06.09 오픈소스 라이센스 정리
  2. 2018.07.23 스핀락
  3. 2018.07.18 eventfd
  4. 2018.05.26 리눅스 디스크, 폴더 용량 확인하기
  5. 2017.01.04 디바이스트리(Device Tree)
  6. 2016.10.02 Linux OS 부팅 과정

오픈소스 라이센스 정리

컴퓨터공부/오픈소스 2019.06.09 11:04 Posted by 아는 개발자

오픈소스를 이용해서 개발하는 경우 코드를 무료로 보고 사용할 수 있어 개발하기 편리하다는 장점이 있으나 사용하고 있는 오픈소스가 어떤 라이센스를 가지느냐에 따라서 상업적인 이용이 제한될 수도 있고 내가 만든 코드를 공개해야 할 의무까지 생길 수 있다. 이런 경우를 예방하려면 개발하기 전부터 사용할 오픈소스 라이센스에 대해 검토해둘 필요가 있다. 이번 포스트에서는 유명한 오픈소스 라이센스들에 대해서 간단히 정리를 해보려고 한다.


0. 라이센스란? 


소프트웨어의 지적 재산권을 일컫는 말이다. 음악의 저작권과 비슷한 개념 정도로 생각하면 될 것 같다. 처음 코딩에 입문하는 사람들은 남들이 짠 코드랑 본인이 짠 것과 함수와 변수 명만 제외하면 거의 차이가 없어(헬로 월드 수준이니까) 딱히 저작권이라고 할만한 것이 있는지도 모르겠고 또 수만줄이 넘는 코드에서 카피 유무의 확인조차 불가능 할 것 같은 소프트웨어에 지적 재산권이 통용되고 있다는 것에 의문을 품기도 하지만 실제 소프트웨어 업계에선 엄연히 라이센스 규정에 따라서 운영되고 있으며 이미 몇가지 판례를 통해 법적인 효력까지 갖추고 있기 때문에 쉽게 무시해선 안된다



사용하고 있는 오픈소스가 어떤 라이센스를 쓰고 있는지 확인하려면 소스코드 파일의 맨위 주석을 보면 된다. 어떤 이유인지는 모르겠으나 거의 대부분의 라이센스가 파일 최상단에 명시돼 있다. 위 그림은 리눅스 커널 파일중 하나를 gedit으로 열어본 사진이다. 이 파일은 GNU 라이센스를 사용하고 있다.


1. GPL 라이센스


소스코드 공개 필요 O, 동일한 라이센스 적용 O, 상업적이용 O


GNU라고도 불리는 이 라이센스는 리눅스 커널에 기본 라이센스로 채택되면서 개발자 사이에선 유명한(악명높은) 라이센스가 됐다.. 이 라이센스는 오픈소스의 철학에 기초해서 만들어진 것이기 때문에 '자유를 누린 만큼 너의 코드도 공개해!' 원칙을 가지고 있다. 이 라이센스를 이용해서 만든 소프트웨어는 동일한 GPL 라이센스를 사용해야하고 모두 코드를 공개해야 한다. '내가 얘네 썼는지 어떻게 알겠어' 하면서 무시할 수도 있지만 이미 판례를 통해 법적인 효력까지 갖춘 라이센스이기 때문에 사용할 때는 신중해야 한다. 자세한 내용은 나무위키 전염성 조항 참고하면 좋다. 리눅스 커널 기반 안드로이드 폰 제조사들이 다른 코드는 몰라도 리눅스 커널 소스코드는 공개할 수 밖에 없는 것은 GPL 라이센스 덕분이다.


2. BSD 라이센스


소스코드 공개 필요 X, 동일한 라이센스 적용 X, 상업적 이용 O 


BSD는 자유 소프트웨어 저작권의 한가지로 BSD계열(미국 캘리포니아 대학 버클리에서 개발한 운영체제인 유닉스)에서 주로 채택하고 있는 라이센스다. 규정은 앞서 언급한 GPL과 확연하게 다른데 이 오픈소스 라이센스를 가진 코드는 자유롭게 사용할 수 있고 소스코드를 공개 하지도 않아도 되며 돈받고 팔아도 된다. BSD에서 눈여겨 볼 만한 것은 다루는 것은 누구나 자신의 용도로 사용할 수는 있지만 이 소프트웨어를 사용해서 발생 가능한 모든 리스크와 손해는 본인이 책임지도록 하고 있다. 책임 도피형 라이센스다.


3. MIT 라이센스


소스코드 공개 필요 X, 동일한 라이센스 적용 X, 상업적 이용 O


라이센스 이름에서 추측할 수 있듯이 매사추세스 공과대학에서 만든 소프트웨어 라이센스다. BSD를 기초해서 만든 라이센스라 거의 규정은 똑같다. 소스코드를 공개할 필요도 없고 동일한 라이센스를 적용할 필요도 없으며 이 소프트웨어를 사용해서 발생할 수 있는 손해에 대해서도 책임을 지지 않는다는 것 까지 동일하다. BSD 계열의 소프트웨어에서 일반 소프트웨어로 옮겨오기 위해 만든 라이센스 정도로 생각하면 될 것 같다.


4. Apache 라이센스 


소스코드 공개 필요 X, 동일한 라이센스 적용 X, 상업적 이용 O 


아파치(Apache) 소프트웨어 재단에서 자체적으로 만든 라이센스다. 안드로이드 프레임워크쪽의 대부분의 라이브러리가 이 라이센스 규정을 따르고 있다. BSD와 거의 규정이 동일해 소스코드를 공개할 필요가 없다. 차이점이 있다면 Apache 라이센스는 특허권 측면에서 좀더 완성도를 높여서 Apache 라이센스로 출원한 특허에 대해서는 소스코드 사용자에게 특허의 무제한적 사용을 허가한다는 규정을 담고 있다. 즉 어떤 소프트웨어가 아파치 라이센스를 채택해서 배포했다면 그 소프트웨어가 특허출원이 되어 있어도 사용자에게 특허 사용료를 요구할 수 없다는 뜻이다. 나무위키에 따르면 소스코드를 무료로 공개해놓고 그걸 빌미 삼아 특허권 소송을 제기하는 더티한 플레이를 막기 위한 규정이라 한다. 이런 법적인 안전장치까지 있어서 개발자들이 가져다가 쓸 때 가장 선호하는 라이센스 중에 하나인 것 같다.


5. 추가로 


상세한 규정을 알고 싶으신 분들은 블로그 사이트를 찾는 것보다 정보통신산업 진흥원에서 만든 책자를 보는 것이 도움이 될 것 같다. https://www.oss.kr/oss_license 사이트에 가면 라이센스 가이드를 다운로드 받을 수 있다. 법적인 문제와 연관 될 수 있으니 가능하면 전문가와 상담하는 것이 좋을 것 같다.

'컴퓨터공부 > 오픈소스' 카테고리의 다른 글

오픈소스 라이센스 정리  (0) 2019.06.09
FFmpeg  (0) 2018.10.31
자동차시장 오픈소스 - 2  (0) 2017.01.31
자동차 시장 오픈소스 - 1  (0) 2017.01.15
이런 오픈 소스도 있다!  (0) 2017.01.07
오픈소스 시작하기  (0) 2017.01.01

스핀락

컴퓨터공부/리눅스 2018.07.23 22:23 Posted by 아는 개발자

멀티프로세서 환경에서는 시스템에서 생성된 프로세스가 하드웨어에 설치된 CPU의 수만큼 동시에 실행된다. CPU에서 동작 중인 프로세스는 메모리 영역에 할당된 데이터중 연산이 필요한 값을 읽고 쓰는 작업을 수행하게 되는데 이때 여러 개의 프로세스가 같은 데이터 영역을 공유하게 되는 경우가 있다. 여러 개의 프로세스가 공통된 전역 변수의 값을 읽는 경우가 대표적인 예다.




데이터 값이 고정되고 모든 프로세스가 고정된 값을 읽기만 하면 별 다른 문제는 없다. 그런데 누군가가 쓰게 되는 경우부터 싱크 문제가 생긴다. A랑 B 프로세스가 있다고 해보자. A 프로세스는 C와 D의 데이터 값을 수정하게 되어있고, B 프로세스는 A프로세스보다 조금 늦게 생성되어 A 프로세스가 수정한 값을 읽도록 짜여져 있다. 싱글코어에선 A 프로세스 다음에 B 프로세스가 스케줄링돼 수정 작업과 읽는 작업이 순차적으로 이뤄진다. 그런데 멀티코어에서는 B프로세스가 A 프로세스의 작업이 끝나기 전에 스케줄링 될 수도 있다. 이런 경우에는 수정된 C, D 값을 읽지못하고 예전 값을 읽게 될수도 있다.


static __always_inline void spin_lock(spinlock_t *lock)
static __always_inline void spin_unlock(spinlock_t *lock)


이런 경우를 막기위해 리눅스에서는 spin_lock이라는 API를 제공한다. spin_lock은 매개변수(spinlock_t)의 값을 이용해 다른 프로세스의 공유 영역에 대한 접근을 막을 수 있는 함수다. spin_lock을 부르면 매개 변수의 값을 보고 다른 프로세스의 점유 유무를 확인한다. 점유한 상태가 아니면 매개변수의 값을 점유 상태로 바꾸고 공유 영역에서 수행할 작업을 수행하고 그렇지 않으면 다른 프로세스가 놓을 때까지 (busy waiting으로)기다린다. 수행한 작업을 완료한 후 spin_unlock 함수를 통해 점유 상태를 취소한다. 취소 후에는 대기중이던 프로세스가 매개 변수의 값을 읽고 재점유한다.


다시 예제로 돌아와보자. A 프로세스가 값을 수정하기 전에 B 프로세스가 읽는 것을 막기 위해선 값을 수정하는 A 프로세스가 값을 수정하는 작업과 B 프로세스가 값을 읽는 작업 앞에 spin_lock을 걸어두면 된다. 수정 작업이 먼저 실행되기 때문에 A 프로세스가 먼저 lock을 획득하게 되고 중간에 읽기 작업을 수행하는 B 프로세스는 lock을 획득하지 못해 busy waiting 상태로 기다린다. 수정작업이 모두 끝난 A는 다른 프로세스가 접근 할 수 있도록 spin_unlock을 수행하게 되고 이 이후에 B 프로세스는 lock을 획득해 수정된 값을 읽을 수 있게 된다.




여기서 더 나아가 하드웨어에서 인터럽트가 들어온 상황을 생각해보자. 프로세스가 lock을 획득해서 작업하는 도중에 인터럽트가 들어오면 CPU는 하던일을 중단하고 인터럽트 서비스 루틴을 처리한다. 그런데 만약 인터럽트를 처리하는 작업중에서 인터럽트가 들어오기전 프로세스가 획득한 lock을 갖기 위해 spin_lock을 부르는 코드가 있다고 해보자. 인터럽트 서비스 루틴도 일반 프로세스와 마찬가지로 lock을 획득 할 수 있을 때까지 대기 상태에 있다. 그런데 lock을 놓아줘야하는 프로세스는 여전히 인터럽트에 걸린 상태에서 다시 스케줄링 되지 못하고 있다. 서로가 서로의 진행을 막고있는 셈이다. 이런 경우 프로세스는 교착 상태(Dead Lock)에 빠진다.


static __always_inline void spin_lock_irq(spinlock_t *lock)
static __always_inline void spin_unlock_irq(spinlock_t *lock)


이를 해결하기 위해 리눅스에서는 lock을 획득한 상태에서는 cpu의 인터럽트를 막을 수 있는 함수를 제공한다. spin_lock_irq 함수를 사용하면 해당 lock을 획득하고 다시 놓을 때까지 프로세스의 인터럽트를 막는다. 이 시간 동안 들어오는 인터럽트는 pending 상태로 대기하고 있다가 spin_unlock_irq가 불리면 다시 재실행 된다.


그런데 spin_unlock_irq는 disable 되어있던 인터럽트를 모두 enable 시키는데 이러면 함수의 depth가 깊어지는 경우 문제가 발생 할 수 있다. 어떤 함수가 spin_lock_irq를 부르고 lock 내부 루틴을 타는 함수가 또 spin_lock_irq를 부르면 정상적으로 함수가 종료 될 때 spin_unlock_irq가 두번 불리게 될 것이다. 그런데 spin_unlock_irq는 예전에 인터럽트가 몇번 disable 됐는지 상관없이 모두다 enable하게돼 첫번째 spin_unlock_irq와 두번째 spin_unlock_irq가 호출되는 사이 지점은 인터럽트가 disable이 되지 않는다. 즉 spin_lock_irq를 못쓰고 spin_lock을 쓰는것과 마찬가지인 셈. 이 사이에 하드웨어 인터럽트가 들어와 똑같은 lock에 spin_lock을 걸면 예전처럼 deadlock이 걸린다.


#define spin_lock_irqsave(lock, flags)              \
do {                                \
    raw_spin_lock_irqsave(spinlock_check(lock), flags); \
} while (0)
static __always_inline void spin_unlock_irqrestore(spinlock_t *lock,
                                 unsigned long flags)
{
    raw_spin_unlock_irqrestore(&lock->rlock, flags);
}


이런 경우를 해결하고자 spin_lock_irqsave라는 함수가 있다. 잘 보면 이 함수는 매개변수로 flags라는 값을 하나 더 주는 것을 볼 수 있다. flag에다가 현재 interrupt의 상태를 보존해 놓는 것이다. 이 값을 활용해 더 안전하게 스핀락을 처리할 수 있도록 한다.



'컴퓨터공부 > 리눅스' 카테고리의 다른 글

Cgroup (Control Group)  (0) 2018.09.15
CPU pinning과 taskset  (0) 2018.08.27
스핀락  (0) 2018.07.23
eventfd  (0) 2018.07.18
workqueue 사용법  (0) 2018.07.16
tasklet 사용법  (0) 2018.06.17

eventfd

컴퓨터공부/리눅스 2018.07.18 21:37 Posted by 아는 개발자

eventfd는 리눅스기반 운영체제에서 파일 디스크립터를 통해 프로세스들끼리 통신 할 수 있는 API중 하나다. IPC로 가장 유명한 pipe는 read와 write용으로 채널을 따로 만드는 것과는 달리 eventfd에서는 읽고 쓸 파일 디스크립터 하나면 충분하기 때문에 번거로운 세팅작업 없이 간단하게 구현 할 수 있다. 그러나 단 한개의 채널을 사용하는만큼 사용할 수 있는 기능은 제한적이다. 이 파일 디스크립터로 교환할 수 있는 값은 1 이상의 64비트 정수 하나가 전부라 데이터 전송보다는 이름처럼 프로세스간에 event를 전달하는 용도로 사용한다.


1. eventfd(uint64 initval, int flag)


프로세스들끼리 통신 할 수 있는 파일 디스크립터를 만드는 함수다. 종료후에는 채널로 사용 할 수 있는 64비트 정수형 파일 디스크립터가 리턴된다.


첫번째 인자는 counter 값이다. counter 값은 eventfd의 고유한 값 정도로 보면 될 것 같다. 이 값은 write로 추가 할 수 있고 read로 읽을 수 있다. 그런데 flag에 따라서 동작이 달라지니까 유의하도록 한다.


2. read(int fd, void* buf, size_t count)


eventfd의 counter 값을 읽는 함수다. 단 counter 값은 무조건 1 이상이 되야 하고 counter 값이 0이면 되면 읽지 못하고 에러를 리턴한다. 1이상이면 가지고 있던 counter 값을 리턴한다. 그런데 리턴한 후에는 flag 값에 따라서 counter 값을 변형한다.


- EFD_SEMAPHORE가 세팅된 경우에는 counter 값을 1만큼 감소시킨다. 세마포어 기능을 한다고 보면 된다. 여러개 프로세스가 읽는 경우를 생각한 것 같다.


- EFT_SEMAPHORE가 세팅이 안된 경우에는 counter 값을 0으로 리셋한다.


3. write(int fd, const void *buf, size_t count)


counter 값을 추가할 수 있는 함수다. counter 값은 uint64 범위 내에서 자유롭게 추가 할 수 있으며 만약 최대값을 넘는 경우에는 막히고 에러를 리턴한다. 별거 어려운게 없다.


나중에는 실제로 프로세스들이 어떻게 사용하고있는지 분석해봐야겠다.




'컴퓨터공부 > 리눅스' 카테고리의 다른 글

CPU pinning과 taskset  (0) 2018.08.27
스핀락  (0) 2018.07.23
eventfd  (0) 2018.07.18
workqueue 사용법  (0) 2018.07.16
tasklet 사용법  (0) 2018.06.17
tasklet과 workqueue의 차이점  (0) 2018.06.15

리눅스 디스크, 폴더 용량 확인하기

삽질 기록 2018.05.26 14:29 Posted by 아는 개발자

매번 구글창에 디스크/파일 용량을 확인하는 단축키를 찾기 번거로워서 이번 포스트로 정리하고 머릿속에 기억해두려고 한다.


1. 디스크 용량 확인, df


시스템상에서 FILE이 있는 곳의 파일시스템에 대한 정보를 보여준다. 즉 시스템에 마운트되어 있는 모든 것들에 대한 사용 정보를 출력한다는 뜻.

그냥 df를 검색하면 아래처럼 나온다. 여기서 1K-blocks는 파일시스템의 전체 용량, Used는 사용중인 크기, Available은 사용가능한 메모리의 크기를 의미한다.



그런데 위 그림은 바이트 단위라서 보기가 어렵다. -h 옵션을 넣어서 human readable하게 변환하자. 훨씬 직관적으로 보인다.



df --help 로 검색하면 다양한 옵션을 발견할 수 있으니 이것도 적극적으로 활용하자. 디스크 용량 검색하는 것은 이걸로 끝. 다른 것들도 있는데 하나만 정확하게 기억하는게 중요할 것 같다.


2. 파일, 폴더내 총용량 확인, du


파일 크기같은 경우 ls를 통해서 용량 정보도 나오고 여기에 -h 옵션만 넣으면 사람이 읽을 수 있는 용량 크기로 나온다. 그런데 폴더내 총용량을 확인할 때는 폴더 디렉토리 자체의 크기만 볼 수 있어서 사용할 수가 없다. 이럴때는 du라는 명령어를 사용하면 편하다. 


du 명령어를 이용해 Pictures 폴더를 검색해봤다.



앞의 416 은 Pictures 폴더내에 있는 파일들의 총 용량 크기다. 단위가 없어서 읽기 불편하니 -h 옵션을 붙여 human readable하게 변형하자. 

이것도 df처럼 용량이 커지면 M, G 단위로 변환해준다.



폴더 내의 파일들이 궁금하면 -a 옵션을 붙이면 된다. 반대로 출력하고 싶지 않다면 -s 옵션을 붙여서 제거한다.


    


디바이스트리(Device Tree)

컴퓨터공부/리눅스 2017.01.04 19:07 Posted by 아는 개발자

운영체제(Operating System)가 하드웨어와 소프트웨어 사이의 중간자 역할을 하고 사용자의 애플리케이션이 하드웨어를 조작하기 쉬운 환경을 제공 한다는 것은 컴퓨터 전공자라면 운영체제 수업시간에 귀가 빠지도록 배웠을 것이다. 운영체제는 오드로이드, 아두이노, 주노보드처럼 다양한 보드 위에 있는 RAM, CPU, EMMC등 하드웨어들을 초기화 및 조작하는 역할을 한다. 지금 이 글을 쓰기 위해 타자를 치는 동안 운영체제는 타자 입력 인터럽트를 처리하고 소프트웨어에 전달하는 일을 계속 하고 있다.


어떤 메인 보드를 사용하더라도 운영체제의 기본 뼈대는 동일해야 한다. 하지만 메인 보드들마다 상세 하드웨어 스펙은 천지 차이다. 예를 들면 RAM의 크기나 USB의 물리 주소의 위치처럼 당연한 것 부터 이 인터럽트 핸들러는 몇번까지 등록하는지 등등 생각하면 한도 끝도 없다. 이런 하드웨어 정보들을 모두 운영체제 코드 내에 하드코딩 해버린다면... 아마 코드가 매우 지저분해지고 엉망이 될 것이다. 그렇다고 하드웨어 제조사들은 많고 또 저마다 자기들이 최고라고 자랑하는 보드를 내놓는데 안 쓰기는 아깝고. 


간단히 하드웨어 정보들만 정리해서 던져주면 운영체제가 전달받은 정보들을 활용해 하드웨어를 전달하면 어떨까? 하드코딩으로 잘 부분을 전달받은 정보들로만 치환해주면 될 것 같아 깔끔해보인다.


이런 구조를 리눅스 OS는 이미 구현 해뒀다. ARM기반의 보드를 사용하는 경우 리눅스를 부팅을 위해선 디바이스 정보들을 바이너리 형태로 싹 저장해둔 디바이스 트리라는 것이 필요하다. 부팅시 리눅스는 디바이스 트리 정보들을 쭉 훑어보고 하드웨어 초기화시 필요한 정보들을 파싱(parsing)해서 사용한다. 예를들어 빈 메모리 영역을 관리할 Buddy system을 만들고 싶다면 먼저 전체 메모리의 크기는 얼마인지도 알아야 하고 물리주소 몇 번부터 몇 번까지를 메모리 영역으로 사용 할 수 있는지도 알아야 한다. 디바이스 트리에 이런 정보들을 모두 담아 뒀으니 리눅스는 그냥 여기 있는 정보들을 가져다가 사용하면 된다.



Device Tree안의 메모리 정보가 있는 곳에서(노드라고 부른다) reg 값의 앞에 두 셀을 Address, 뒤의 두 셀을 Size로 정해서 시작 주소와 하드웨어의 사이즈를 읽어 올 수 있다.


운영체제가 사용하는 디바이스 트리 정보들은 모두 바이너리로 되어 있다. 이런 하드웨어 정보들은 제조사들이 dts파일인 스크립트 형태로 만들어 두고 커널 빌드시 디바이스트리도 같이 빌드돼어 dtb 형태의 바이너리 파일이 만들어진다. 리눅스 커널을 받아보면 arch/arm64/boot/dts 폴더 안에 보드에 따라 설정된 device tree 스크립트를 확인 할 수 있다. 



디바이스 트리 문법을 알고 싶다면 https://www.devicetree.org/ 요 사이트를 한번 참고해보길 바란다. 예전에 문법공부한다고 참고하던 사이트가 있었는데 어디갔는지 모르겠다. 어렵지 않으니 그냥 스크립트만 봐도 금방 따라 갈 수 있을 것이다.


만든 스크립트는 dtc라는 컴파일러를 이용해 바이너리로 만들 수 있다. 우분투에 패키지 형태로 제공되므로 다운받아서 사용하면 편하다.


x86에서는 ACPI를 사용한다.

Linux OS 부팅 과정

컴퓨터공부/리눅스 2016.10.02 17:18 Posted by 아는 개발자

이번 글을 리눅스 OS가 설치된 컴퓨터가 부팅 되는 과정을 설명하는 포스트다. 부트로더가 하는 역할과 초기 부팅시 커널 이미지가 어떤 작업을 하는지 파악하는것에 초점을 두고 작성했으며 중간중간 이들의 정의도 포함되있다.


http://www.tldp.org/HOWTO/HighQuality-Apps-HOWTO/boot.html 페이지를 참고해서 글을 작성했다.


1. 컴퓨터 전원 On


사용자가 컴퓨터 전원을 키면 메인보드에서는 전원이 켜진것을 확인하고 특정 저장위치에 심어둔 부트로더를 실행시킨다.


2. 부트로더(Boot loader)


부트로더의 기능은 말그래도 부팅할 때(Boot) 로드(Load)하는 역할을 한다. 무엇을 로드하는지가 중요한데 OS에 따라 다르지만 리눅스를 기준으로 설명하면, 부트로더는 리눅스가 구동할 커널 이미지(zImage)와 부팅 디바이스의 정보(device tree block)를 로드하는 역할을 한다. 커널이미지와 device tree block은 디스크 저장소에 존재한다. 사용자가 부트로더 어떤 부분을 읽어오라 설정을 하면 바이오스는 해당 위치에 존재하는 바이너리 파일(이미지)를 읽어오도록 구동된다. 여기서 어떤 하드디스크를 사용하느냐에 따라 로딩 속도가 달라진다. SDD가 HDD보다 부팅속도가 빠른것도 위와 같은 이유 때문이다.




로드 하는 곳은 특정 램 영역이다. 커널 이미지는 컴퓨터가 꺼지기 전까지 항상 램 영역에 상주해 컴퓨터 작동에 필요한 코드를 제공한다. 커널 이미지가 위치하는 램 영역 또한 부트로더를 이용해 설정이 가능하다.


대표적인 부트로더로는 바이오스와 u-boot가 있는데 사용하는 Architecture로 구분된다. x86은 주로 바이오스를 사용하고 ARM은 u-boot를 사용한다. 그런데 요새는 둘다를 통합 할 수 있는 UEFI를 사용하는 추세다.


3. 커널 이미지 코드 실행


부트로더 작업이 모두 끝나면 부트로더는 CPU가 다음에 실행하는 코드 (pc값)을 커널 내부로 넘겨서 제어권을 커널에게 줘버린다. 그러면 이제부턴 온전히 커널의 독무대이다. 


( Program Counter 값을 커널 이미지가 위치한 곳으로 옮겨서 앞으로 커널 코드를 수행하도록 한다 )


커널은 개발자가 작성해둔 작업들을 실행한다. 그 작업들로는


      • 페이지 테이블 초기화 작업
      • 하드웨어 초기화 작업(블루투스, 마우스, 키보드 등등...)
      • 드라이버 초기화 작업
      • 네트워크 접속
      • Secondary core 초기화 작업
      • 등등.. 무수히 많다...
OS를 실행 할 때 필요한 초기화 작업들을 모두 한다. 맨처음 부팅할 때 나오는 커널 메시지들이 요런 녀석들이다. 컴퓨터 킬 때 스크린에서 한 번 쯤 봤을 로그들이다.


[    0.191970] ... value mask:             0000ffffffffffff

[    0.191971] ... max period:             000000007fffffff

[    0.191972] ... fixed-purpose events:   0

[    0.191972] ... event mask:             000000000000000f

[    0.193308] x86: Booted up 1 node, 1 CPUs

[    0.193310] smpboot: Total of 1 processors activated (5184.01 BogoMIPS)

[    0.193488] NMI watchdog: disabled (cpu0): hardware events not enabled

[    0.193489] NMI watchdog: Shutting down hard lockup detector on all cpus

[    0.193643] devtmpfs: initialized

[    0.193898] evm: security.selinux

[    0.193899] evm: security.SMACK64

[    0.193900] evm: security.SMACK64EXEC

[    0.193900] evm: security.SMACK64TRANSMUTE

[    0.193901] evm: security.SMACK64MMAP

[    0.193902] evm: security.ima

[    0.193902] evm: security.capability 


맨 처음 시작 할 때의 코드는 head.S 이다. 코드는 요기에 있다. 첫 실행 파일은 어셈블리 언어로 이뤄져 있다. 부팅에 필요한 기본적인 작업들만 수행하고나서 커널로 점프를 한다.


linux/arch/arm/boot/compressed/head.S -> ARM버전

linux/arch/x86/boot/compressed/head_32.S -> x86 32bit 버전


부팅이 정상적으로 마무리 되면, 커널은 루트파일 시스템을 mount하는 작업을 한다.


4. 루트파일 시스템에 mount 작업


마운트 하는 작업을 설명하기 전에 먼저 루트파일 시스템에 대해서 설명부터 하자. 리눅스 커널을 사용하는 OS로는 CentOS, 우분투, 레드햇, Fedora 등등 여러가지가 있다. 그러나 동일하게 리눅스 커널을 사용하기는 하는데 막상 부팅하고 보면 로그인 화면은 모두 차이가 난다. 사용하다보면 폰트도 다르고, 루트 파일의 폴더명도 다르며 패키지 관리 방식과 네트워크 설정 화면 등등 모든것이 다르다. 이것들은 모두 루트파일 시스템이 달라서 그런 것이다. 동일한 리눅스 커널을 사용한다는 것은 최하윗단에서 하드웨어의 리소스를 관리를 리눅스 코드를 사용한다는 것이지 이들의 GUI가 동일하다는 것은 아니다. 커널 소스를 제외한 껍데기(GUI, 기본 명령어, 폴더명 등등...)를 말하는 것이 루트파일 시스템이다. 


커널은 부팅과정에서 자신이 할 일을 마치면 개발자가 미리 설정해둔 루트 파일 시스템 파티션에 mount작업을 실시하고 루트 파일 시스템 실행시 필요한 작업들을 실행한다. OS 버전마다 초기에 시작하는 프로그램이 다르고 또 이들을 실행하는 방식이 다르다. 크게 init.d, systemd로 나뉜다. init.d로 설명하면 pid 1번으로 등록되는 init process가 부팅에 필요한 프로그램들을 실행시킨다.


( 루트파일 시스템 실행중 나오는 로그 )


정상적으로 마운트 되고나면 각 OS마다 제공하는 로그인화면이 뜨게 된다. 모든 부팅이 종료된 상황이다.