커널 모듈
- 리눅스 커널이 부팅되어 동작중인 상태에서 디바이스 드라이버를 동적으로 추가하거나 제거할 수 있게 하는 개념.
- 리눅스는 다른 운영체제와 달리 대부분의 기능을 모듈로 구현할 수 있다.
- 커널의 한 부분으로 동작한다.
- 모듈 프로그래밍
- 모듈은 커널의 프로그램의 특징을 갖고 있으면서 커널에 등록, 해제할 수 있으므로 일반 프로그램과는 다른 소스 형식
- 커널 라이브러리를 객체 형태(.ko)로 만들어서 시스템 콜을 통해서 리눅스 커널에 등록 요청 -> 커널은 해당 객체를 커널에 동적으로 링크
- 커널 심볼 테이블
- 커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블
- 객체 형태로 작성된 커널 모듈이 참조할 커널 내부의 함수나 변수에 연결되어 동적으로 링크된다.
- 코드에서 EXPORT_SYMBOL 매크로로 심볼 등록을 하면 보드의 /proc/kallsyms에서 등록된 심볼을 확인할 수 있다.
커널 내 심볼의 시작 주소 / 심볼 타입 / 심볼 이름
커널 모듈 vs 응용 프로그램
응용 프로그램
exit으로 종료시 문제가 될 경우 시스템에 큰 영향을 미치지 않음 (kill)
- 시스템콜을 사용하지 않고 API(Application Programming Interface)를 사용
- UNIX에서는 POSIX을 표준으로 사용
- POSIX: IEEE에서 OS를 위한 표준 인터페이스 규칙 ex) stdio.h, string.h
- 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에 다음을 추가
- 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 : 드라이버 소스가 있는 작업 디렉토리
- Makefile을 다음과 같이 구성할 수 있다.
- 예시
- 커널 소스 트리에 있는 경우
- 모듈 설치
- 컴파일된 모듈은 일반적으로 다음 위치에 설치
- /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.oKDIR := /work/ldd/linux-2.6.17.13-rebis
all:
make -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
make -C $(KDIR) SUBDIRS=$(PWD) clean
- Makefile 구성
- 드라이버 매크로
- 드라이버 정보 매크로
- 매크로설명
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() 모듈이 지원하는 장치의 타입
- 모듈 매개변수 매크로
커널은 사용자가 매개변수를 부트시나 모듈 로드시에 선언하고 드라이버 안에서는 이 매개변수들이 전역변수처럼 보이도록 하는 프레임웍을 제공한다.
매크로설명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
'Linux Driver' 카테고리의 다른 글
캐릭터 디바이스 드라이버 (Character Device Driver) (0) | 2018.08.30 |
---|---|
디바이스 드라이버 (Device Driver) - 1. 개요 (1) | 2018.08.29 |
디바이스 드라이버를 위한 커널 서비스 (Kernel Service for Device Driver) - 2. 인터럽트 처리 (0) | 2018.08.29 |
디바이스 드라이버를 위한 커널 서비스 (Kernel Service for Device Driver) - 1. 변수와 메모리 할당 (0) | 2018.08.29 |
리눅스 커널 (linux kernel) (2) | 2018.08.29 |