본문 바로가기
나머지/ETC

부트로더 교육

by 무늬만학생 2012. 4. 9.
반응형

퍼온글..

출처 : http://noaa.byus.net/blog/144


강사: 고현철


회사에서 2008년 7월경에 받은 교육이다. MDS에서 주관하였고 교육업계(?)에 종사하는 지인의 말에 의하면 고현철님은 상당한 실력자이고 현재 프리랜서로 활동중이란다.

고현철님은 http://www.aesop-embedded.org/, http://kelp.or.kr/ 와 같은 사이트에서 '고도리'라는 필명으로 활동한다.


이번 교육은 일주일간 주로 리눅스 포팅에 관련한 내용이었으며 지금까지 내가 받은 리눅스 관련 교육중에서 매우 유익하였다. 그리고 현재 우리 팀에서 리눅스로 첫 과제를 진행중이어서 교육 효과가 더욱 컸던거 같다. 

간단하게 기억나는 것들을 정리해 본다. 



* 임베디드 리눅스 부팅의 3가지 조건


1) 부트로더 (통상 ARM/PPC 계열은 u-boot)

: 개발시에는 u-boot를 사용하지만 상용화 때에는 small bootloader를 사용함 (GPL 이슈도 있어서 u-boot를 상용으로 사용하는데 일부 제약이 있음)

=> 우리 팀에서도 리눅스용 부트로더를 새로 작성중이다. 왜냐하면 부트로더에서 파일시스템에서 접근하는 코드가 있는데 이게 공개 불가 코드이기 때문에 u-boot를 사용할 수 가 없다.


2) 커널 + 디바이스 드라이버

3) Root Filesystem (RFS)

: 왜 RFS를 사용하는가? => 유닉스 시스템에서 사용해 왔던 것이므로 어쩔 수 없음.



* 리눅스 커널 2.6

: 사실 리눅스 커널 교육도 받아 보았지만 영양가가 없다. 왜냐하면 우리 나라에 리눅스 커널을 잘(?) 가르칠 수 있는 강사도 별로 없고 개발자 입장에서 리눅스 커널을 잘 안다고 해도 소용이 없기 때문이다. OS는 가져다 쓰기 위해 존재하는 것이고 OS의 커널을 수정하는 일은 사실 개발 중에 있어서는 안 되기 때문이다. 리눅스로 제품을 만드는 과정에서 리눅스 커널을 잘 알 필요는 별로 없고, 대략 디바이스 드라이버를 작성하는데 필요한 정도의 수준이면 족하다. 그 이상의 커널 지식이란 사실 자기 만족이다. 


서두가 길었는데 리눅스 커널 2.6에서 알아야 할 점은 Kernel Device Model이다. 즉 디바이스 드라이버 구조가 변경된 것이다. 


2.6 커널 부팅시에 로딩되는 디바이스 드라이버는 /sys 폴더 아래에 보이게 되는데, udev 데몬이 이 정보를 기초로 /dev 폴더 아래 디바이스 파일을 자동으로 생성해 준다. 예전에 mknod 명령을 통해 수행하던 작업을 하지 않아도 된다는 것이다. 

그런데 udev 데몬이 부팅시에 4~5초를 소비하기 때문에 임베디드에서는 빠른 부팅을 위해 udev를 사용하지 않는게 좋다.  따라서 임베디드에서는 역시나 mknod를 통해 디바이스 파일을 생성해 주어야 한다. 어쨋거나 /sys 폴더가 생긴건 새로운 점이긴 하다. 



* 호스트 개발 환경

: PC용 어플은 PC에서 개발하지만 핸드폰 어플은 PC에서 개발한다. 핸드폰에서 컴파일러 돌리고 하려면 버벅이기 때문이다. 그러나 ARM11을 사용하는 임베디드 제품은 타겟에서 직접 개발하는 사례도 생겨나고 있다고 한다. 


아직은 주로 PC에서 개발하기 때문에 크로스 컴파일 과정이 필요하다. x86 컴퓨터에서 컴파일해서 ARM용 바이너리를 만들어 내기 때문에 이를 크로스 컴파일 과정이라고 한다. 윈도우 운영체제에서 리눅스 개발환경을 갖추기 위해 Cygwin, VMWare 등을 사용한다. 나의 팀에서는 비용 문제로 VMWare 대신 colinux를 사용중이다. (VMWare를 정품으로 사용하려면 상당히 비싸다...)


대신 이렇게 크로스 컴파일을 하는 경우에는 컴파일된 결과물(커널 이미지, 어플리케이션 프로그램 등등)을 임베디드 타겟에 옮겨야 하는데 이 작업이 번거롭다. 리눅스에서는 이런 경우 이더넷을 이용한 NFS, TFTP등을 사용한다. 대신 타겟에 이더넷이 없다면 개발이 지저분해 진다. 컴파일 할 때마다 USB를 연결해서 파일을 복사해야 되니까 말이다. 내가 만드는 제품은 이더넷이 없다. 그래서 개발이 좀 지저분하다. USB를 통한 NFS 사용방법을 생각중이긴 한데 동료들의 반응이 시원찮다.


* 크로스 컴파일러 툴체인 (ARM용 GCC)

:  왜 GCC를 사용할까? 무료료 사용할 수 있고 지원하는 CPU도 많기 때문이다. 그런데 컴파일러, 링커, 로더등을 맘에 들게 갖추는 게 쉽지 않다고 한다. 커널 버전과 GCC 버전을 잘 조합해야만 안정적으로 개발을 할 수가 있다. 왜냐하면 u-boot, 리눅스 커널, 리눅스 어플리케이션들이 각각 안정적으로 동작하는 GCC 버전이 다를 수 있기 때문이다. 따라서 크로스 컴파일러를 갖추는 작업이 어렵다고 한다. 커뮤니티에서 널리 인정된 안정 버전을 따르는게 좋다.

나의 팀에서는 codesourcery라는 회사의 툴체인을 사용중이다. 돈을 내고 쓰는 건 아니고 이 툴체인에 대한 지원을 받으려면 돈을 내야 한다고 한다. 


* MEP2440 보드

: 우리가 실습한 보드이다. S3C2440 프로세서를 가지고 이거저거 붙여서 만든 개발용 보드이다. NOR 4M, NAND 128M, SDRAM 128M, LCD, AC97 등이 붙어 있다. 

지난번 교육 글 에서 처럼 S3C2440은 NAND 부팅 또는 NOR 부팅이 가능하다. 

S3C2440은 1G address space를 가지는 데 보드는 이중에서 일부만 사용하게 된다.

왜냐하면 사용하는 메모리가 NOR 4M, SDRAM 128M이기 때문이다. 따라서 SDRAM을 2440의 메모리 뱅크 어디에 연결할 지에 따라서 메모리 주소가 달라진다. 


* 부트로더 동작 순서

( 정의: 시스템의 하드웨어를 초기화하고 운영체제의 커널을 메모리에 올려 실행시키는 시스템 프로그램)


- Exception vector init, Disable watch dog timer, disable interrupt


- clock & power 초기화 뒤에  memory bank를 초기화 한다. 

: clock은 12M나 16.9344 MHz를 사용하는데 이걸 CPU가 받아서 내부적으로 PLL을 이용해서 200M, 300M로 높인다. (외부에서 바로 높은 클럭이 들어오면 노이즈 문제가 발생하기 때문에 이런 방식으로 한다고 한다.)

: 2440은 CPU의 main clock이 분주되어서 각종 peripheral의 clock으로 사용되는데 이런 구조는 power saving 모드에 적합하지 않다. sleep 모드로 들어가면서 모든 클럭이 떨어지면 일부 주변장치의 동작이 전혀 안 되는 상황이 발생할 수 있기 때문이다. 


- u-boot relocation


- stack init

   스택 초기화 이후에는 어셈블리어 대신 C언어를 사용가능 하다. C언어는 함수 호출시에         stack 구조를 사용해야 하므로.


- BSS init

- start_armboot (C코드의 메인)


* 부트로더 동작과 메모리 배치

: u-boot는 커널을 메모리에 복사한 뒤 동작을 종료하게 되는데, 커널은 이제부터는 MMU를 사용해서 SDRAM 128M을 알아서 control 하게 된다. 


* 루트 파일 시스템 (RFS)

: 우리가 리눅스를 PC에서 사용하는 경우에 ls를 치면 나오는 각종 디렉토리들이 임베디드 리눅스에서도 필요하다.(ex. /dev, proc, etc, sbin, bin, lib, mnt, usr 등등)

리눅스라는 놈이 이러한 폴더들에 적절한 파일들이 존재하지 않으면 부팅을 하지 않기 때문이란다. 개발자 입장에서는 귀찮지만 이러한 디렉토리들을 챙겨주는 작업이 RFS를 만드는 일이다. 기본적인 것들은 busybox라는 유틸을 사용해서 만들게 된다. 


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

* fork()

: 예전에 fork를 배울때에는 이해하지 못했으나 이번에는 좀 이해를 하게 되었음.

리눅스 쉘어서 ls를 수행하면 어떻게 동작하게 되는걸까?

쉘 프로세스는 fork를 수행하여 프로세스를 복제한 뒤 parent는 child 프로세스가 종료될 때 까지 wait 동작에 들어가고 child 프로세스는 실제로 ls를 수행하게 된다. (복제 순간에 parent의 stack, data가 child 프로세스에 copy되고 code 영역에 ls의 code가 들어가게 된다는 것이 fork의 동작 원리이다.)



* EABI (Embedded Applicationi Binary Interface)

: ARM 사의 ADS 컴파일러에서는 EABI v4까지 지원하지만 gcc에서는 v1만 지원한단다.

버전이 높을 수록 생성된 바이너리가 속도도 빠르고 좋다지만 gcc에서는 아직 안전성이 검증되지 않아서 사용하지 않는게 좋단다. 

컴파일러라는게 우스워서 예전 과제에서는 컴파일러가 잘못된 기계어를 생성해서 특정 함수에서는 local 변수 대신 global변수를 써야 하는 문제가 있었다. 이런 버그는 발견하는 데만 1달은 우습게 걸린다. 당시에 gcc 컴파일러 버전을 변경할 수없는 상황이어서 이런 현상이 발생하는 함수들의 코드를 수정하는 선에게 해결했다. 


* 메모리 맵

: CPU 마다 메모리 맵이 달라서 부트로더에 이러한 정보를 코딩해야 한다. 메모리 맵은 프로세서의 데이터시트에 나와 있다. 



* 교육 후의 생각들

: 사실 예전에도 임베디드 리눅스 교육을 받았지만 이해를 잘 못했다. 결국 아는 만큼 이해하게 되는 것이라고 생각한다. 따라서 비슷한 교육도 여러번 들을 수 있다는 게 내 생각이다. 회사에선 싫어하겠지만 말이다. 

새로운 프로세서와 개발보드가 만들어 졌을 때에 보드에 적합한 부트로더를 개발하고 OS를 올리고 디바이스 드라이버를 작성하는 일은 쉬운 일은 아니다. 그래서 일부 인력들은 높은 몸값을 가질 수도 있다. 그러나 이러한 일의 수요가 그다지 많지는 않기 때문에 인력도 적다. 이러한 일은 주로 프로세서 개발 회사에서 많이 하기 때문에 프로세서를 사용하는 고객들은 업체에서 이미 개발한 것들을 가져다가 수정해서 사용한다.

하지만 이러한 과정을 전혀 모르고 상품화를 할 수는 없기 때문에 개발자로써 어느 정도의 지식은 필요하다고 생각한다. 

반응형

'나머지 > ETC' 카테고리의 다른 글

아웃룩 익스프레스6(Outlook Express)6 메시지 경로  (0) 2012.07.17
IMAP과 POP3 비교  (0) 2012.05.04
부트로더의 필요성과 종류  (0) 2012.04.09
리눅스 tgz파일 압축풀기  (0) 2012.04.09
make mrproper make distclean  (0) 2012.04.06