커널 모듈


  • 리눅스 커널이 부팅되어 동작중인 상태에서 디바이스 드라이버를 동적으로 추가하거나 제거할 수 있게 하는 개념.
  • 리눅스는 다른 운영체제와 달리 대부분의 기능을 모듈로 구현할 수 있다.
  • 커널의 한 부분으로 동작한다.

  • 모듈 프로그래밍
    • 모듈은 커널의 프로그램의 특징을 갖고 있으면서 커널에 등록, 해제할 수 있으므로 일반 프로그램과는 다른 소스 형식
    • 커널 라이브러리를 객체 형태(.ko)로 만들어서 시스템 콜을 통해서 리눅스 커널에 등록 요청 -> 커널은 해당 객체를 커널에 동적으로 링크
    • 커널 심볼 테이블
      • 커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블
      • 객체 형태로 작성된 커널 모듈참조할 커널 내부의 함수나 변수에 연결되어 동적으로 링크된다.
      • 코드에서 EXPORT_SYMBOL 매크로로 심볼 등록을 하면 보드의 /proc/kallsyms에서 등록된 심볼을 확인할 수 있다.


    • 커널 내 심볼의 시작 주소 / 심볼 타입 / 심볼 이름


  • 커널 모듈 vs 응용 프로그램

    • 응용 프로그램

      • exit으로 종료시 문제가 될 경우 시스템에 큰 영향을 미치지 않음 (kill)

      • 시스템콜을 사용하지 않고 API(Application Programming Interface)를 사용
        • UNIX에서는 POSIX을 표준으로 사용
        • POSIX: IEEE에서 OS를 위한 표준 인터페이스 규칙 ex) stdio.h, string.h
    • 커널 모듈
      • 등록 후 초기화 함수를 실행한 뒤 요청에만 응답
      • exit는 더 이상 요청을 받아들이지 않겠다는 의미
      • 유저 공간의 응용 프로그램이 직접 커널 코드를 호출하는 것은 불가능
      • 커널은 보호된 메모리 공간에 있으므로 시스템이 커널 모드로 전환되어야 한다.

    • 커널 코드와 유저 코드의 차이점
      • 커널은 커널 안에 구현된 라이브러리를 사용해야 한다.
            ex) lib/string.c -> linux/string.h
      • 커널은 GNU C로 작성된다.
      • 커널에는 유저 공간에서와 같은 메모리 보호가 쉽지 않다.
      • 커널은 고정 크기의 스택을 갖는다.


    • module를 사용하기 위한 명령
      • modutils 패키지에 포함된 명령
      • 명령
        설명
        insmod 모듈명.ko

        시스템에 모듈을 추가

        rmmod 모듈명

        시스템에서 모듈을 제거

        lsmod

        현재 등록된 모듈을 확인

 

  • 모듈의 기본 형태
    • 초기화 함수 
      • static int my_init(void);
    • 초기화 등록 매크로 함수
      • module_init(my_init);
      • 초기화 함수는 외부 코드에 의해 불려지지 않는다.
      • 초기화가 성공하면 0을 리턴한다.
      • 초기화는 대게 리소스를 등록하거나 자료구조를 생성한다.

  • 커널 모듈 적재 절차
    1. 주어진 모듈명을 ".ko" 인 파일에서 모듈명을 얻는다.
    2. /lib/modules/ 의 하부 디렉토리에서 해당 파일명을 찾는다.
    3. 읽어온 파일의 코드와 모듈명 그리고 module 구조체를 저장하는데 필요한 메모리 영역의 크기를 구한다.
    4. 구조체의 정보를 이용해 create_module() 함수를 호출한다.
    5. 이 함수는 모듈의 권한을 검사하고, fine_module() 함수를 이용해 이미 적재된 모듈인가를 검사한다.
    6. 만약 적재되지 않았다면, vmalloc() 함수를 호출해 새로운 모듈을 위한 메모리 영역을 할당한다.
    7. query_module() 함수를 통해 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. (QM_MODULES, QM_INFO, QM_SYMBOL)
    8. 커널에 적재하려는 모듈의 프로그램 코드 주소를 재배치한다.
    9. 사용자 모드 주소 공간에 메모리 영역을 할당하고, 이곳에 모듈 구조체의 내용과 모듈명 그리고 재배치된 모듈 코드를 복사한다.
    10. 모듈 구조체의 init 필드의 함수 주소와 exit 필드의 함수 주소를 할당한다.
    11. 사용자 모드 주소 공간에 할당된 메모리 주소를 이용해 init_module() 함수를 호출한다.

 

  • 모듈 빌드하기
    • 커널 소스 트리에 있는 경우
      • 예시
        • Fish Master XL 2000을 위한 디바이스 드라이버(캐릭터)
        • drivers/char/ 의 Makefile에 다음을 추가
          • obj-m += fishing.o
        • drivers/char/fishing/ 안에 다음 내용을 포함하는 Makefile 작성
          • obj-m += fishing.o
        • 디바이스 드라이버 파일이 커 두 개로 나눠야 할 때
          • obj-m += fishing.o
          • fishing-objs := fishing-main.o fishing-line.o
        • 최종적인 fishing.ko가 만들어진다.

    • 커널 소스 밖에 있는 경우
      • 예시
        • Makefile을 다음과 같이 구성할 수 있다.
          • obj-m := fishing.o
          • fishing-objs := fishing-main.o fishing-line.o
        • 가장 큰 차이점은 build 과정이다.
          • make -C kernel/source/location SUBDIRS=$(PWD)
          • -C : 커널 소스 트리가 있는 위치
          • SUBDIRS : 드라이버 소스가 있는 작업 디렉토리

  • 모듈 설치
    • 컴파일된 모듈은 일반적으로 다음 위치에 설치
      • /lib/modules/version/kernel
      • 예: /lib/modules/2.6.10/kernel/drivers/char/fishing.ko
    • 일반적인 커널은 다음 빌드 명령으로 적절한 위치에 설치한다.
      • make modules_install
  • 여러개의 파일로 구성된 모듈 컴파일
    • Makefile 구성
      • start.c stop.c 코드를 가지고 모듈을 컴파일한다고 할 때

        Makefile 예시

        obj-m +=startstop.o
        startstop-objs := start.o stop.o


        KDIR := /work/ldd/linux-2.6.17.13-rebis


        all:

               make -C $(KDIR) SUBDIRS=$(PWD) modules

        clean:

               make -C $(KDIR) SUBDIRS=$(PWD) clean



  • 드라이버 매크로
    • 드라이버 정보 매크로
      • 매크로
        설명
        module_init()함수 변경 매크로
        module_exit()함수 변경 매크로
        __initinit 함수가 종료되었을 때, init 함수를 버리고, 메모리를 free로 만듦
        __initdata초기 변수에 대해 __init과 유사한 작업을 함
        __exit모듈이 커널 내로 삽입될 때 함수를 생략
        MODULE_LICENCE()GPL, GPL v2, Dual BSD/GPL, Proprietary 등
        MODULE_DESCRIPTION()모듈의 하는 일을 설명
        MODULE_AUTHOR()모듈 제작자
        MODULE_SUPPORTED_DEVICE()모듈이 지원하는 장치의 타입
    • 모듈 매개변수 매크로
      • 커널은 사용자가 매개변수를 부트시나 모듈 로드시에 선언하고 드라이버 안에서는 이 매개변수들이 전역변수처럼 보이도록 하는 프레임웍을 제공한다.

        매크
        module_param(name, type, perm)command 인자를 얻음. ex) ./insmod mynodule.ko myvariable=5
        module_param_array(name, type, nump, perm)

        인자: 변수명, 자료형, nump는 정수형 포인터, sysfs의 접근 권한.

        세번째 인자로 배열 개수를 포인터로 넘겨줌 (NULL 가능)

        module_param_string(name, string, len, perm);

        charp로 정의된 문자열을 지정할 때, len으로 길이와 함께 지정.

        길이는 sizeof(string) 형태로 사용 가능

        module_param_named(name, value, type, perm);변수의 값(value)를 내부에 포함 시키고자 할 때 사용
        MODULE_PARM_DESC()

        모듈이 가질 수 있는 문서 인자들로 사용.

        인자: 변수 이름, 변수를 묘사하는 문자열


  • 커널 메세지 출력
    • printk()
      • printf()와 유사하지만 커널의 메시지를 출력하고 관리할 수 있는 특성이 있다.
      • 메세지 기록 관리를 위한 로그 레벨의 지정
      • 출력 디바이스의 다중 지정
      • 콘솔에서 확인하거나 dmesg 명령을 이용해서 로그 파일 확인
      • 로그레벨 지정
        • 로그 레벨은 printk() 함수에 전달되는 문자열의 선두 문자에 "<1>"과 같이 숫자로 등급을 표현한다.
        • linux/kernel.h 에 정의된 선언문을 이용하는 것이 바람직하다.
        • 상수 선언문
          의미
          #define KERN_EMERG<0> 시스템이 동작하지 않는다
          #define KERN_ALERT<1> 항상 출력된다.
          #define KERN_CRIT<2> 치명적인 정보
          #define KERN_ERR<3> 오류 정보
          #define KERN_WARNING<4> 경고 정보
          #define KERN_NOTICE<5> 정상적인 정보
          #define KERN_INFO<6> 시스템 정보
          #define KERN_DEBUG<7> 디버깅 정보
        • 레벨에 대한 표시를 하지 않으면 KERN_WARNING과 같은 레벨이다.
        • 다음의 처리 결과는 모두 같다.
          • printk(KERN_WARNING "system ok\n");
          • printk("<4>" "system ok\n");
          • printk("<4> system ok\n");
          • printk("system ok\n");
        • printk() 사용시 주의점
            • 개행 문자가 있어야 출력을 시작한다.

    • 커널 메시지 관리 데몬
      • klogd: 커널에서 발생하는 메시지를 기록하고 관리한다.
      • syslogd: 커널에서 발생하는 메시지와 응용 프로그램에서 요청한 시스템 정보를 기록하고 관리한다.
      • /proc/kmsg
        • 커널 메시지가 발생할 때마다 관찰할 수 있다.
        • cat /proc/kmsg
        • dmesg

 


출처 http://blog.naver.com/PostView.nhn?blogId=yyg1368&logNo=60121733093

출처 https://wiki.kldp.org/Translations/html/The_Linux_Kernel-KLDP/tlk12.html

출처 https://www.joinc.co.kr/w/Site/Embedded/Documents/LinuxKernelModuleProg

 

 

+ Recent posts