리눅스 커널 모듈

sungho·2024년 12월 29일

디바이스 드라이버

목록 보기
4/12

1. 디바이스 드라이버

  • 디바이스 드라이버 기능
    • 컴퓨터 시스템에서 하드웨어는 스스로 동작할 수 없음
    • 하드웨어를 제어하고 응용 프로그램과 상호 작용
  • 디바이스 제어의 어려움
    • 현대 컴퓨터 시스템은 수많은 디바이스로 구성

2. 디바이스 제어 방법

  • Memory-mapped I/O와 Bit Manipulation
    • Memory-mapped I/O
      • 디바이스 컨트롤러는 메모리 주소 공간의 일부분에 매핑
      • 특정 메모리 주소에 값을 쓰거나 읽는 방식으로 디바이스를 제어
      • ioremap 함수를 사용하여 물리 주소를 가상 주소에 매핑
    • Bit Manipulation
      • 디바이스 컨트롤러의 레지스터는 비트 단위로 제어
      • 비트는 특정 기능을 켜거나 끄는 역할
      • Set
        • value |= (1 << position); (특정 위치의 비트를 1로 설정)
      • Clear
        • value &= ~(1 << position); (특정 위치의 비트를 0으로 설정)
      • Toggle
        • value ^= (1 << position); (특정 위치의 비트를 반전)
      • ex)
        #define GPIO_BASE 0xFE200000 // 예시: BCM2711 GPIO 기준#define GPFSEL1  (*(volatile unsigned int *)(GPIO_BASE + 0x04))
        #define GPSET0   (*(volatile unsigned int *)(GPIO_BASE + 0x1C))
        #define GPCLR0   (*(volatile unsigned int *)(GPIO_BASE + 0x28))
        
        #define LED_PIN 17 // 예시: GPIO 17// GPIO 17을 출력 모드로 설정
        GPFSEL1 &= ~(7 << ((LED_PIN % 10) * 3)); // Clear bits
        GPFSEL1 |=  (1 << ((LED_PIN % 10) * 3)); // Set output mode
        
        // LED 켜기
        GPSET0 = (1 << LED_PIN);
        
        // LED 끄기
        GPCLR0 = (1 << LED_PIN);

3. 인터럽트

  • 인터럽트의 개념
    • 하드웨어는 특정 이벤트가 발생했을 때 CPU에게 알림
    • 인터럽트는 현재 실행 중인 프로세스에게 특정 사건을 알리는 메커니즘
  • 인터럽트 동작 방식
    1. 하드웨어에서 인터럽트 신호 발생
    2. CPU는 현재 실행 중인 작업을 잠시 중단
    3. 인터럽트 핸들러 실행
    4. 인터럽트 처리 완료 후 중단되었던 작업 재개
  • 비동기 처리
    • 인터럽트는 비동기적(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
    • 네트워크 디바이스는 소켓 API를 통해 접근

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
    • /dev/dri/*
  • 오디오
    • /dev/snd/*
  • 카메라
    • /dev/video*
  • Watchdog
    • /dev/watchdog*
  • 입력 장치
    • /dev/input/*

9. sysfs 파일시스템

  • /sys 디렉토리
    • 커널 내부 정보를 파일 및 디렉토리 형태로 제공
  • /dev가 디바이스에 접근하는 통로
  • /sys는 디바이스의 속성 및 커널 내부 정보를 확인하고 설정할 수 있는 통로
  • 예시
    • LED
      • /sys/class/leds
    • PWM
      • /sys/class/pwm
    • IIO
      • /sys/class/iio

10. GPIO

  • GPIO (General Purpose Input/Output)
    • 범용 입출력 핀으로 디지털 신호를 제어하는 데 사용
  • Legacy interface
    • /sys/class/gpios 를 통해 GPIO를 제어하는 방식
      • 보안 문제
        • 프로세스가 비정상 종료될 경우 GPIO 설정이 해제되지 않는 문제
      • ex)
        # GPIO 23을 출력으로 설정
        echo 23 > /sys/class/gpios/export
        echo out > /sys/class/gpios/gpio23/direction
        
        # GPIO 23에 1 출력
        echo 1 > /sys/class/gpios/gpio23/value
        
        # GPIO 23을 입력으로 설정
        echo in > /sys/class/gpios/gpio23/direction
        
        # GPIO 23의 값 읽기
        cat /sys/class/gpios/gpio23/value
        
        # GPIO 23 해제
        echo 23 > /sys/class/gpios/unexport
  • 새로운 인터페이스
    • libgpiod
      • /dev/gpiochipX 캐릭터 디바이스를 통해 GPIO를 제어
      • gpiodetect, gpioset, gpioget 등의 명령어를 제공
      • 보안 문제가 개선
      • ex)
        # 사용 가능한 GPIO 칩 확인
        gpiodetect
        
        # gpiochip0의 23번 핀을 출력으로 설정하고 1 출력
        gpioset --mode=signal gpiochip0 23=1
        
        # gpiochip0의 23번 핀의 값 읽기
        gpioget gpiochip0 23

11. 커널 모듈

  • 커널 모듈
    • 동적으로 커널에 추가 또는 제거할 수 있는 코드(디바이스 드라이버를 만드는 데 사용)
  • 장점
    • 커널 크기 감소
      • 모든 기능을 커널에 포함할 필요가 없음
    • 개발 생산성 향상
      • 필요한 부분만 빌드, 수정, 배포
    • 부팅 시간 단축
      • 필요한 모듈만 로드
  • 모듈 파일
    • .ko 확장자
    • /lib/modules// 디렉토리에 위치
  • 메타 정보 파일
    • module.dep
      • 모듈 간의 의존성 정보
    • module.alias
      • 모듈의 별칭(alias)을 정의
    • modules.symbols
      • 모듈의 심볼 정보
    • modules.builtin
      • 빌트인 모듈 목록

12. 모듈 로드 및 언로드

  • insmod
    • 모듈을 커널에 로드
      • 단점
        • 의존성을 고려하지 않음
      • insmod <module_file>.ko
  • rmmod
    • 모듈을 커널에서 언로드
      • rmmod <module_name>
  • 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)
    • 모듈 파라미터 정의
      • name
        • 파라미터 이름
      • type
        • 파라미터 타입
      • perm
        • sysfs 파일 권한
  • insmod 또는 modprobe 사용 시 파라미터 값을 지정
    • insmod mymodule.ko myparam=123
    • modprobe mymodule myparam=123
  • modprobe의 경우 /etc/modprobe.conf 파일에 설정을 추가
    • options mymodule myparam=123
  • 빌트인 모듈의 경우 커널 command line에 파라미터를 지정
    • mymodule.myparam=123

16. 모듈과 sysfs

  • 모듈이 로드되면 /sys/module/<module_name> 디렉토리가 생성
  • /sys/module/<module_name>/parameters 디렉토리 아래에 모듈 파라미터에 해당하는 파일이 생성
  • 쓰기 가능한 파라미터는 동적으로 값을 변경

17. Broadcom BCM2711

  • 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의 하드웨어 구성을 살펴보았습니다.

0개의 댓글