소프트웨어에 요구되는 다양한 기능을 처음부터 개발한다면 기간과 비용 모두 막대하게 소비된다. 이 문제를 해결하기 위해서는 임베디드 시스템의 개발 프로세스를 개선해 소프트웨어를 전부 다 만드는 것이 아니라 사용 가능한 소프트웨어 부품을 사용해서 개발 기간 및 비용을 절감하는 것이 바람직하다.
리눅스 운영체제는 무료로 이용할 수 있고 풍부한 미들웨어 및 장치 드라이버가 있기 때문에 임베디드 제품 개발에 이용되고 있아.
임베디드 시스템은 최소한의 CPU, ROM, RAM 이 있으면 소프트웨어를 동작시킬 수 있다. 리눅스의 경우 추가적으로 메모리를 가상화하는 MMU (Memory Management Unit)
가 있는 것이 바람직하다.
MMU (Memory Management Unit)
가상으로 취급하는 주소 공간과 물리적으로 취급하는 주소 공간
이 있다. 가상주소 공간은 CPU가 다룰 수 있는 32비트 공간이다. 물리주소 공간은 실제로 하드웨어가 장착한 메모리 용량을 취급하는 공간이다.더 자세한 사항은 필자가 작성한 다음의 글을 참고하길 바람.
리눅스 운영체제는 소프트웨어에서 최대로 사용할 수 있는 메모리 공간인 4GB 중 3GB 를 애플리케이션이 동작하는 공간으로 할당하고, 1GB 는 전체를 관리하는 소프트웨어인 커널 공간으로 할당한다.
/dev
인터페이스를 제공한다. 그리고 이 인터페이스들은 POSIX 호환 API 로 취급하도록 되어 있다./sys
)은 커널이 제공하는 추상 인터페이스를 더욱 편리하게 이용하기 위한 라이브러리, 프로세스간 통신을 위한 라이브러리 (IPC
), 기동 순서를 제어하는 데몬(daemon) 이라고 부르는 상주 소프트웨어, GUI 를 제어하는 윈도우 관리자 등으로 구성된다.자세한 사항은 필자가 작성한 다음의 글을 참고하길 바란다.
리눅스 운영체제는 실행 프로그램을 프로세스 (Process)
라는 단위로 관리한다. (더 정확하게는 태스크) 하나의 프로세서는 가상 공간에 하나의 메모리 공간을 가지고 있으며, 서로 다른 프로세스 간의 변수에는 접근할 수 없다. 프로세스는 PID (Process ID)라는 번호로 관리된다. (커널 내부에서는 tgid(Thread Group ID) 로 관리됨.)
더 자세한 사항은 필자가 작성한 다음의 글을 참고하길 바란다.
프로세스 안의 처리 단위로 잘게 나눈 것이 쓰레드(Thread)다. 개념적으로 프로세스와 동일하지만, 쓰레드를 사용함으로써 프로그램 처리의 병렬성을 향상시킬 수 있다. 프로세스와의 큰 차이는 부모-자식 관계가 없이 생성한 프로세스 내에서 실행 된다는 점이다.
사용자 공간의 각 프로세스는 서로 다른 가상 공간의 메모리에서 동작하며, 서로의 메모리 공간에 액세스 할 수 없다. 프로세스끼리 메모리 간의 액세스와 처리를 의뢰할 때에는 IPC (Inter Process Communication)
라는 프로세스 간 통신을 사용한다.
IPC 와 관련된 더 자세한 사항은 Advanced Programming in the UNIX Environment 3/e
이라는 책을 읽기 바란다.
시스템 콜 인터페이스
를 사용자 공간에 제공한다.시스템 콜 인터페이스: 커널이 지닌 기능을 사용자 공간의 프로세스에서 사용할 수 있도록 제공하는 인터페이스다.
리눅스의 장치 드라이버는 커널의 최하층, 즉 하드웨어에 가장 가까운 부분에 위치한다. 장치 드라이버에는 캐릭터형 장치
와 블록형 장치
, 네트워크형 장치
가 있다.
장치 드라이버 명 | 내용 | 특징 |
---|---|---|
캐릭터형 장치 | 바이트 스트림을 취급해 순차적으로 읽고 쓰는 장치. 대표적인 장치는 디스플레이나 프린터, USB 시리얼 등이 있다. | 순차적인 액세스이기 때문에 하드디스크 등의 제어에는 적합하지 않다. |
블록형 장치 | 고정된 크기의 블록을 랜덤으로 읽고 쓰는 장치. 대표적인 장치로는 하드디스크나 CD 드라이브, SD 카드 등이 있다. | 데이터 크기는 고정 길이 버퍼를 사용하기 때문에 CPU와 속도 차이가 있는 장치의 경우 장점이 있다. |
네트워크형 장치 | 소켓 인터페이스를 통해 통신하는 장치. 대표적 장치는 이더넷 및 와이파이 등이 있다. | 프로토콜 스택의 제어에 의존한다. 데이터 수신 시, 인터럽트가 발생한다. |
시스템 콜 인터페이스는 사용자 공간에서 실행되는 프로세스나 라이브러리가 커널의 기능을 사용할 경우의 인터페이스다.
장치 파일
은 시스템 콜을 사용하여 장치 드라이버에 액세스하는데 사용한다. 파일 형식으로 되어 있으며, 커널이 장치별로 종류와 번호를 관리한다./dev
의 아리에 위치하며, 장치 드라이버를 초기화 했을 때 파일이 생성된다. 원칙적으로는 하나의 장치에 하나의 파일이 생성된다./sys
아래에 위치하며, 장치 드라이버를 초기화했을 때 파일을 생성한다. /proc
은 사용자 공간의 프로세스 상태 및 커널의 설정 상태, 하드웨어 상태 등 다양한 상태를 확인할 수 있는 디렉토리이다.
책에서는 Yocto Linux
를 통해 생성한 이미지 파일을 Raspberry Pi 3B+
모델에서 실행시키는 과정을 보여주는데, 생략된 부분이 많고 과정 자체가 구식이기에 필자는 입맛대로 최신 문서를 읽고 메뉴얼대로 진행했다.
https://velog.io/@mythos/Yocto-Linux-Quick-Build-for-Raspberry-Pi-3B-Fedora-35
위 링크는 Fedora 35
환경에서 Yocto Linux
의 meta-raspberrypi
를 통해 Raspberry Pi 3B+
모델에서 실행시킬 이미지 파일을 생성하는 과정을 풀어서 작성한 글이다.
책에서는 USB to Serial
을 통해 보드에 접속하는데, 필자는 그냥 HDMI
케이블을 연결해서 접속했다.
local.conf
을 아래와 같이 수정하여 커널 드라이버를 자체 환경에서 빌드할 수 있게 만든다:
EXTRA_IMAGE_FEATURES ?= "debug-tweaks tools-sdk dev-pkgs tools-debug"
IMAGE_INSTALL:append = "kernel-devsrc git cmake"
책에서는 IMAGE_INSTALL_append
로 나오지만 최신 버전에는 다음과 같이 문법이 변경되었다.
bitbake core-image-base
cd tmp/deploy/images/raspberrypi3-64
sudo bmaptool copy <image name>.rootfs.wic.bz2 --bmap <image name>.rootfs.wic.bmap <device name>
위 명령어를 순차적으로 입력하여 이미지파일을 microSD 에 기록한다. 빌드가 완료되었다면 파이에 접속하여 (유저명 root
입력하여 접속) date
명령을 입력하여 날짜와 시간을 변경한다:
date -s "2022-10-01 23:55:00"
이후 다음의 명령을 입력하여 커널을 빌드한다. 책에서는 archheaders
로 되어있으나, make archheaders
를 입력해도 해당 target
에 대한 build
가 제대로 이뤄지지 않는다. 필자는 대신 archheaders
를 dependancies
로 포함하고 있는 headers
를 빌드하였다.
cd /lib/moudules/$(uname -r)/build/
make prepare
make headers
make scripts
Hello World
드라이버 작성하기
vi
를 통해 다음과 같은 코드를 작성한다. 위 코드는 커널에 모듈로 삽입되어질 때 hello, world!
를, 제거되어질 때 goodbye~
를 출력하는 모듈이다.
다음과 같이 Makefile
을 작성한 뒤 make
명령을 이용하여 빌드를 수행한다. 책에서는 CC=/usr/bin/aarch64-poky-linux-gcc
의 구문이 따로 없으나 다음 구문이 없으면 gcc
와 aarch64-poky-linux-gcc
가 동일한 파일(심볼릭 링크로 연결되어 있음)임에도 불구하고 서로 다른 컴파일러로 취급하여 빌드가 수행되지 않는다. 따라서 필자는 위와 같은 방식으로 CC
를 직접 명시해주었다.
빌드가 성공적으로 수행되었다면 hello.ko
라는 이름의 파일이 생성되는 것을 확인할 수 있다.
insmod
명령과 rmmod
명령을 입력하여 커널에 모듈을 삽입하고 또 제거할 수 있다.
외부에서 파일을 다운로드 받기 위해 다음과 같이 LAN
케이블을 보드에 연결한 뒤 보드에 전원을 인가했다.
정상적으로 부팅이 되었다면 ifconfig
명령어를 입력하여 제대로 연결이 되었는지 확인한다. 책에서는 네트워크 설정 파일을 만져서 직접 인터페이스를 생성하는데 필자는 처음부터 자동으로 연결이 되어 있어서 따로 만지지 않고 진행했다.
wget
명령을 입력하여 공식 github
에서 wiringPi
라이브러리 파일을 다운로드 받는다. 책에서는 git://git:drogon.net/wiringPi
를 이용하는데 이는 정말 deprecated
된, 또 더 이상 사용이 불가능한 주소이므로... 업데이트가 이뤄지는 비공식 github
주소를 이용하길 바란다:
wget https://github.com/WiringPi/WiringPi/archive/refs/tags/2.61-1.zip
unzip 2.61-1.zip
다운로드가 끝났다면 압축을 풀고 build
파일을 vi
로 열어 다음과 같이 수정한다.
build
파일 중간에 sudo=${WIRINGPI_SUDO-sudo}
라는 구문이 나오는데 이를 주석처리 한다. (Custom Yocto Linux 에는 sudo
라는 이름의 명령어가 없어서 에러가 발생한다.) 이제 다음의 명령어를 입력하여 빌드에 필요한 디렉토리를 생성하고 빌드를 진행한다:
mkdir -p /usr/local/bin
mkdir -p /usr/local/include
mkdir -p /usr/local/lib
./build
빌드가 정상적으로 수행되었다면 All Done.
이라는 메세지가 프롬프트되면서 스크립트가 종료된다.
이제 다음과 같은 코드를 작성하고 컴파일하여 실행한 뒤 정상적으로 GPIO
에 대한 제어가 이뤄지는지 확인한다:
gcc led.c -I/usr/local/include -L/usr/local/lib -lwiringPi
책에는 GPIO
직접 조작 방법 장치 드라이버 작성하는 법 등이 기술되어 있으나 필자는 귀찮아서 생략했다.
[책] 임베디드 엔지니어 교과서 (와타나베 노보루, 마키노 신지 지음, 정인식 옮김)