디바이스 드라이버를 만드는 방법에 대해 알아보자. 우선 대부분의 문서에 나오는 간단한 예제를 이용해보자.
/* hello.c */ (1) #ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif (2) #define __NO_VERSION__ #include <linux/module.h> #include <linux/version.h> #include <linux/fs.h> (3) struct file_operations Fops = { NULL, /* owener */ NULL, /* llseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ NULL, /* open */ NULL, /* flush */ NULL, /* release */ NULL, /* fsync */ NULL, /* lock */ NULL, /* readv */ NULL, /* writev */ NULL, /* sendpage */ NULL /* get_unmapped_area */ }; (4) int init_module() { if (register_chrdev(213, "hello", &Fops) < 0) return -EIO; printk("hello.o start\n"); return 0; } (5) void cleanup_module() { unregister_chrdev(213, "hello"); printk("hello.o end\n"); } |
컴파일은 다음과 같이 한다.
gcc -o hello.o -c -D__KERNEL__ -DMODULE -O -Wall -I/usr/include hello.c |
컴파일 할 때 컴파일러에게 커널에 해당하는 프로그램임과 이 것이 MODULE 임을 알려준다. 일반 적인 프로그램과는 달리 -c 옵션을 사용해 링킹을 하진 않아야한다.
에러 없이 컴파일 됐으면 다음과 같은 명령으로 만들어진 디바이스 드라이버를 등록해보자.
insmod hello.o |
화면에 뭔가 출력되는가? 아무 것도 출려되지 않으면 dmesg 명령을 사용해 커널에서 뿌린 메시지의 마지막에 'hello.o start'가 찍혔는지 확인한다. 또 lsmod 명령으로 정상적으로 hello.o가 등록됐느지 확인해 보자. 아래 것은 필자의 리눅스 시스템에 올라간 모듈들을 본것이다. 제일 위에 hello.o가 등록된 것이 보일 것이다. 비록 아무 것도 사용하지 않는다고 나와 있지만 처음 만들어본 디바이스 드라이버가 등록된 것을 확인할 수 있다.
Module Size Used by hello 592 0 (unused) smbfs 31376 4 (autoclean) sd_mod 10640 2 (autoclean) vfat 9520 1 (autoclean) fat 29696 0 (autoclean) [vfat] sr_mod 12176 0 (autoclean) tuner 8176 0 (autoclean) (unused) i2c-core 13024 0 (autoclean) [tuner] vmnet 17984 6 parport_pc 19648 0 (unused) parport 14176 0 [parport_pc] vmmon 18784 0 (unused) 3c59x 24960 1 ide-scsi 7648 0 ide-cd 26656 0 cdrom 29056 0 [sr_mod ide-cd] md 44224 0 (unused) snd-pcm-oss 35792 0 (unused) snd-mixer-oss 8528 1 [snd-pcm-oss] snd-card-fm801 7296 1 snd-pcm 47744 0 [snd-pcm-oss snd-card-fm801] snd-mpu401-uart 2656 0 [snd-card-fm801] snd-rawmidi 11968 0 [snd-mpu401-uart] snd-ac97-codec 22832 0 [snd-card-fm801] snd-opl3 5264 0 [snd-card-fm801] snd-timer 9584 0 [snd-pcm snd-opl3] snd-hwdep 3376 0 [snd-opl3] snd-seq-device 3744 0 [snd-rawmidi snd-opl3] snd 23632 0 [snd-pcm-oss snd-mixer-oss snd-card-fm801 snd-pcm snd-mpu401-uart snd-rawmidi snd-ac97-codec snd-opl3 snd-timer snd-hwdep snd-seq-device] hid 19152 0 (unused) input 3360 0 [hid] usb-storage 26400 1 scsi_mod 88400 4 [sd_mod sr_mod ide-scsi usb-storage] usb-uhci 21408 0 (unused) usbcore 49632 1 [hid usb-storage usb-uhci] rtc 5600 0 (autoclean) |
이어 'rmmod hello'란 명령을 실행해 보자. insmod 때와 마찬가지로 화면에 아무 것도 출력되지 않으면 dmesg를 사용해 또 확인해 보자. 'hello.o end'란 말이 출력됐는가? 이어 lsmod를 사용해 hello.o가 해제됐는지 확인해 보기 바란다.
hello.c를 간단하게 분석해 보자.
만약 위의 예제를 X-window 상에서 insmod/rmmod 한다면 화면에 아무 것도 나오지 않을 것이다. 이는 printk의 출력이 가상 터미널 7번에 출력되기 때문이다. 그러므로 xterm에 옵션을 주지 않고 그냥 연 창엔 출력되지 않으므로 demsg를 사용해 확인해야한다. 대신 xterm -C로 연 xterm에선 바로 확인이 가능할 것이다.
예제는 그 크기가 작기 때문에 하나의 파일에 모두 들어가지만 일반적인 경우 한개의 파일에 모듈 전체의 내용이 들어가지 않을 경우엔 소스 코드 여러개에 나눠 쓰게 된다. 이런 경우엔 각각의 파일에 모듈에 필요한 정의를 하고 컴파일 후 링커를 사용해 합쳐주면 된다.
gcc -D__KERNEL__ -DMODULE -Wall -O -c -o start.o start.c gcc -D__KERNEL__ -DMODULE -Wall -O -c -o stop.o stop.c ld -m elf_i386 -r -o hello.o start.o stop.o |
start.c에 init_module()이 들어있고 stop.c에 cleanup_module()이 들어있다면 각각을 컴파일 한 후 ld를 사용해 하나로 묶어준다.