임베디드 시스템 개발에서 타겟 보드에 맞는 맞춤형 리눅스 커널을 직접 빌드하고 포팅하는 과정은 초기 환경 구축 단계에서 가장 까다로운 작업 중 하나입니다. 하드웨어의 성능을 100% 이끌어내고 불필요한 자원 낭비를 줄이기 위해서는 호스트 PC에서 타겟 아키텍처에 맞는 교차 개발 환경을 올바르게 설정하는 것이 무엇보다 중요합니다. 이번 포스팅에서는 우분투 환경을 기준으로 64비트 ARM(ARM64) 프로세서 타겟의 리눅스 커널 소스 코드 다운로드부터 크로스 컴파일러 설정, menuconfig를 활용한 커널 구성, 그리고 최종 이미지 및 디바이스 트리(DTB) 파일 빌드까지의 실무 과정을 단계별로 알기 쉽게 정리해 보았습니다.

핵심 요약 3줄
- 호스트 PC에서 ARM64 타겟 바이너리를 생성하기 위해 필수 개발 패키지와 크로스 컴파일러 툴체인을 먼저 설치합니다.
- 환경 변수(ARCH, CROSS_COMPILE)를 설정한 후 타겟 보드의 기본 설정을 로드하고 menuconfig로 필요한 드라이버를 커스터마이징합니다.
- 컴파일을 통해 생성된 최종 커널 이미지(Image)와 하드웨어 명세서인 디바이스 트리 블롭(DTB) 파일을 타겟 보드로 전송하여 포팅을 완료합니다.
1. 리눅스 커널 빌드를 위한 호스트 PC 환경 구축
임베디드 리눅스 커널을 컴파일하려면 호스트 시스템(Ubuntu)에 소스 코드를 해석하고 빌드할 수 있는 도구들이 먼저 갖춰져야 합니다.
1.1 필수 개발 패키지 설치
터미널을 열고 다음 명령어를 입력하여 빌드 프로세스에 필요한 필수 도구와 라이브러리를 설치합니다. 이 패키지들은 커널 컴파일 과정에서 스크립트 실행, 암호화 처리, 구성 메뉴 출력 등에 사용됩니다.
sudo apt update
sudo apt install -y build-essential libncurses-dev bison flex libssl-dev libelf-dev
1.2 크로스 컴파일러(Cross-Compiler) 설치 및 확인
호스트 PC(x86_64)에서 타겟 보드(ARM64)용 실행 파일을 만들어내기 위해 교차 컴파일러를 설치합니다. GNU 프로젝트에서 제공하는 64비트 ARM용 툴체인을 사용합니다.
# ARM64용 GCC 및 G++ 컴파일러 설치
sudo apt install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# 설치 완료 후 버전 확인을 통한 정상 작동 여부 체크
aarch64-linux-gnu-gcc --version
2. 리눅스 커널 소스 코드 확보 방법
커널 소스 코드는 개발 목적에 따라 공식 안정화 버전을 압축 파일로 다운로드하거나, Git 저장소를 통해 특정 버전을 체크아웃하여 관리할 수 있습니다. 여기서는 실무에서 가장 많이 쓰이는 두 가지 방법을 모두 정리해 드립니다.
| 다운로드 방식 | 실행 명령어 예시 | 특징 및 장점 |
| Tarball 압축 파일 다운로드 | mkdir ~/linux-kernel && cd ~/linux-kernel wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.5.tar.xz tar -xvf linux-6.5.tar.xz && cd linux-6.5 |
특정 버전을 빠르게 다운로드하여 단발성으로 빌드할 때 유리하며, 저장소 히스토리가 없어 디스크 용량을 적게 차지합니다. |
| Git 저장소 클론 (Clone) | git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git cd linux-stable git checkout v6.5 |
최신 패치 내역을 유지할 수 있고, 개발 중 발생한 변경 사항 관리나 커스텀 패치 적용 및 버전 전환이 매우 용이합니다. |
3. 환경 변수 설정 및 하드웨어 맞춤형 환경 구성
빌드 명령어를 입력할 때마다 아키텍처와 컴파일러 경로를 매번 지정하는 번거로움을 줄이기 위해 호스트 세션에 환경 변수를 먼저 등록합니다.
3.1 교차 빌드 환경 변수 선언
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
Tip: 해당 세션이 닫히면 변수가 초기화되므로, 지속적으로 사용하려면 ~/.bashrc 파일 하단에 위 두 줄을 추가해 두는 것이 편리합니다.
3.2 기본 설정(Default Configuration) 로드
임베디드 시스템은 범용 PC와 달리 보드마다 하드웨어 구성이 제각각입니다. 소스 패키지 내부(arch/arm64/configs/)에 미리 정의된 타겟 제조사의 기본 설정 파일을 불러와 컴파일의 기준점을 잡습니다.
make rockchip_defconfig
(주의: 사용하는 SoC 제조사 아키텍처에 따라 generic_defconfig, sunxi_defconfig 등 실제 제공되는 기본 설정 파일명을 입력해야 합니다.)
3.3 menuconfig를 이용한 커널 상세 커스터마이징
기본 설정을 기반으로 시스템에 필요한 드라이버를 추가하거나 불필요한 모듈을 제거하고 싶을 때는 텍스트 기반의 그래픽 메뉴 창을 띄워 편집할 수 있습니다.
make menuconfig
이 명령어를 실행하면 터미널 화면에 파란색 메뉴 창이 나타나며, 방향키와 스페이스바를 이용해 네트워크 스택 활성화, 특정 센서 드라이버 포함 여부(Built-in 또는 Module) 등을 세부적으로 튜닝할 수 있습니다.
4. 커널 및 디바이스 트리(DTB) 컴파일과 추출
설정이 완료되었다면 실제 타겟 보드에 탑재할 바이너리 파일들을 빌드할 차례입니다.
4.1 핵심 컴포넌트 빌드 프로세스
컴파일을 마치고 나면 보드 부팅에 필요한 핵심 파일 두 가지가 지정된 경로에 생성됩니다.
| 빌드 대상 | 실행 명령어 | 최종 생성 파일 경로 및 설명 |
| 리눅스 커널 이미지 | make -j$(nproc) | arch/arm64/boot/Image 하드웨어를 제어하고 시스템을 구동하는 순수 커널 바이너리 본체입니다. |
| 디바이스 트리 블롭 (DTB) | make dtbs | arch/arm64/boot/dts/제조사명/*.dtb 하드웨어 장치들의 핀 정보와 주소 맵을 커널에 전달하는 명세서 파일입니다. |
4.2 빌드 파일 출력 및 타겟 전송 준비
개발 과정에서 호스트 PC와 타겟 보드 간의 빠른 테스트를 위해 주로 TFTP 서버나 USB 스토리지를 활용하여 파일을 공유합니다. 아래는 빌드가 완료된 결과물들을 호스트 PC의 TFTP 공유 디렉터리로 내보내는 명령어 예시입니다.
# TFTP 서버 디렉터리로 커널 이미지 복사
sudo cp arch/arm64/boot/Image /tftpboot
# 타겟 보드에 해당하는 디바이스 트리 파일 복사 (실제 칩셋 명칭의 dtb 지정)
sudo cp arch/arm64/boot/dts/rockchip/*.dtb /tftpboot
개발을 위한 팁 (Tips)
- 환경 변수 영구 등록 활용하기: 터미널을 새로 열 때마다 export 명령어를 다시 입력하는 게 귀찮다면, 사용자의 홈 디렉터리에 있는 ~/.bashrc 파일 맨 아래에 환경 변수를 적어두고 source ~/.bashrc를 실행해 주면 터미널을 열 때마다 자동 적용됩니다.
- menuconfig 검색 기능 사용하기: 커널 옵션이 너무 많아 내가 원하는 드라이버 설정 위치를 찾기 어려울 때는 menuconfig 화면에서 슬래시(/) 키를 누른 뒤 찾고자 하는 키워드(예: USB_SERIAL)를 입력하면 해당 옵션의 정확한 메뉴 경로와 의존성을 바로 확인할 수 있습니다.
- 부분 빌드로 시간 아끼기: 커널 소스 코드 전체를 건드리지 않고 오직 디바이스 트리 파일(*.dts) 소스만 수정한 경우라면, 전체 컴파일을 돌릴 필요 없이 make dtbs 명령만 따로 내려 몇 초 만에 결과물을 갱신할 수 있습니다.
흔히 하는 실수 (Common Mistakes)
- 필수 패키지 누락으로 인한 빌드 중단: 빌드 도중 fatal error: ncurses.h: No such file or directory 또는 flex: command not found 같은 에러 메시지가 나오면서 멈춘다면 호스트 PC 환경 구축 단계에서 필수 라이브러리가 빠진 것입니다. 1.1 절의 패키지 설치 명령을 다시 확인하고 실행해야 합니다.
- 의존성 무시로 인한 드라이버 미동작: menuconfig에서 특정 장치 드라이버를 활성화하려고 해도 메뉴에 보이지 않거나 동작하지 않는 경우가 있습니다. 이는 해당 드라이버가 작동하기 위해 선행되어야 하는 상위 버스 구동 옵션(예: CONFIG_PCI 또는 CONFIG_I2C)이 먼저 켜져 있지 않아서 발생하는 현상이므로 검색 기능으로 의존성을 먼저 확인해야 합니다.
- 호스트 PC 용량 부족 에러: 리눅스 커널 소스는 압축을 풀고 컴파일을 완료하면 수 기가바이트(GB)에서 수십 기가바이트까지 용량이 늘어납니다. 가상머신(VM) 환경에서 개발을 진행하는 경우, 디스크 할당 용량이 부족하면 빌드 마무리에 파일 쓰기 에러가 나면서 실패할 수 있으니 사전에 충분한 스토리지 공간을 확보해 두어야 합니다.
맺음말
이번 포스팅에서는 교차 개발 환경을 직접 다루며 소스 코드를 빌드하고 타겟 보드에 올릴 준비를 하는 실전 과정을 다루었습니다. 패키지 설치부터 시작해 크로스 컴파일러 지정, 하드웨어 설정을 거쳐 최종 Image와 DTB 파일을 손에 쥐기까지의 흐름을 이해하셨다면 임베디드 리눅스 엔지니어로서의 기초 체력을 단단히 다지신 셈입니다. 다음 단계는 이렇게 빌드된 파일들을 부트로더 환경에서 메모리로 로드해 실제 하드웨어를 깨우는 포팅 실습입니다. 오늘 정리해 드린 빌드 파이프라인을 바탕으로 유연하고 강력한 임베디드 시스템 개발 환경을 직접 구축해 보시길 바랍니다.
'Embedded System > Linux Kernel & Device Drivers' 카테고리의 다른 글
| 임베디드 리눅스 BSP 개념과 개발 공정: 수정부터 신규 작성까지 (0) | 2025.03.12 |
|---|---|
| 임베디드 리눅스 커널 포팅 절차 총정리: 환경 구축부터 빌드까지 (0) | 2025.03.11 |
| 임베디드 리눅스 디바이스 트리(Device Tree) 구조와 작성법 총정리 (0) | 2025.03.10 |
| 임베디드 리눅스 부팅 과정 완벽 가이드: BootROM부터 Rootfs까지 (0) | 2025.03.09 |
| 임베디드 리눅스 커널 구조 이해와 ARM64 빌드 실무 가이드 (0) | 2025.03.07 |