
1. 디바이스 드라이버
- 디바이스 드라이버 기능
- 컴퓨터 시스템에서 하드웨어는 스스로 동작할 수 없음
- 하드웨어를 제어하고 응용 프로그램과 상호 작용
- 디바이스 제어의 어려움
2. 디바이스 제어 방법
- Memory-mapped I/O와 Bit Manipulation
- Memory-mapped I/O
- 디바이스 컨트롤러는 메모리 주소 공간의 일부분에 매핑
- 특정 메모리 주소에 값을 쓰거나 읽는 방식으로 디바이스를 제어
- ioremap 함수를 사용하여 물리 주소를 가상 주소에 매핑
- Bit Manipulation
3. 인터럽트
- 인터럽트의 개념
- 하드웨어는 특정 이벤트가 발생했을 때 CPU에게 알림
- 인터럽트는 현재 실행 중인 프로세스에게 특정 사건을 알리는 메커니즘
- 인터럽트 동작 방식
- 하드웨어에서 인터럽트 신호 발생
- CPU는 현재 실행 중인 작업을 잠시 중단
- 인터럽트 핸들러 실행
- 인터럽트 처리 완료 후 중단되었던 작업 재개
- 비동기 처리
- 인터럽트는 비동기적(asynchronous) 으로 발생
- 프로세스가 실행되는 중에 언제든지 발생
- 인터럽트 핸들러
- 인터럽트 발생 시 실행되는 커널 레벨의 함수
- 프로세스 문맥이 아닌 별도의 인터럽트 문맥에서 실행
- 현재 실행 중인 프로세스와는 무관하게, 인터럽트 처리를 위한 독립적인 실행 흐름
- 인터럽트 핸들러는 디바이스 드라이버 내부에 위치
- 인터럽트 벡터 테이블
- ARM Core는 인터럽트 유형별로 실행될 핸들러의 주소를 저장하고 있는 테이블을 관리(인터럽트 벡터 테이블)
4. 리눅스 디바이스 드라이버의 구조
- 버스 컨트롤러 드라이버
- I2C, SPI, USB, PCI 등과 같은 버스 시스템을 관리
- 특정한 통신 방식을 사용하여 외부 장치와 데이터를 주고받는 역할
- 버스 서브시스템
- 버스 드라이버를 위한 AP (Application Programming Interface) 를 제공
- ex)I2C API, SPI API, PCI API, USB API
- 디바이스 드라이버
- 커널 서브시스템
- 네트워킹 스택(Ethernet, WiFi, CAN 등),
GPIO, Video4Linux(카메라, 비디오 인코더/디코더), DRM(디스플레이 컨트롤러, GPU), ALSA(오디오), IIO(ADC, DAC, 센서 등), MTD(플래시 메모리), PWM, Input(키보드, 마우스, 터치스크린), Watchdog, RTC, Remoteproc(보조 프로세서), Crypto(암호화 가속기), Hwmon(하드웨어 모니터링 센서), Block Layer(블록 스토리지) 등 특정 기능 또는 디바이스 클래스를 지원
- User-space driver
- 사용자 공간에서 실행되는 드라이버
- I2C(i2c-dev), SPI(spidev), Memory-mapped IO(UIO), USB(/dev/bus/usb - libusb 사용), PCI(sysfs 항목)
- 주의
- 커널 내부의 프레임워크 또는 서브시스템과 충돌할 가능성

5. User-space 인터페이스
- /dev 디렉토리
- 디바이스 파일을 통해 디바이스에 접근
- Character device
- Block device
- 고정된 크기의 블록 단위로 데이터를 읽고 쓰기
- open(), read(), write(), close(), ioctl() 등의 시스템 콜을 사용하여 디바이스를 제어
- 디바이스 파일은 타입(character 또는 block), 주번호(major number), 부번호(minor number).
- 주번호
- 부번호
- 동일한 종류의 디바이스 중에서 특정 디바이스를 구분
- sysfs 파일시스템
- /sys 디렉토리 아래에 파일 및 디렉토리 형태로 커널 객체(디바이스, 드라이버, 버스 등)의 정보를 제공하고, 일부 설정을 변경
- Network sockets
6. Everything is a File
- 리눅스의 철학
- 리눅스에서는 모든 것을 파일
- 응용 프로그램은 디바이스를 일반 파일(open(), read(), write(), close() 등의 시스템 콜을 사용하여 제어)
- ls -l /dev 명령을 통해 디바이스 파일의 타입, 주번호, 부번호를 확인
7. 디바이스 파일 생성
- mknod 명령(과거)
- mknod /dev/ 형식으로 디바이스 파일을 수동으로 생성
- c는 문자 디바이스, b는 블록 디바이스
- udev(현재)
- udev 데몬이 커널 이벤트를 감지하여 자동으로 디바이스 파일을 생성하고 삭제
- /lib/udev/rules.d 또는 /etc/udev/rules.d 디렉토리에 있는 규칙 파일(rules file) 에 따라 동작
- 동적으로 장치가 연결되거나 해제될 때 유용
- mdev
- udev의 임베디드 버전
- BusyBox에 포함(임베디드 시스템에서 주로 사용)
8. User-space 인터페이스 예시
- 시리얼
- /dev/ttyS, /dev/ttyUSB, /dev/ttyACM*
- GPIO
- /dev/gpiochipX (libgpiod 사용), /sys/class/gpios (레거시)
- 블록 디바이스
- /dev/sd, /dev/mmcblk, /dev/nvme*
- 디스플레이 컨트롤러 및 GPU
- 오디오
- 카메라
- Watchdog
- 입력 장치
9. sysfs 파일시스템
- /sys 디렉토리
- 커널 내부 정보를 파일 및 디렉토리 형태로 제공
- /dev가 디바이스에 접근하는 통로
- /sys는 디바이스의 속성 및 커널 내부 정보를 확인하고 설정할 수 있는 통로
- 예시
10. GPIO
- GPIO (General Purpose Input/Output)
- 범용 입출력 핀으로 디지털 신호를 제어하는 데 사용
- Legacy interface
- /sys/class/gpios 를 통해 GPIO를 제어하는 방식
- 새로운 인터페이스
11. 커널 모듈
- 커널 모듈
- 동적으로 커널에 추가 또는 제거할 수 있는 코드(디바이스 드라이버를 만드는 데 사용)
- 장점
- 커널 크기 감소
- 개발 생산성 향상
- 부팅 시간 단축
- 모듈 파일
- .ko 확장자
- /lib/modules// 디렉토리에 위치
- 메타 정보 파일
- module.dep
- module.alias
- modules.symbols
- modules.builtin
12. 모듈 로드 및 언로드
- insmod
- 모듈을 커널에 로드
- 단점
- insmod <module_file>.ko
- rmmod
- modprobe
- 의존성을 고려하여 모듈을 로드 및 언로드
- modprobe <module_name> (모듈 로드)
- modprobe -r <module_name> (모듈 언로드)
- 의존성 정보는 module.dep 파일을 참고
13. 커널 모듈 코드 예제
- kdt_module.c
#include <linux/module.h>#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("KTD <ktd@kdt.com>");
MODULE_DESCRIPTION("KDT initial module");
MODULE_VERSION("1.0.0");
static int __init kdt_module_init(void) {
pr_info("kdt 커널 모듈 \n");
return 0;
}
static void __exit kdt_module_exit(void) {
pr_info("바이!!\n");
}
module_init(kdt_module_init);
module_exit(kdt_module_exit);
- Makefile
obj-m := kdt_module.o
export BUILDROOT=/home/kesl/grepp/src/buildroot
export ARCH=arm64
export CROSS_COMPILE=$(BUILDROOT)/output/host/bin/aarch64-buildroot-linux-gnu-
KERNELDIR ?= $(BUILDROOT)/output/build/linux-custom
all default: modules
install: modules_install
modules modules_install help clean:
$(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@
- 컴파일 및 실행
make
sudo insmod kdt_module.ko
dmesg | tail
sudo rmmod kdt_module
14. EXPORT_SYMBOL
- EXPORT_SYMBOL(symbol_name)
- 다른 모듈에서 사용할 수 있도록 심볼(함수 또는 변수)을 외부에 공개
- EXPORT_SYMBOL_GPL(symbol_name)
- GPL 라이선스를 가진 모듈에서만 사용할 수 있도록 심볼을 공개
15. 모듈 파라미터
- module_param(name, type, perm)
- insmod 또는 modprobe 사용 시 파라미터 값을 지정
- insmod mymodule.ko myparam=123
- modprobe mymodule myparam=123
- modprobe의 경우 /etc/modprobe.conf 파일에 설정을 추가
- options mymodule myparam=123
- 빌트인 모듈의 경우 커널 command line에 파라미터를 지정
16. 모듈과 sysfs
- 모듈이 로드되면 /sys/module/<module_name> 디렉토리가 생성
- /sys/module/<module_name>/parameters 디렉토리 아래에 모듈 파라미터에 해당하는 파일이 생성
- 쓰기 가능한 파라미터는 동적으로 값을 변경
- 4개의 ARM Cortex-A72 CPU 코어
- BCM2711의 중앙 처리 장치(CPU)는 4개의 ARM Cortex-A72 코어로 구성
- VideoCore VI GPU
- LPDDR4 SDRAM 컨트롤러
- H.265/H.264 디코더/인코더
- H.265 및 H.264 비디오 코덱을 위한 하드웨어 디코더와 인코더
- JPEG 디코더/인코더
- 카메라 인터페이스
- 디스플레이 인터페이스
- USB 3.0/2.0 컨트롤러
- 기가비트 이더넷 MAC
- PCIe 2.0 x1 인터페이스
- GPIO
- UART, SPI, I2C
- PWM
- SDIO
결론
리눅스 커널 모듈은 커널의 기능을 확장하고 디바이스 드라이버를 개발하는 데 필수적인 도구입니다. 커널 모듈의 개념, 동작 방식, 개발 방법 그리고 리눅스 디바이스 드라이버의 구조에 대해 알아보았습니다. 그리고 BCM2711의 블록 다이어그램을 통해 Raspberry Pi 4B의 하드웨어 구성을 살펴보았습니다.