커널 모듈
- 리눅스 커널이 부팅되어 동작중인 상태에서 디바이스 드라이버를 동적으로 추가하거나 제거할 수 있게 하는 개념.
- 리눅스는 다른 운영체제와 달리 대부분의 기능을 모듈로 구현할 수 있다.
- 커널의 한 부분으로 동작한다.
- 모듈 프로그래밍
- 모듈은 커널의 프로그램의 특징을 갖고 있으면서 커널에 등록, 해제할 수 있으므로 일반 프로그램과는 다른 소스 형식
- 커널 라이브러리를 객체 형태(.ko)로 만들어서 시스템 콜을 통해서 리눅스 커널에 등록 요청 -> 커널은 해당 객체를 커널에 동적으로 링크
- 커널 심볼 테이블
- 커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블
- 객체 형태로 작성된 커널 모듈이 참조할 커널 내부의 함수나 변수에 연결되어 동적으로 링크된다.
- 코드에서 EXPORT_SYMBOL 매크로로 심볼 등록을 하면 보드의 /proc/kallsyms에서 등록된 심볼을 확인할 수 있다.
커널 모듈 vs 응용 프로그램
- 시스템콜을 사용하지 않고 API(Application Programming Interface)를 사용
- 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을 리턴한다.
- 초기화는 대게 리소스를 등록하거나 자료구조를 생성한다.
- 커널 모듈 적재 절차
- 주어진 모듈명을 ".ko" 인 파일에서 모듈명을 얻는다.
- /lib/modules/ 의 하부 디렉토리에서 해당 파일명을 찾는다.
- 읽어온 파일의 코드와 모듈명 그리고 module 구조체를 저장하는데 필요한 메모리 영역의 크기를 구한다.
- 구조체의 정보를 이용해 create_module() 함수를 호출한다.
- 이 함수는 모듈의 권한을 검사하고, fine_module() 함수를 이용해 이미 적재된 모듈인가를 검사한다.
- 만약 적재되지 않았다면, vmalloc() 함수를 호출해 새로운 모듈을 위한 메모리 영역을 할당한다.
- query_module() 함수를 통해 커널 심볼 테이블과 커널에 적재된 다른 모듈의 심볼 테이블을 구한다. (QM_MODULES, QM_INFO, QM_SYMBOL)
- 커널에 적재하려는 모듈의 프로그램 코드 주소를 재배치한다.
- 사용자 모드 주소 공간에 메모리 영역을 할당하고, 이곳에 모듈 구조체의 내용과 모듈명 그리고 재배치된 모듈 코드를 복사한다.
- 모듈 구조체의 init 필드의 함수 주소와 exit 필드의 함수 주소를 할당한다.
- 사용자 모드 주소 공간에 할당된 메모리 주소를 이용해 init_module() 함수를 호출한다.
- 모듈 빌드하기
- 커널 소스 트리에 있는 경우
- 예시
- Fish Master XL 2000을 위한 디바이스 드라이버(캐릭터)
- drivers/char/ 의 Makefile에 다음을 추가
- drivers/char/fishing/ 안에 다음 내용을 포함하는 Makefile 작성
- 디바이스 드라이버 파일이 커 두 개로 나눠야 할 때
- 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
- 일반적인 커널은 다음 빌드 명령으로 적절한 위치에 설치한다.
- 여러개의 파일로 구성된 모듈 컴파일
- 드라이버 매크로
- 드라이버 정보 매크로
module_init() | 함수 변경 매크로 |
module_exit() | 함수 변경 매크로 |
__init | init 함수가 종료되었을 때, init 함수를 버리고, 메모리를 free로 만듦 |
__initdata | 초기 변수에 대해 __init과 유사한 작업을 함 |
__exit | 모듈이 커널 내로 삽입될 때 함수를 생략 |
MODULE_LICENCE() | GPL, GPL v2, Dual BSD/GPL, Proprietary 등 |
MODULE_DESCRIPTION() | 모듈의 하는 일을 설명 |
MODULE_AUTHOR() | 모듈 제작자 |
MODULE_SUPPORTED_DEVICE() | 모듈이 지원하는 장치의 타입 |
- 모듈 매개변수 매크로
커널 메세지 출력- 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