0️⃣ 들어가며

본격적인 드라이버 관련 내용이 시작되었다.
정리할 것도 많고 배울 것도 많아서 머리가 터질 것 같음!
그래도 할 건 해야겠죠


1️⃣ 학습 내용

1. 드라이버의 개요

1.1 리눅스 환경 이해하기

✅ 운영체제와 리눅스

  • 운영체제(OS) 분류

    • Desktop OS

      일반 사용자용으로, GUI 중심

      Windows, macOS 등

    • Server OS

      네트워크 서비스 및 데이터 처리용으로, 안정성 중심

      Linux (RHEL, CentOS, Ubuntu Server), Windows Server

    • Embedded OS

      특정 하드웨어를 제어하거나 제한된 자원을 관리하는 용도

      RTOS (Real-Time OS): FreeRTOS, uC/OS (엄격한 시간 제어 필요 시)

      Embedded Linux: Android, Raspberry Pi OS, Yocto Project

  • 임베디드 리눅스 부팅 프로세스

    단계구성 요소역할
    1Bootloader하드웨어 초기화 및 커널 로딩 (예: U-Boot)
    2Device Tree하드웨어 정보(핀 맵, 주소 등)를 커널에 전달 (.dtb, dtoverlay)
    3KernelOS의 핵심. 메모리/프로세스 관리 및 드라이버 로딩 (zImage)
    4Root Filesystem사용자 공간. 실행 파일 및 설정 파일 저장소 (/bin, /etc, Buildroot 등으로 생성)
  • 개발 환경 (Dev Env)

    임베디드 개발을 위해 필요한 도구

    GCC, IDE(Eclipse, VS Code 등)처럼 작업을 도움

✅ 리눅스의 환경

  • 리눅스 시스템 아키텍처
    1. Application Area (User Space)

      User Code : 사용자가 작성한 프로세스들

      C Library : 커널 기능(open/read/write/close)을 쉽게 사용할 수 있도록 감싼 함수들

    2. Kernel area (Kernel Space)

      • Process Manager (Scheduler)

        CPU 자원을 누구에게 얼마나 줄지 결정 (스케줄링)

      • Memory Manager

        가상 메모리 관리, 페이지 할당/해제 담당

      • Filesystem Manager (VFS)

        VFS (Virtual File System) : 모든 파일시스템을 공통된 인터페이스로 관리

        지원 포맷 : ext4 (리눅스 표준), ntfs/fat (윈도우 호환), procfs/sysfs (가상 파일시스템)

      • Network Manager

        TCP/IP 스택 및 네트워크 패킷 처리

      • Device Manager (Device Drivers)

        하드웨어 제어를 담당하는 드라이버들이 모여 있는 곳

    3. Hardware

      CPU, Memory (RAM), Console (UART), Disk (SD Card), Network Interface (NIC) 등 실제 물리적인 장치들

    • 이미지

✅ 시스템 접근과 하드웨어 이해

  • 콘솔(Console)과 터미널(Terminal)

    • 콘솔 (Console)

      리눅스 컴퓨터 본체에 물리적으로 직접 연결된 입출력 장치 (모니터+키보드)

      물리적 접근이 가능하므로 기본적으로 root 로그인이 허용됨

    • 터미널 (Terminal)

      네트워크나 GUI 창을 통해 원격으로 접속하는 가상 클라이언트 (Remote Client)

      /dev/tty 나 /dev/pts 같은 가상 장치 파일이 무수히 많이 생성됨

      보안상의 이유로 기본적으로 root 직접 로그인이 차단되어 있고, 일반 유저 접속 후 전환 필요

  • 리눅스 하드웨어 자원의 종류

    • CPU (Processor)

      Single Core / Multi-Core

    • Memory

      Main Memory, RAM : 실행 중인 데이터가 상주하는 휘발성 메모리 (예 : D-RAM, LPDDR4, HBM, DDR-5 등)

      Storage (저장 장치) : 데이터가 영구 저장되는 공간 (예 : Hard Disk, SSD, SD Card)

    • Console

      stdinstdoutstderr 스트림 처리

      터미널 또는 UART 연결, ttyS0 등

    • I/O (Device/Peripherals)

      연결 방식과 데이터 처리에 따라 드라이버 종류가 나뉨 (Char, Block, Network 등)

  • 포팅(Porting)

    특정 보드(Board) 하드웨어 위에서 리눅스가 동작하도록 소프트웨어를 이식하는 과정

    CPU, Memory, Console, I/O 등 모든 장치에 대해 커널이 인식하도록 작업이 필요함

    • 하드웨어 자원별 포팅 방법

      CPU : 포팅 난이도가 가장 높음 → 주로 칩 제조사(SoC Vendor)가 미리 포팅해서 BSP로 제공

      Memory : 데이터시트를 참조하여 Timing, 전압 등을 커널/부트로더 설정에 맞춰주는 작업

      Console : 대부분 기본 제공되나 UART 드라이버 등을 통해 디버깅 출력을 확보하는 것이 최우선 과제

      I/O 장치 : 나머지 센서나 주변 장치들은 개발자가 직접 드라이버를 작성하거나 Device Tree를 수정하여 포팅해야 함

1.2 리눅스 커널 소스 트리 (Kernel Source Tree)

✅ 커널 정보 확인 및 준비

  • 현재 커널 정보 확인

    터미널에서 현재 실행 중인 커널의 정보 확인

    kernel.org에서 동일한 버전의 커널 소스 다운로드 가능

    $ uname -a
    Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64 GNU/Linux
    $ uname -r  # 커널 릴리즈 버전만 확인
    6.1.21-v8+
    
  • 커널 소스 준비

    1. sudo -s 명령어로 root 사용자 전환

    2. 다운로드한 커널 소스 압축 해제 후 해당 디렉토리로 이동

    3. 소스 코드 버전 검증

      root@host:~/linux-6.1.21# make kernelversion
      6.1.21

✅ 커널 소스 디렉토리

  • 커널 소스 디렉토리 구조 (Source Tree)

    디렉토리설명디렉토리설명
    arch아키텍처 종속적 코드(CPU별 arm64, x86 등 저수준 코드)mm메모리 관리 하위시스템(가상 메모리, 페이지 할당 등)
    crypto암호화 API(SHA, DES, AES 등 암호화 알고리즘 구현)net네트워킹 하위시스템(TCP/IP 스택, 소켓, 프로토콜 구현)
    Documentation커널 소스 문서(API 설명 및 서브시스템 기술 문서)scripts빌드 스크립트(make menuconfig 등 빌드 보조 도구)
    drivers디바이스 드라이버(전체 소스의 60% 이상, 각종 하드웨어 제어)security리눅스 보안 모듈(SELinux, AppArmor 등 보안 관련 코드)
    fs파일 시스템(VFS 및 ext4, fat, procfs 등 구현)sound사운드 서브 시스템(ALSA 등 오디오 드라이버 및 프레임워크)
    include커널 헤더 파일들(개발에 필요한 구조체, 매크로 정의 linux/)usr초기 유저 공간 코드(initramfs 생성을 위한 코드 포함)
    init커널 부트와 초기화(start_kernel 등 부팅 진입점)block블록 레이어(저장 장치 I/O 스케줄링 및 블록 디바이스 관리)
    ipc프로세스 간 통신(메시지 큐, 세마포어, 공유 메모리)virt가상화 하위시스템(KVM 등 가상화 지원 코드)
    kernel커널 코어 (핵심)(스케줄러, 시그널, 타이머 등 핵심 기능)lib유틸리티 루틴(커널 내부용 공통 라이브러리 함수)
  • 주요 디렉토리 탐색 예시

    root@host:~/linux-6.1.21/arch# ls
    alpha  arm    csky     ia64     loongarch  microblaze  nios2     parisc   riscv  sh     um   xtensa
    arc    arm64  hexagon  Kconfig  m68k       mips        openrisc  powerpc  s390   sparc  x86
    root@host:~/linux-6.1.21/drivers# ls
    accessibility  bluetooth    cpuidle  eisa      hid ... (매우 많음)
    root@host:~/linux-6.1.21/drivers# find . -name "dht*" 
    ./iio/humidity/dht11.c   # 이미 커널에 포함되어 있음을 확인

1.3 드라이버 제작 기초

✅ 드라이버 작성 시 고려 사항

  • 드라이버의 실행 영역

    드라이버는 커널 공간(Kernel Space)에서 실행되고, 사용자 공간(User Space)와 달리 메모리 보호 기능이 느슨함

    드라이버의 작은 버그가 시스템 전체를 멈추게 하거나 데이터를 파괴하는 등 치명적인 피해가 발생할 수 있음

  • 심볼 확인

    /proc/kallsyms 명령어는 현재 커널에 로드된 모든 심볼(함수, 변수)의 주소와 이름을 확인

    디버깅 시에 유용한 기능

  • 콜백(Callback) 구조

    드라이버는 주도적으로 실행되기보다는 커널이나 유저의 요청에 반응하는 수동적인 구조

    File Operations : open , read , write 등의 시스템 콜이 들어왔을 때 호출될 함수 연결 필요

    Interrupt Service Routine(ISR) : 하드웨어 인터럽트 발생 시 호출될 핸들러 함수 등록 필요

  • 커널 기능 활용

    메모리 할당 : malloc 대신 kmalloc , vmalloc 등을 사용

    동기화 : mutex 등을 사용하여 동시성 문제(Race Condition)을 해결해야 함

    인터럽트 : 하드웨어 신호 처리를 위한 인터럽트 등록 및 해제 기능을 사용

  • 드라이버 제작 형태 결정

    커널 형태와 모듈 형태 중 드라이버의 형태를 결정

✅ 드라이버 제작 및 빌드 형태

  • 소스 위치에 따른 분류(Source Location)

    • In-Tree(인트리)

      리눅스 커널 소스 트리 내부(/drivers/…)에 코드를 위치시킴

      커널 공식 드라이버들이 사용하는 방식으로, 커널 빌드 시스템(Kconfig, Makefile)을 그대로 따름

    • Out-of-Tree(Ex-Tree, 아웃 오브 트리)

      커널 소스 외부에 코드를 위치시킴 (예 : 사용자 홈 디렉토리 /home/user/my_driver/ )

      개발 단계에서 주로 사용하며, 해당 모듈만 빠르게 빌드할 수 있어 효율적

  • 빌드 결과물에 따른 분류(Build Artifact)

    커널 소스(linux-x.x.x)를 빌드하면 크게 두 가지 형태의 결과물을 얻을 수 있음

    • 커널 이미지 포함(Built-in)

      vmlinux → Image → zImage (압축된 부팅 이미지, RPi는 kernel8.img 등)을 얻음

      .c → .o → 링킹되어 vmlinux에 영구적으로 포함

      부팅 시 무조건 메모리에 올라가므로, 필수적인 드라이버(파일시스템, 부팅 디스크 등)는 이 방식으로 제작

      명령어 : # make zImage

    • 로드 가능한 모듈(Loadable Module)

      .c → .o → .ko 로 생성된 .ko (Kernel Object) 파일을 얻음

      필요할 때만 메모리에 적재(insmod)하고, 안 쓰면 제거(rmmod)할 수 있음

      /lib/modules/$(uname -r)/kernel/drivers 경로에서 현재 설치된 모듈들 확인 가능

      명령어 : # make modules

    • Device Tree Blob

      커널 빌드 시 드라이버 외에 생성되는 .dtb 파일

      부팅 시 하드웨어 정보를 커널에 전달하는 역할

      (예 : bcm2711-rpi-4-b.dtb)

1.4 디바이스의 종류와 소스 위치

✅ 디바이스의 종류

  • 문자 디바이스 (Character Device)

    데이터를 바이트(Byte)단위로, 스트림 형태로 처리

    주로 순차적 접근(Sequential Access)을 하며 버퍼링 없이 즉시 입출력이 일어나는 경우 많음

    키보드, 마우스, 시리얼 포트(UART), 모뎀, 비디오 장치 등 사람이 사용하는 장치

    /dev 디렉토리 아래의 디바이스 노드 파일을 통해 접근 (예 : /dev/console , /dev/ttys0 )

  • 블록 디바이스 (Block Device)

    데이터를 블록 단위로 묶어서 처리

    순차적 및 랜덤 접근(Random Access)이 가능하고 커널 내부의 버퍼 캐시를 사용함

    /dev 아래의 노드를 통해 접근(예 : /dev/sda)하지만 주로 파일 시스템을 통해 간접적으로 사용

    하드 디스크(HDD), SSD, USB 메모리, SD 카드 등 저장 장치가 대부분

✅ 드라이버 소스 코드 위치

  • 리눅스 커널 소스 트리 내 드라이버

    <Kernel Top Dir>/drivers/ 디렉토리 아래에 종류별로 정리되어 참고하면 좋음

    제어하려는 장치와 가장 유사한 기존 드라이버를 찾고 수정하여 활용하는 것이 경제적

    경로설명활용 팁
    drivers/char/문자 디바이스 드라이버범용적인 문자 장치 예제 찾기 좋음 (예: mem.c)
    drivers/block/블록 디바이스 드라이버램디스크(brd.c) 등 저장 장치 관련
    drivers/misc/기타 잡다한 드라이버분류하기 애매한 간단한 드라이버들. 초보자 참고용으로 적합
    drivers/input/입력 장치 드라이버키보드, 마우스, 터치스크린 등 입력 서브시스템 관련
    drivers/video/디스플레이 관련프레임버퍼(Framebuffer) 등 화면 출력 장치
    drivers/i2c/I2C 버스 드라이버I2C 통신을 사용하는 센서 드라이버들의 기초
    drivers/iio/산업용 I/O (IIO)ADC, 가속도 센서, 온습도 센서 등 최신 센서 드라이버 표준

1.5 유저 애플리케이션과 커널 모듈

✅ 유저 애플리케이션과 커널 모듈의 차이

  • 실행 환경과 구조의 차이

    구분응용 애플리케이션 (User App)커널 모듈 (Kernel Module)
    실행 공간User Space (사용자 영역)Kernel Space (커널 영역)
    진입점main() 함수에서 시작하여 절차적으로 수행module_init()으로 등록되고, 요청(Call)이 있을 때만 콜백 함수가 실행됨
    실행 방식능동적, 순차적 (Flow 중심)수동적, 이벤트 기반 (Event Driven: 시스템 콜, 인터럽트 등)
    오류 영향프로세스만 죽고 시스템(OS)은 계속 동작 (Segfault 등)시스템 전체가 멈추거나 재부팅됨 (Kernel Panic)
    이식성하드웨어에 비교적 독립적 (OS 위에서 동작)하드웨어에 매우 의존적 (레지스터 직접 제어 등)
    라이브러리표준 C 라이브러리(glibc) 등 풍부한 라이브러리 사용 가능커널 내부 함수(printk, kmalloc 등)만 사용 가능

✅ 커널 모듈의 특징

  • 커널 모듈의 특징
    • 기본 개념

      리눅스 커널의 기능 확장을 위해 동적으로 로드되는 코드 조각

      주로 .ko (Kernel Object) 확장자를 가짐

      필요한 기능을 런타임에 동적으로 로드/언로드하여 사용할 수 있는 구조

      디바이스 드라이버를 개발할 때 가장 많이 사용되는 형태

    • 장점

      1. 동적 로딩/언로딩

        시스템을 재부팅하지 않고도 새로운 드라이버를 메모리에 올리거나 내릴 수 있음

      2. 메모리 효율성

        필요한 기능만 메모리에 올리므로 커널 메모리(zImage 크기)를 최소화

      3. 빠른 개발 사이클

        커널 전체를 다시 컴파일(make zImage)할 필요 없이 해당되는 모듈 파일만 빠르게 빌드

    • 단점

      1. 엄격한 형식

        매크로, 라이선스 명시, 초기화/종료 함수 등 커널이 정한 규칙을 반드시 따라야 함

      2. 제한된 환경

        오직 C언어로만 작성해야 하며 부동소수점 연산 사용 불가

      3. 디버깅 어려움

        printk 로 로그를 찍고 dmesg 로 확인해야 하며, 버그 발생 시 시스템 다운으로 원인 파악 어려움

1.6 프로세스 동작과 상호작용

✅ 프로세스의 동작 정황(Process Context)

  • CPU의 실행 모드(Priviliege Level)

    • User Mode

      응용 프로그램(User App)이 실행되는 제한된 영역

      하드웨어 직접 제어는 불가

    • Kernel Mode (SVC Mode)

      운영체제 커널 및 드라이버가 실행되는 영역

      하드웨어 자원에 접근 가능

  • 전환 메커니즘

    응용 프로그램이 하드웨어 자원을 쓰기 위해서는 시스템 콜(System Call)을 통해 커널 모드로 전환해야 함

    MMU가 가상 주소를 물리 주소로 변환하는 주소 공간 매핑 작업 수행

    • User Space

      각 프로세스마다 독립적인 주소 공간(0x00000000 ~ 0xBFFFFFFF)을 가짐

    • Kernel Space

      모든 프로세스가 공유하는 상위 주소 공간(0xC0000000 ~ 0xFFFFFFFF)에 매핑

    • 참고 이미지

✅ API와 시스템 콜(System Call)

  • API (Application Programming Interface)

    응용 프로그램은 라이브러리에서 제공하는 POSIX 표준 API 함수를 사용

    POSIX C Library : IEEE에서 정한 표준 인터페이스 (예 : glibc)

    응용 프로그램 → C 라이브러리 → 커널

  • 시스템 콜 수행 과정 예시

    1. User App

      printf() 호출 → C Library의 write() 함수 호출

    2. C Library

      write() 내부에서 SWI 또는 SVC 명령어 실행

    3. CPU

      인터럽트 발생을 감지하고 User→Kernel(SVC)로 모드 전환

    4. Vector Table

      CPU는 미리 정의된 벡터 테이블의 vector_swi 주소로 점프

    5. Kernel Handler

      vector_swi()sys_write() 실행

    6. Driver

      sys_write() → VFS를 거쳐 해당 장치 드라이버의 write 함수 실행

  • 유저 영역에서의 커널 영역 자료 참조

    드라이버 개발이나 디버깅 시 유저 영억에서 커널 내부 정보를 확인하거나 설정을 변경할 때 사용하는 기능

    가상 파일시스템으로, 읽고 쓰기를 통해 커널 영역과 상호작용 가능

    • /proc (Process Filesystem)

      리눅스 커널의 프로세스 및 시스템 상태 정보를 보여주는 가상 파일시스템

      실제 디스크에 저장되지 않고 메모리에만 존재함 (type proc )

      root@host:/lib/modules/6.1.21-v8+/kernel/drivers# mount | grep proc
      proc on /proc type proc (rw,relatime)
      systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct)
      • /proc 의 주요 파일

        /proc/cpuinfo : CPU 아키텍처 및 코어 정보

        /proc/meminfo : 메모리 사용량 및 여유 공간

        /proc/cmdline : 부팅 시 커널에 전달된 인자(Boot args)

        /proc/modules : 현재 로드된 커널 모듈 목록

    • /sys (Sysfs)

      리눅스 커널의 디바이스 모델을 계층적으로 보여주는 가상 파일시스템

      드라이버가 하드웨어 정보를 보여주거나, 유저가 드라이버 설정을 실시간으로 변경할 때 사용

      • /sys 의 주요 경로

        /sys/class/gpio : GPIO 핀 제어 인터페이스

        /sys/bus : 시스템 버스(I2C, SPI, USB 등) 연결 정보

        /sys/devices : 시스템의 모든 디바이스 트리 계층 구조

        /sys/module : 로드된 모듈의 파라미터 정보


2️⃣ 느낀 점

부팅에 필요한 4가지 요소 같은 부분은 이전에 살짝 배웠던 개념이라 이후에 나오는 내용보다는 익숙하다.
아마도 다다음 내용부터는 처음 보는 함수가 잔뜩 나오게 될 것이다.
우선 유저 프로세스와 커널 프로세스가 어떤 차이가 있는지만 잘 알고 있으면 좋을 듯!

0개의 댓글