$ sudo apt-get install module-assistant간단합니다. 이제 커널 모듈을 컴파일 할 준비가 다 되었습니다. 더 자세한 내용을 알고 싶으신 독자는 데비안 리눅스 커널 핸드북 을 참고하시면 좋겠습니다.
$ sudo yum install kernel-devel간단하죠? 더 자세한 내용은 페도라 릴리즈 노트 에서 확인해 주세요.
$ wget -c http://kernel.org/pub/linux/kernel/v2.6/linux-커널 소스의 압축을 해제합니다..tar.bz2
$ tar xjvf linux-이제 커널의 소스코드가 linux-.tar.bz2
$ cd linux-커널 소스를 컴파일 하고 설치하기 위한 make의 타겟들이 굉장히 많이 있는데요. 어떤 타겟을 사용할 수 있을지 확인하려면 다음과 같이 입력해 봅니다.$ make menuconfig
$ make help대부분의 배포본에서 작동하는 타겟은 다음과 같습니다.
$ make tar-pkg빌드가 끝난 후에 커널을 설치하려면 다음과 같이 입력합니다.
$ sudo tar -C / -xvf linux-그리고 나서 소스 코드 트리가 있는 디렉토리로 심볼릭 링크를 생성합니다..tar
$ sudo ln -s이제 커널 모듈을 컴파일 하기 위한 준비가 끝났습니다. 더 진행하기 전에 새로 재부팅해서 새로 빌드된 커널을 시작하도록 합니다./lib/modules/"uname -r"/build
$ tar xzvf hello_printk.tar.gz압축을 해제 하면 2개의 파일이 있습니다: 하나는 Makefile로 모듈을 컴파일하는데 필요한 규칙들을 기술하는데 사용되고, hello_printk.c 파일은 모듈의 소스 코드입니다. 우선 Makefile을 먼저 보도록 하지요.
obj-m := hello_printk.oobj-m 은 커널 모듈을 만들기 위해서 빌드 해야하는 파일들의 리스트가 됩니다. ".o" 파일은 ".c"확장자를 가지는 소스 코드로 부터 생성되는 오브젝트 파일이라고 알고 계시면 됩니다.
KDIR := /lib/modules/$(shell uname -r)/buildKDIR은 커널 소스의 저장위치를 나타냅니다.
PWD := $(shell pwd)PWD는 현재 디렉토리를 나타내며, 우리가 만들고 있는 커널 모듈의 코드가 있는 위치이기도 합니 다.
default: $(MAKE) -C $(KDIR) M=$(PWD) modulesdefault는 기본 타겟명령으로서 make 명령을 실행했을 때 인자로 다른 타겟을 지정해 주지 않는다면 default 타겟에 있는 명령이 실행되게 됩니다. default 타겟에 있는 명령은 make를 커널 소스가 있는 디렉토리를 작업 디렉토리로 지정하고 실행을 하되 $(PWD)에 있는 모듈만 컴파일을 하라고 하는 것입니다. 위와 같이 명령을 이용하면 커널 소스 트리에 설정된 모듈 컴파일 규칙들을 이용할 수가 있게 됩니다.
#include위 두 줄은 커널 모듈을 컴파일 하는데 필요한 헤더파일을 포함하고 있습니다. 위 파일을 포함시키면 나중에 보겠지만 module_init() 매크로 등이 포함되게 됩니다.#include
static int __init hello_init(void) { printk("Hello, world!n"); return 0; } static void __exit hello_exit(void) { printk("Goodbye, world!n"); } module_exit(hello_exit);다음은 모듈의 초기화 함수로서 모듈이 처음 로딩되었을때 실행되는 함수입니다. __init 키워드는 커널에게 이 함수가 딱 한 번만 실행될 것이라는 걸 알려주는 역할을 하게 됩니다. printk는 전에 말했듯이 커널 메시지 버퍼에 "Hello, world!"라는 문자열을 저장하는 역할을 하게 됩니다. printk 함수에서의 포맷팅은 대부분 printf 함수와 동일합니다.
module_init(hello_init);module_init 매크로는 커널에게 모듈이 로딩되었을때 호출되어야 하는 함수를 알려주는 역할을 합니다. 그리고 모듈이 하게 되는 모든 일들이 바로 이 초기화 함수가 호출하는 함수에 의해서 처리가 되게 됩니다.
static void __exit hello_ext(void) { printk("Goodbye, world!n"); } module_ext(hello_exit);마찬가지로 exit 함수도 한 번만 실행이 되고 module_exit 매크로를 이용해서 exit 함수를 지정해 줄 수 있습니다. __exit 키워드가 커널에게 모듈의 언로딩 시에 이 함수를 한 번만 호출해야 한다는 것을 알려주는 역할을 합니다.
MODULE_LICENSE("GPL"); MODULE_AUTHOR("valerie HensonMODULE_LICENSE 매크로는 커널에게 모듈이 어떤 라이센스 하에서 커널을 이용하게 되는지를 알려주는 역할을 합니다. 사용하는 라이센스에 따라서 사용할 수 있는 심볼(함수 혹은 변수 등)에 제한이 생기게 됩니다. GPLv2 라이센스를 선택했다면(지금 우리가 선택한 것처럼) 커널 내의 모든 심볼을 사용할 수 있습니다. 특정 라이센스를 가진 모듈 혹은 MODULE_LICENSE를 지정하지 않아서 non-GPLv2로 라이센스가 설정된 커널은 불안정 할 수도 있기때문에 이런 커널에서 발생된 버그는 리포팅을 해도 커널 개발자들에 의해서 수정되지 않을 수 있습니다. 나머지 MODULE_로 시작하는 매크로들은 모듈에 대한 정보를 제공하는 용도로 사용됩니다."); MODULE_DESCRIPTION(""Hello, world!" minimal module"); MODULE_VERSION("printk");
$ cd hello_printk $ make컴파일이 다 되었으면 insmod 명령을 이용해서 모듈을 로딩합니다. 그리고 나서 모듈이 출력하는 메시지를 보기 위해서 dmesg 명령을 이용해 보겠습니다.
$ sudo insmod ./hello_printk.ko $ dmesg | tail제대로 모듈이 로딩되었다면 다음과 같은 "Hello, world!" 메시지를 볼 수 있습니다. 이제 모듈을 커널에서 언로딩 해보겠습니다. rmmod 명령을 이용해서 하고 마찬가지로 dmesg로 메시지를 보겠습니다.
$ sudo rmmod hello_printk $ dmesg | tail성공적으로 컴파일과 실행을 해 보았습니다.
#include우선 proc_fs 에 대한 헤더 파일을 포함하고 있는데, 이 파일을 포함하면 /proc 파일 시스템을 등록하는데 필요한 기능을 사용할 수 있게 됩니다.#include #include
static int hello_read_proc(char *buffer, char **start, off_t offset, int size, int *eof, void *data) {이 함수에서 사용하고 있는 인자에 대해서 설명을 좀 해보도록 하겠습니다. buffer 는 커널 버퍼에 대한 포인터로써 여기에 우리가 출력하고 싶은 메시지를 출력하게 될 것입니다. start는 지금과 같은 간단한 모듈에서는 사용되지 않기 때문에 건너뛰도록 하겠습니다. offset은 파일의 어느 위치부터 읽기 시작할지를 나타내는데 여기서는 0만 허용하도록 하겠습니다. size 는 버퍼의 크기를 나타내며 이 변수를 이용해서 버퍼에 쓸때 버퍼의 크기 이상으로 쓰지 않도록 하겠습니다. eof는 end of file의 줄임말입니다. data 역시 start와 마찬가지로 여기서는 사용되지 않습니다.
char *hello_str = "Hello, world!n"; int len = strlen(hello_str); /* 비어있는 문자열인지 확인하기 위해서 길이를 계산한다. */ /* * 전체 스트링을 한 번에 읽어들이는 연산만 허용한다. */ if (size < len) return< -EINVAL; /* * position 값이 0이 아니면, * 더 이상 읽을 데이터가 없는 것으로 간주한다. */ if (offset != 0) return 0; /* * 버퍼가 충분히 크기 때문에 체크를 생략한다. */ strcpy(buffer, hello_str); /* * Signal EOF. */ *eof = 1; return len; }이제 /proc 파일 시스템에 모듈을 등록할 차례입니다.
static int __init hello_init(void) { /* * /proc 디렉토리 안에 "hello_world"라는 파일을 만들고 파일을 읽었을때 hello_read_proc() 함수를 * 호출하도록 합니다. */ if (create_proc_read_entry("hello_world", 0, NULL, hello_read_proc, NULL) == 0) { printk(KERN_ERR "Unable to register "Hello, world!" proc filen"); return -ENOMEM; } return 0; } module_init(hello_init);/proc 파일 시스템에서 제거하는 함수도 만들어 봅시다.(이 함수를 만들어 주지 않으면 모듈이 언로딩 되고 나서 /proc/hello_world에 누군가가 접근을 하게 될 경우 커널에 치명적인 오류를 유발시킬 수 있습니다)
static void __exit hello_exit(void) { remove_proc_entry("hello_world", NULL); } module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Valerie Henson이제 다음과 같이 /proc/hello_world라는 파일이 생성되었습니다."); MODULE_DESCRIPTION(""Hello, world!" minimal module"); MODULE_VERSION("proc"); 이제 모듈을 컴파일 해서 로딩할 준비가 되었습니다. $ cd hello_proc $ make $ sudo insmod ./hello_proc.ko
$ cat /proc/hello_world Hello, world!이제 더 많은 /proc 파일들을 만들어 볼 수 있을 것입니다. 이것보다 더 진보된 모듈을 만들어보고 싶으신 분은 seq_file 함수를 이용해서 /proc 파일에 쓰는 기능을 만들 수 있습니다. 더 자세한 내용은 Driver porting: The seq_file interface 를 참고해주세요.
이전 글 : MySQL Proxy 시작하기(2)
다음 글 : 리눅스 디바이스 드라이버 심플 소개(2)
최신 콘텐츠