반응형
서 론
빌드루트, Buildroot(혹은 하드웨어 스케일에 따라서는 Yocto)라는 툴을 다루는 것은 임베디드 리눅스의 영역, 그것도 BSP(Board Support Package)의 영역이다.
분명 학부 때는 전기공학과를 다니며, 펌웨어 코딩과 자동제어를 주로 프로그래밍했었는데,
어느 새 취업을 하고 보니 임베디드 리눅스의 영역도 다루게 되고 있다.
Buildroot에 대해 다루기 전에, 임베디드 리눅스의 구조에 대해 알아볼 필요가 있다.
Buildroot라는 툴을 다루려면 최소한 그 안에서 어떤 동작을 하는지, 그 구조는 어떻게 되는지에 대해서는 알아야 하기 때문이다.
임베디드 리눅스를 구성하는 가장 중요한 요소들은 무엇일까?
그것에 대해 매우 간략히 정리해보도록 하겠다.
리눅스, Linux
Embedded Linux
- 임베디드 리눅스를 구성하는 가장 중요한 4가지 요소는 아래와 같다.
- 툴체인
- 부트로더
- 커널
- 루트 파일시스템
- Buildroot는 위 4개의 요소를 구현하기 쉽도록 도와주는 도구이다.
툴체인
- 툴체인은 소스 코드를 타깃 장치에서 실행할 수 있는 실행 파일로, 컴파일러, 링커, 런타임 라이브러리를 포함하는, 컴파일 도구의 집합이다.
- 리눅스 시스템의 나머지 3요소(부트로더, 커널, 루트파일시스템)를 빌드하기 위해 툴체인이 필요하다.
- 표준 GNU 툴체인
- Binutils: http://www.gnu.org/software/binutils/
- GCC: http://gcc.gnu.org/
- C Library:
- glibc: http://www.gnu.org/software/libc
- 대다수의 경우
- musl libc: https://www.musl-libc.org
- 저장소가 32MiB 이하일 때
- uClibc-ng: https://uclibc-ng.org/
- uClinux를 사용할 때 씀
- 저장소가 32MiB 이하일 때
- eglibc: http://www.eglibc.org/home
- glibc: http://www.gnu.org/software/libc
- 툴체인의 종류
- 네이티브: 툴체인이 만들어내는 프로그램과 같은 종류의 시스템, 때로는 실제로 같은 시스템에서 실행된다. 데스크톱과 서버에서는 일반적인 경우
- 크로스: 툴체인이 타깃 기계와 다른 종류의 시스템에서 실행된다. 빠른 데스크톱 PC에서 개발한 다음 임베디드 장치에 로드해서 테스트.
- CPU Architecture
- CPU Architecture: ARM, MIPS, x86_64
- Endian: Big Endian, Little Endian
- Fixed Float Point Support
- ABI: OABI → EABI → EABIHF
부트로더
- 부트로더는 시스템을 Basic Level로 초기화하고 커널을 로드하는 두 가지 주요 작업을 실행한다.
- 부트 순서
- 롬 코드
- 리셋이나 전원을 켠 직후에 실행되는 코드는 SoC의 칩상에 저장돼야 한다.
- 이는 롬 코드(ROM Code)라고 불린다.
- 롬 코드는 제조 시에 칩에 프로그램되므로, 비공개이며 오픈소스 대용품으로는 대체할 수 없다.
- DRAM 구성은 장치 별로 다르기에, 메모리 컨트롤러를 초기화하는 코드는 보통 담고 있지 않으므로, 메모리 컨트롤러가 필요 없는 SRAM만 사용할 수 있다.
- 1단계가 끝날 때면, SPL이 SRAM에 존재하고 롬 코드는 SPL 코드의 시작으로 점프한다.
- Secondary Program Loader (SPL)
- 메모리 컨트롤러와 기타 Tertiary Program Loader(TPL)을 DRAM에 로드하기 위해 필요한 시스템의 필수적인 부분들을 시작해야 한다.
- SPL의 기능은 크기로 인해 제한된다.
- 롬 코드처럼 다시 한 번 사전에 프로그램된 플래시 저장 장치 시작부터의 오프셋을 이용해 일련의 저장 장치로부터 프로그램을 읽을 수 있다.
- SPL에 파일시스템 드라이버가 내장되어 있다면, 디스크 파티션에서 u-boot.img처럼 잘 알려진 파일 이름을 읽을 수 있다.
- SPL은 제조사가 바이너리로 제공하는 비공개 코드인 것이 일반적. (TI, Atmel 제외)
- 2단계가 끝날 때면, 3단계 로더가 DRAM에 존재하고 SPL은 그 영역으로 점프할 수 있다.
- Tertiary Program Loader (TPL)
- U-Boot나 Barebox 같은 완전한 부트로더 실행
- 새로운 부트, 커널 이미지를 플래시 저장소에 로드하고, 커널을 로드하고 부팅하는 등의 유지보수 작업을 수행할 수 있게 하는 간단한 CLI가 있고, 사용자 개입없이 커널을 자동으로 로드하는 방법도 있음.
- 3단계가 끝날 때면, 커널이 메모리에서 시작되길 기다리고 있다.
- 임베디드 부트로더는 보통 일단 커널이 실행되면 메모리에서 사라지고 시스템의 동작에 더 이상 관여하지 않는다.
- 롬 코드
- 예시) UEFI 펌웨어로 부팅
- 부트 순서
- 프로세서가 플래시 메모리로부터 플랫폼 초기화 펌웨어를 로드한다.
- 플랫폼 초기화 펌웨어는 SPL의 역할을 수행한다. 로컬 디스크상의 EFI System Partition(ESP)나 PXE 부트를 통해 네트워크 서버에서 EFI 부트 관리자를 로드할 수 있도록 DRAM 컨트롤러와 기타 인터페이스를 초기화한다.
- UEFI 부트 관리자가 TPL이다. 이 경우 TPL은 리눅스 커널과 램 디스크를 메모리에 로드할 수 있는 부트로더여야 한다. 일반적으로 다음 둘 중 하나를 선택한다.
- systemd-boot: 예전에는 gummiboot라고 불렀다. 간단한 UEFI 호환 부트로더. (https://www.freedesktop.org/wiki/Software/systemd/systemd-boot/)
- Tummiboot: 'trusted boot'(인텔의 Trusted Execution Technology(TEX))가 지원되는 gummiboot
- 부트 순서
- 부트로더가 제어를 커널로 넘길 때 넘겨야 하는 정보
- 장치 트리가 지원되지 않는 파워PC와 ARM 플랫폼에서 SoC의 종류를 식별하기 위해 사용하는 기계 번호 (machine number)
- 이제까지 발견된 하드웨어의 기본적인 세부사항 (최소한 물리적인 램의 크기와 위치, CPU 클럭 속도 등을 포함)
- 커널 커맨드라인
- 장치 트리 바이너리의 위치와 크기
- 초기 램 디스크(initramfs 초기 램 파일시스템)의 위치와 크기
장치 트리
- 장치 트리는 컴퓨터 시스템의 하드웨어 요소를 정의하는 유연한 방법이다.
- 부트로더가 로드해서 커널에 넘기는 것
- 장치 트리를 따로 로드할 수 없는 부트로더를 위해 커널 이미지 자체에 포함시킬 수도 있긴 함
부트로더 선택
Name | Supported Architecture |
Das U-Boot | ARC, ARM, Blackfin, Microblaze, MIPS, Nios2, OpenRiec, 파워PC, SH |
Barebox | ARM, Blackfin, MIPS, Nios2, 파워PC |
GRUB 2 | x86, x86_64 |
Little Kernel | ARM |
RedBoot | ARM, MIPS, 파워PC, SH |
CFE | Broadcom MIPS |
YAMON | MIPS |
커널
- 커널은 자원 관리와 하드웨어 인터페이스를 담당하므로 최종 소프트웨어 빌드의 거의 모든 측면에 영향을 주는 요소다.
- 커널은 특정 하드웨어 구성에 맞춰지는데, 장치 트리를 이용하면, 일반적인 커널을 만들고 장치 트리의 내용을 통해 특정하드웨어에 맞춰지도록 할 수도 있다.
- 커널은 자원을 관리하고, 하드웨어와 인터페이스하고, 사용자 공간 프로그램에게 유용한 수준의 추상화를 제공하는 API를 제공한다.
- Kconfig: 구성 메커니즘을 Kconfig라 하며, Kconfig에 통합된 빌드 시스템을 Kbuild라 한다.
커널 소스 청소
- clean: 오브젝트 파일과 대부분의 중간 파일을 제거한다.
- mrproper: 모든 중간 파일(.config 파일 포함)을 제거한다. 이 타깃을 이용하면 소스 트리를 클론하거나 압축 해제한 직후의 상태로 되돌릴 수 있다.
- distclean: mrproper와 같지만 편집기 백업 파일과 패치 파일, 기타 소프트웨어 개발 부산물들도 지운다.
커널 작동 순서
- 커널 초기화에서 사용자 공간으로 이행하기 위해, 커널은 루트 파일시스템을 마운트하고, 루트 파일시스템에 있는 프로그램을 실행한다. 이는 램디스크를 통하거나 블록 장치상의 실제 파일시스템을 마운트함으로써 이뤄진다.
- 위를 처리하는 모든 코드는 init/main.c에 있고, 함수 rest_init()에서 시작한다.
- rest_init()함수는 PID 1인 첫 번째 thread를 만들고, kernel_init() 코드를 실행한다.
- 램디스크가 있다면, 프로그램 /init을 실행하려고 할 것이고, 이 프로그램은 계속해서 사용자 공간을 설정하는 작업을 수행한다.
- /init을 찾아서 실행하는 데에 실패하면, init/do_mounts.c 안의 함수 prepare_namespace()를 불러 파일시스템을 마운트하려고 할 것이다.
- 이를 위해서는 다음과 같은 root= 커맨드라인을 통해 마운트할 때 쓸 블록 장치의 이름을 제공해야 한다.
- root=/dev/<disk_name><partition_number>
- root=/dev/<disk_name>p<partition_number>
- 예시) root=/dev/mmcblk0p1
- 성공한다면, /sbin/init → /etc/init → bin/init → /bin/sh를 성공할 때까지 차례로 실행을 시도한다.
- 램디스크의 경우 rdinit=을, 파일시스템의 경우 init=을 사용한다.
커널 메시지
Warning Msg | Value | Description |
KERN_EMERG | 0 | 시스템이 사용 불능 |
KERN_ALERT | 1 | 즉시 조치를 취해야함 |
KERN_CRIT | 2 | 위급 상태 |
KERN_ERR | 3 | 에러 상태 |
KERN_WARNING | 4 | 경고 상태 |
KERN_NOTICE | 5 | 정상이지만 중요한 상태 |
KERN_INFO | 6 | 정보 |
KERN_DEBUG | 7 | 디버깅 메시지 |
루트 파일시스템
- 커널은 부트로더로부터 포인터로 전달된 initramfs나, root= 매개변수를 통해 커널 커맨드라인에 지정된 블록 장치를 마운트함으로써 루트 파일시스템을 구한다.
- 최소한의 루트 파일시스템을 만들기 위해서는 다음 요소들이 필요하다.
- init: 일련의 스크립트를 실행함으로써 모든 것을 시작시키는 프로그램
- shell: init과 기타 프로그램이 호출하는 shell 스크립트를 실행하기 위해 필요
- daemon: daemon은 다른 프로그램에게 서비스를 제공하는 백그라운드 프로그램이다. syslogd(system log daemon)와 sshd(secure shell daemon)가 있다. init 자체도 데몬으로, 다른 데몬들을 시작하는 서비스를 제공한다.
- 공유 라이브러리: 대부분의 프로그램들은 공유 라이브러리와 링크되는데, 이들 라이브러리는 루트 파일시스템에 존재해야한다.
- 구성 파일: init과 기타 데몬용 구성 파일들은 일련의 텍스트 파일로, 보통 /etc 디렉토리에 저장된다.
- 장치 노드: 다양한 장치 드라이버에 접근할 수 있게 해주는 특수 파일들
- /proc과 /sys: 커널 자료 구조를 디렉토리와 파일의 계층구조로 나타내는 2개의 가상 파일시스템. 여러 프로그램과 라이브러리 함수들이 proc과 sys에 의존한다.
- 커널 모듈: 커널의 일부를 모듈로 구성했다면, 루트 파일시스템(보통 /lib/modules/[kernel_version])에 설치되어야 한다.
- 디렉토리 레이아웃
- /bin: 모든 사용자에게 필수적인 프로그램들
- /dev: 장치 노드와 기타 특수 파일들
- /etc: 시스템 구성
- /lib: 필수 공유 라이브러리 (예: C 라이브러리를 이루는 것들)-
- /proc: proc 파일 시스템
- /sbin: 시스템 관리자에게 필수적인 프로그램들
- /sys: sysfs 파일시스템
- /tmp: 임시 파일이나 휘발성 파일들을 담아두는 곳
- /usr: /usr/bin, /usr/lib, /usr/sbin에 각각 추가 프로그램, 라이브러리, 시스템 관리 유틸리티가 담겨있다. 시스템을 부팅할 때 필요한 것을 담고 있어서는 안 된다.
- /var: 실행 중 변경될 수도 있는 파일과 디렉토리(예: 로그 메시지, 그 중 일부는 부트 뒤에도 남아있어야함)를 담고 있다.
- 400 r - - - - - - - - ─┐
- 200 - w - - - - - - - ├── 소유자 권한
- 100 - - x - - - - - - ─┘
- 040 - - - r - - - - - ─┐
- 020 - - - - w - - - - ├── 그룹 권한
- 010 - - - - - x - - - ─┘
- 004 - - - - - - r - - ─┐
- 002 - - - - - - - w - ├── 세계 권한
- 001 - - - - - - - - x ─┘
- POSIX 파일 접근 권한
- 이 때, r은 읽기 권한, w는 쓰기 권한, x는 실행 권한을 나타낸다.
- 이밖에도 특별한 의미가 있는 3비트는 아래와 같다.
- SUID(4): 파일이 실행 가능하면, 프로그램이 실행될 때 프로세스의 유효 UID를 파일 소유자의 ID로 바꾼다.
- SGID(2): SUID와 유사하게, 파일이 실행 가능하면, 프로세스의 유효 GID를 파일의 그룹 ID로 바꾼다.
- Sticky(1): 디렉토리에서 삭제를 제한해 다른 사용자 소유의 파일을 지울 수 없게 한다. 이는 보통 /tmp와 /var/tmp에 설정되어있다.
RYO (Roll Your Own)
- 임베디드 리눅스 초기에 루트 파일시스템을 만드는 유일한 방법
- 현재는 램이나 저장소의 크기가 매우 제한적이거나, 빠른 시연을 위해서나, 요구사항이 표준 빌드 시스템 도구로 충족되지 않는 모든 경우에 사용한다.
반응형
'IT > 임베디드 IoT' 카테고리의 다른 글
[통신 이론] MQTT, MQTT Protocol (MQTT 프로토콜) 이란? - 2 (실전편) (0) | 2020.01.16 |
---|---|
[통신 이론] MQTT, MQTT Protocol (MQTT 프로토콜) 이란? - 1 (이론편) (3) | 2020.01.13 |