Linux Kernel and Device Driver Jo, Heeseung
커널과의관계 시스템이지원하는하드웨어를응용프로그램에서사용할수있도록커널에서제공하는라이브러리 응용프로그램이하드웨어를제어하려면커널에자원을요청 - 응용프로그램에서는 C 라이브러리같은함수를호출 - 라이브러리내부에서는커널에게시스템콜을호출 - 커널로제어가넘어가게되면 ( 커널모드 ) 를통해서하드웨어를제어 를통해하드웨어에명령을전달 2
커널과의관계 3
디바이스파일 리눅스는시스템에있는모든자원을파일형식으로표현 - 램, 키보드, 하드디스크등도파일로표현 - e.g. /dev/input/mouse0, /dev/sda1 /dev 디렉터리에존재 - 디바이스파일은실질적인하드웨어와 mapping - 하드웨어정보와 I/O 를제공 - 디바이스파일은디바이스타입정보, 주번호, 부번호로구분 응용프로그램은하드웨어를다루기위해해당디바이스파일에저수준입출력함수를사용 4
디바이스파일생성 디바이스파일은 mknod 유틸리티에의해서생성 mknod를이용하여디바이스파일을만드는방법 root@ubuntu:# mknod /dev/ttys4 c 4 68 - ttys4: 디바이스파일명 - c: 문자 - 4: 주번호 - 68: 부번호 5
주번호와부번호 주번호 - 커널에서를구분 - 주번호는제어하려는디바이스를구분하기위한디바이스의 ID 부번호 - 내에서장치를구분 - 같은종류의디바이스가여러개있을때그중하나를선택 6
주번호와부번호 시리얼디바이스파일의경우 [root@sm5s4210 ~]#ls -al /dev/ttys* ------------------다음과같은메시지가 출력된다 ----------------- crw------- 1 root root 4, 64 Jan 1 1970 /dev/ttys0 crw------- 1 root root 4, 65 Jan 1 1970 /dev/ttys1 crw------- 1 root root 4, 66 Jan 1 1970 /dev/ttys2 crw------- 1 root root 4, 67 Jan 1 1970 /dev/ttys3 시리얼 COM1 포트를나타내는 /dev/ttys0 와 COM2 포트를나타내는 /dev/ttys1 의주번호는 '4' 로같음 즉시리얼포트라고하는같은종류의디바이스파일 하지만이들각각을구분해주는부번호는다름 7
저수준파일입출력함수 리눅스커널에서제공하는파일관련시스템콜 기본적인파일을처리하는함수 - 디바이스파일을실질적으로다룰때사용 디바이스파일에연관된함수가동작 8
저수준파일입출력함수 저수준파일입출력함수들의종류와기능 저수준파일입출력함수 open() close() read() write() lseek() ioctl() fsync() 기능파일이나장치를연다열린파일을닫는다파일에서데이터를읽어온다파일에데이터를쓴다파일의쓰기나읽기위치를변경한다 read(), write() 로다루지않는특수한제어를한다파일에쓴데이터를실제하드웨어의동기를맞춘다 9
의종류 문자 - 임의의길이를갖는문자열이나자료의순차성을지닌장치를다루는 - 버퍼캐쉬를사용하지않음 - 사용자에게 raw 데이터를제공 - Serial, console, keyboard, printer, mouse 등 블록 - 일정크기의버퍼를통해데이터를처리하는디바이스 - 커널내부의파일시스템에서관리하고내부적인버퍼가있는 - 하드디스크, 램디스크등 네트워크 - 네트워크계층과연결되어네트워크통신을통해패킷을송수신할수있는기능을제공 - 이더넷, PPP 등 10
커널모듈 리눅스커널이부팅되어동작중인상태에서를동적으로추가하거나제거 를개발할때개발시간을단축 커널자원을효율적으로다룰수있음 - 필요없는기능은커널에포함시키지않음 리눅스에서는대부분의기능을모듈로구현가능 - 파일시스템,, 통신프로토콜등 - 하드웨어를다루는기능을구현한것 11
모듈프로그래밍 모듈은커널의일부로서동작 커널에동적으로적재되고제거가능 모듈은객체형태로제작 시스템콜을통해서리눅스커널에적재 Kernel Module1 Device driver1 12
모듈과일반프로그램과의차이점 커널모듈은커널영역에서동작 커널에로딩및제거될때호출되는함수가별도로지정되어있음 - main() 함수가존재하지않음 일반적인라이브러리를사용할수없음 커널이 export 해준함수만을사용가능 13
모듈관련유틸리티 모듈로만들어진는커널에링크시키도록도와주는유틸리티가필요 insmod : 모듈을커널에적재 rmmod : 커널에서모듈을제거 lsmod : 커널에적재된모듈목록을출력 depmod : 모듈간의존성정보를생성 modprobe : 모듈을커널에적재하거나제거 14
간단한모듈프로그램작성 "Hello world" 메시지를출력해주는간단한모듈드라이버작성 파일명은 hello.c 모듈드라이버를작성할디렉토리생성 root@ubuntu:# cd /working root@ubuntu:/working# mkdir module_driver root@ubuntu:/working# cd module_driver 15
root@ubuntu:/working/module_driver# vi hello.c ----------- 다음과같이수정한다 ------------------- 1 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> 2 static int hello_init(void){ printk("hello world\n"); return 0; } 3 static void hello_exit(void) { printk("goodbye world\n"); } 4 module_init(hello_init); module_exit(hello_exit); 5 MODULE_LICENSE("Daul BSD/GPL"); ------------------ 저장하고종료한다 ----------------- root@ubuntu:/working/module_driver# 16
간단한모듈프로그램작성 모듈을컴파일하기위한 Makefile 을작성 root@ubuntu:/working/module_driver# vi Makefile ---------------------- 다음과같이수정한다 ---------------- # Hello Device Driver Makefile CC = arm-linux-gcc obj-m := hello.o KDIR := /working/linux-2.6.35-s4210/ --> 자신의커널소스위치 PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -f *.ko rm -f *.o rm -f *.mod.* rm -f.*.cmd ----------------------- 저장하고종료한다 ------------------- root@ubuntu:/working/module_driver# 17
간단한모듈프로그램작성 모듈컴파일 rooroot@ubuntu:/working/module_driver# ls ------------------다음과같은메시지가출력된다 ----------------- Makefile hello.c root@ubuntu:/working/module_driver# make ------------------다음과같은메시지가출력된다 ----------------- make -C /working/linux-2.6.35-s4210/ SUBDIRS=/working/module_driver modules make[1]: Entering directory `/working/linux-2.6.35-s4210' CC [M] /working/module_driver/hello.o Building modules, stage 2. MODPOST 1 modules CC /working/module_driver/hello.mod.o LD [M] /working/module_driver/hello.ko make[1]: Leaving directory `/working/linux-2.6.35-s4210' root@ubuntu:/working/linux-2.6.35-s4210# 18
간단한모듈프로그램작성 생성된파일리스트를확인 - 생성된파일중에커널에로딩하는모듈파일은 hello.ko root@ubuntu:/working/module_driver# ls ------------------ 다음과같은메시지가출력된다 ----------------- Makefile hello.c hello.mod.c hello.o Module.symvers hello.ko hello.mod.o modules.order 19
간단한모듈프로그램작성 모듈실행 - 보드에서 linux 로 nfs 로 mount 하여 module 을확인 hello 모듈을커널에로딩 [root@sm5s4210 ~]# insmod hello.ko ------------------ 다음과같은메시지가출력된다 ----------------- Hello world [root@sm5s4210 ~]# dmesg 21
간단한모듈프로그램작성 모듈확인 - lsmod 를사용하여적재된모듈목록을확인 첫번째열은등록된모듈명, 두번째열은모듈이커널에서차지하고있는메모리의크기 세번째열은사용여부 네번째열은해당모듈을참조하고있는모듈명을나타냄 [root@sm5s4210 ~]# lsmod ------------------다음과같은메시지가 출력된다 ----------------- Module Size Used by Tainted: P hello 806 0 22
간단한모듈프로그램작성 모듈제거 - 커널에등록된모듈을제거 - 이때모듈명만을지정 [root@sm5s4210 ~]# rmmod hello [root@sm5s4210 ~]# tail /var/log/messages --------------- 다음과같은메시지가출력된다 ---------------- Goodbye world [root@sm5s4210 ~]# dmesg [root@sm5s4210 ~]# lsmod 23
life cycle Module init. open() release() Module exit 24
초기화 처리해야할하드웨어검출 검출된하드웨어를사용할수있도록초기화작업수행 module_init 매크로 - 모듈이로딩될때수행 - 처리하고자하는하드웨어의검출 - 의등록 - 에내부구조체의메모리할당 - 하드웨어초기화 25
종료 종료시점에는를사용한후의처리를구현 module_exit 매크로 - 모듈이제거될때수행 - 의해제 - 에할당된메모리의해제 - 하드웨어제거에따른처리 26
open() 과 release() 의 open() 과 release() 함수들은디바이스파일이열리는시점과닫히는시점에호출 디바이스가사용되면서필요한처리와디바이스가더이상사용되지않을때의처리구현 module_init, module_exit 가 loading/unloading 되는시점에 called 27
문자 저수준파일입출력함수이용 - 디바이스파일에데이터를읽고씀 내의대응함수가호출됨 문자디바이스의경우 chrdevs 라는전역변수에서문자를관리 - struct file_operations *fops 라는필드를포함한문자를관리하는구조체 파일오퍼레이션구조체 (struct file_operations) - 문자와응용프로그램을연결하는고리 - 응용프로그램은저수준파일입출력함수를사용 - 문자의오퍼레이션구조체정보를참고하여디바이스파일에접근한함수에대응하는함수를호출 28
문자 파일오퍼레이션구조체 (struct file_operations) Operation struct module *owner llseek read /write readdir select ioctl mmap open / release fsyn fasync readv / writev 설명 어떤모듈로올라간와연관을가지는지를나타내는포인터 현재의읽기쓰기포인터를옮기고자할때사용데이터를장치에서읽거나쓰고자할때사용 디렉터리에대해서만사용되며, 디바이스에대해서는 NULL 값을가짐 디바이스가읽기나쓰기, 혹은예외상황을일으켰는지를확인할때사용 특정디바이스에의존적인명령을수행하고자할때사용 디바이스의메모리일부를현재프로세스의메모리영역으로매핑하고자할때사용 디바이스에대한열기 / 닫기를할때사용 디바이스에대한모든연산의결과를지연하지않고즉시일어나도록할때사용 FASYNC Flag의변화를디바이스에알리기위해서사용 BSD 형식의 read/write를지원하기위한것 29
(LED 문자소스 ) 1 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <asm/uaccess.h> #include <linux/fs.h> #include <asm/io.h> #include <linux/ioport.h> #include <linux/kdev_t.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> #include <asm/ioctl.h> #include <asm/io.h> 2 #define DRIVER_AUTHOR "Hanback Electronics" #define DRIVER_DESC "led test program" #define LED_NAME "led" #define LED_MODULE_VERSION "LED V1.0" #define LED_ADDRESS 0x05000020 #define LED_ADDRESS_RANGE 0x1000 30
(LED 문자소스 ) //Global variable static int led_usage = 0; static unsigned long *led_ioremap; 3 // define functions... int led_open(struct inode *minode, struct file *mfile) { if(led_usage!= 0) return -EBUSY; led_ioremap= ioremap(led_address,led_address_range); if(!check_mem_region((unsigned long)led_ioremap,led_address_range)){ request_mem_region((unsigned long)led_ioremap, LED_ADDRESS_RANGE, LED_NAME); } else printk(kern_warning"can't get IO Region 0x%x\n", (unsigned int)led_ioremap); led_usage = 1; return 0; } 31
(LED 문자소스 ) 4 int led_release(struct inode *minode, struct file *mfile) { iounmap(led_ioremap); release_mem_region((unsigned long)led_ioremap, LED_ADDRESS_RANGE); led_usage = 0; return 0; } 5 ssize_t led_write_byte(struct file *inode, const char *gdata, size_t length, loff_t *off_what) { unsigned short *addr; unsigned char c; get_user(c,gdata); addr = (unsigned short*)led_ioremap; *addr = c 0x100; } return length; 32
(LED 문자소스 ) 6 static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.write = led_write_byte,.release = led_release, }; static struct miscdevice led_driver = {.fops = &led_fops,.name = LED_NAME,.minor = MISC_DYNAMIC_MINOR, }; 7 int led_init(void) { } misc_register(&led_driver); return 0; 33
(LED 문자소스 ) 8 void led_exit(void) { misc_deregister(&led_driver); printk(kern_info"driver: %s DRIVER EXIT\n", LED_NAME); } 9 module_init(led_init); module_exit(led_exit); 10 MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("Dual BSD/GPL"); 34
proc Filesystem proc 파일시스템프로그래밍 #include <linux/proc_fs.h> #define PROC_FILENAME "helloproc" /* Function Prototypes */ static int hello_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data); static int new_proc_entry(void) { struct proc_dir_entry *entry; printk("hello, /proc File System!\n"); if ((entry = create_proc_entry(proc_filename, S_IRUGO, NULL)) == NULL) return -EACCES; entry->read_proc = hello_read_proc; return 0; } static int hello_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len; len = sprintf(buf, "Hello, I'm in /proc file system.\n"); return len; } 35
Ex. helloworld module 에서 proc 파일시스템을이용할수있도록수정 root@sm5s4210:> insmod hello.ko [ 73.947714] hello: module license 'Daul BSD/GPL' taints kernel. [ 73.948020] Disabling lock debugging due to kernel taint [ 73.956027] Hello world [ 73.956092] Hello, /proc File System! root@sm5s4210:> cat /proc/helloproc Hello, I'm in /proc file system. root@sm5s4210:> rmmod hello [ 95.447758] Goodbye world root@sm5s4210:> 36