임베디드기사 실기 20부작 6편
Linux Device Driver와 사용자 프로그램은 어떻게 연결되는가?
임베디드 리눅스를 공부하다 보면 어느 순간 아주 중요한 질문을 만나게 됩니다. “사용자 프로그램은 실제 하드웨어를 어떻게 움직이는가?” 입니다. C 프로그램에서 단순히 open(), read(), write()를 호출했을 뿐인데 GPIO가 켜지고, UART가 송신되고, SPI 센서의 값이 읽히는 이유는 무엇일까요?
그 사이에는 Linux Device Driver가 있습니다. 이번 편에서는 사용자 프로그램과 하드웨어 사이를 연결하는 구조를 한 번에 이해할 수 있도록 정리합니다.
Linux는 시스템 안정성과 보호를 위해 실행 영역을 크게 User Space와 Kernel Space로 나눕니다. 일반 응용 프로그램은 User Space에서 실행되고, Device Driver와 Kernel은 Kernel Space에서 실행됩니다.
사용자 프로그램이 임의의 하드웨어 레지스터 주소에 직접 접근할 수 있다면 잘못된 포인터 하나만으로도 시스템 전체가 멈출 수 있습니다. 그래서 Linux는 하드웨어 접근을 Kernel이 중개하도록 설계합니다.
이 한 줄의 흐름을 이해하면 Device Driver 문제의 절반은 풀린다고 봐도 됩니다.
Device Driver는 운영체제와 실제 하드웨어 사이의 번역기이자 중계자입니다. User Application은 “데이터를 읽어라”, “속도를 설정하라”, “장치를 켜라”와 같은 논리적인 명령을 내리지만, 실제 하드웨어는 특정 Register에 특정 Bit를 쓰거나 읽어야 움직입니다.
따라서 Driver는 보통 다음 역할을 수행합니다.
- 하드웨어 Register 접근
- Interrupt 처리
- DMA 설정
- Buffer 관리
- User Space에 표준 인터페이스 제공
- Hardware 상태를 Linux의 파일 추상화와 연결
시험에서는 “사용자 프로그램과 H/W 사이를 중개한다”는 표현을 기억해 두면 좋습니다.
Linux는 많은 장치를 파일처럼 다루는 철학을 가지고 있습니다. 그래서 사용자 프로그램은 실제 UART Controller Register를 직접 조작하는 대신 /dev/ttyS0 같은 Device File을 열고 사용합니다.
센서나 FPGA Driver도 마찬가지입니다. 예를 들어 /dev/fpga_ctrl이라는 장치 파일이 있다면 사용자는 다음처럼 접근할 수 있습니다.
사용자 프로그램은 이 코드만 보지만, 내부에서는 Kernel이 해당 File Operation을 Driver 함수로 연결합니다.
| 함수 | 역할 |
|---|---|
| open() | 장치 파일을 열고 Driver와 연결 |
| read() | Driver에서 User Space로 데이터 전달 |
| write() | User Space의 데이터를 Driver/H/W로 전달 |
| ioctl() | 속도, 모드, Register 설정 등 장치 고유 제어 |
| close() | 장치 사용 종료 및 자원 반환 |
read()와 write()가 데이터 전달에 적합하다면, ioctl()은 장치별 특수 설정에 자주 사용됩니다. 예를 들어 Baudrate, 동작 Mode, Trigger 설정과 같은 제어 명령이 대표적입니다.
Character Device를 예로 들면 Kernel 안에서는 Driver를 등록해야 하고, User Space에서는 그 Driver에 접근할 Device Node가 필요합니다.
register_chrdev()는 Kernel 내부에서 Character Device Driver를 등록하는 역할을 합니다. 반면 mknod는 /dev 아래에 장치 파일을 만들어 User Space와 연결할 수 있도록 합니다.
register_chrdev() = Kernel 쪽 Driver 등록
mknod = User Space가 접근할 Device Node 생성
예를 들어 다음 명령을 보겠습니다.
여기서 c는 Character Device를 의미합니다. 240은 Major Number이고, 0은 Minor Number입니다.
- Major Number: 어떤 Driver에 연결할 것인지 식별
- Minor Number: 같은 Driver 아래 여러 Device를 구분
예를 들어 하나의 Driver가 여러 Channel을 제어한다면 Minor Number를 0, 1, 2처럼 나누어 사용할 수 있습니다.
| 구분 | Character Device | Block Device |
|---|---|---|
| 단위 | Byte/Stream 중심 | Block 단위 |
| 예 | UART, Sensor, GPIO 계열 | Disk, eMMC, 저장장치 |
| mknod Type | c | b |
실기에서는 Character/Block의 구분과 mknod의 type 문자까지 함께 나올 수 있으므로 연결해서 외우는 것이 효율적입니다.
Device Driver의 가장 아래쪽에서는 결국 Register 접근이 일어납니다. 많은 임베디드 SoC와 MCU는 주변장치 Register를 Memory Address 공간에 배치하는 MMIO(Memory-Mapped I/O) 방식을 사용합니다.
Driver는 필요한 Register를 Mapping한 뒤 Bit를 읽거나 써서 GPIO, SPI, I2C, UART, PWM, FPGA Register 등을 제어합니다.
→ write()/ioctl()
→ Driver Handler
→ MMIO Register Bit 설정
→ 실제 LED 출력 변화
이 흐름을 기억하면 “Software 명령이 Hardware 동작으로 바뀌는 지점”이 어디인지 명확해집니다.
자료에서는 User Space가 /dev뿐 아니라 /sys 계층을 통해 Kernel Device 정보를 확인하거나 간단한 제어를 수행하는 구조도 다루고 있습니다. sysfs는 Kernel 내부의 Device 구조를 파일 형태로 노출한 가상 파일시스템입니다.
따라서 복잡한 Binary I/O보다 상태 조회나 간단한 설정에는 sysfs 형태의 인터페이스가 사용될 수 있습니다.
자료에는 특정 고속 처리 상황에서 /dev/mem과 mmap()을 이용해 물리 메모리 영역을 User Space에 Mapping하는 방식도 소개되어 있습니다. 다만 일반적인 Driver 구조의 기본 답안은 User Application → System Call → Device Driver → Hardware 흐름으로 잡는 것이 좋습니다.
시험에서는 “Driver가 기본 경로이고, 직접 Mapping은 별도의 특수한 접근 방식” 정도로 구분하면 충분합니다.
문제 1. Linux 사용자 프로그램이 H/W를 제어하는 일반적인 흐름을 쓰시오.
User Application은 System Call을 통해 Device Node에 접근하고, Kernel Device Driver가 요청을 처리하여 MMIO 또는 I/O Register를 제어함으로써 실제 Hardware를 동작시킨다.
문제 2. mknod의 Major Number와 Minor Number의 의미를 쓰시오.
Major Number는 연결할 Device Driver를 식별하고, Minor Number는 동일 Driver가 관리하는 개별 장치를 구분한다.
문제 3. ioctl()의 용도를 설명하시오.
ioctl()은 read/write만으로 표현하기 어려운 장치 고유의 제어 명령이나 설정 값을 User Space에서 Driver로 전달하기 위해 사용한다.
장치가 동작하지 않을 때도 위의 계층을 아래에서 위로 또는 위에서 아래로 나누어 확인하면 원인을 빠르게 좁힐 수 있습니다.
2) open()이 성공하는가?
3) Driver가 정상 Load되어 있는가?
4) Major/Minor가 맞는가?
5) ioctl/read/write Handler가 호출되는가?
6) MMIO Register가 실제로 변하는가?
7) 전원·배선·신호 등 Hardware가 정상인가?
이렇게 보면 Device Driver 학습은 단순히 시험 암기가 아니라 실제 임베디드 장비의 장애 분석과도 직접 연결됩니다.
Device Driver = H/W 제어 중계자
/dev = Device File
mknod = Device Node 생성
Major = Driver 식별
Minor = Device 식별
open/read/write = 기본 I/O
ioctl = 장치 고유 제어
MMIO = Register를 Memory Address로 접근
Linux Device Driver를 처음 공부할 때는 Kernel 함수의 이름부터 외우려는 경우가 많습니다. 그러나 먼저 전체 구조를 이해하면 훨씬 수월합니다.
사용자 프로그램 → Device Node → System Call → Driver → Register → Hardware
이 연결 구조가 머릿속에 잡히면 이후 module_init(), register_chrdev(), request_region(), insmod, Interrupt Handling 같은 주제도 하나의 흐름으로 자연스럽게 연결됩니다.
7편 - module_init(), register_chrdev(), request_region: Kernel Module 초기화의 흐름
다음 편에서는 Driver가 Kernel에 실제로 등록되고, I/O 자원을 확보하는 과정을 실기 문제 중심으로 살펴봅니다.
태그
#임베디드기사 #임베디드실기 #리눅스 #디바이스드라이버 #LinuxDeviceDriver #mknod #ioctl #시스템콜 #유저스페이스 #커널스페이스 #MMIO #register_chrdev #장치파일 #CharacterDevice #DeviceNode #커널모듈 #임베디드시스템 #리눅스커널 #자격증공부 #기술블로그
'경제' 카테고리의 다른 글
| 임베디드기사 실기 20부작 5편 ## Kconfig, defconfig, .config의 관계 — Linux Kernel 설정은 어떻게 만들어지는가? (0) | 2026.09.15 |
|---|---|
| 임베디드기사 실기 20부작 4편 Linux Kernel은 무엇을 관리하는가? (0) | 2026.09.14 |
| 임베디드기사 실기 20부작 3편 Bootloader와 Linux 부팅 과정 (0) | 2026.09.14 |
| 임베디드기사 실기 20부작 2편 Startup Code와 부팅의 시작 (0) | 2026.09.14 |
| 임베디드기사 실기 20부작 1편 ## 실기시험 전체 지도와 합격 공부법 (0) | 2026.09.14 |