9 1 Kernel Programming 9 1 1 Kernel

  • Slides: 62
Download presentation

목차 9. 1 Kernel Programming 이란? 9. 1. 1 Kernel Programming? 9. 1. 2

목차 9. 1 Kernel Programming 이란? 9. 1. 1 Kernel Programming? 9. 1. 2 Kernel vs Application 9. 1. 3 Kernel programming 주의 사항 9. 1. 4 Kernel Interface 함수 9. 2 System call 추가하기 9. 2. 1 System call이란? 9. 2. 2 POSIX API와 System calls 9. 2. 3 System call 추가 따라하기 9. 2. 4 System call 원리 이해 9. 2. 5 System call 확장 9. 3 Device 9. 3. 1 9. 3. 3 9. 3. 4 9. 3. 5 9. 3. 6 Driver Device Driver란 Device Driver의 종류 Device Driver 작성 제작한 Driver 응용프로그램 만들기 Makefile 만들기 Driver를 커널에 모듈로 등록 및 해제하기 2

9. 1 Kernel Programming 이란? ± ± ± Linux kernel core 기능 추가 Linux

9. 1 Kernel Programming 이란? ± ± ± Linux kernel core 기능 추가 Linux kernel 알고리즘 개선 Linux kernel 모듈 프로그래밍 3

9. 1. 1 Kernel vs Application ± 수행 방법 ® Application Program: 처음부터 순차적으

9. 1. 1 Kernel vs Application ± 수행 방법 ® Application Program: 처음부터 순차적으 로 수행 ® Kernel: 응용프로그램을 위한 system call 이나 인터럽트 핸들러를 수행하기 위해 비 동기 적으로 수행 System call Interrupt Application Kernel 4

9. 1. 1 Kernel vs Application ± Library ® Kernel: kernel에서 export 하는 것들만

9. 1. 1 Kernel vs Application ± Library ® Kernel: kernel에서 export 하는 것들만 사용할 수 있다. ® Application Program: 모든 library를 link 하고 사용할 수 있 다. ± Kernel mode vs User mode ® Kernel Program: 모든 것이 허용된다. ® Application Program: user mode에서 수행되며 하드웨어에 직접 접근하는 것과 메모리에 대한 허용되지 않은 접근이 제 한된다. 5

9. 1. 1 Kernel vs Application ± Address Space ® Application Program과 Kernel Program은

9. 1. 1 Kernel vs Application ± Address Space ® Application Program과 Kernel Program은 서로 다른 메모리 매핑법을 가지고 있으며, 프로그램 코드는 서로 다른 address space를 가지고 있다. Kernel address space 4 G byte User address space 1 G byte 3 G byte 6

9. 1. 3 Kernel Interface 함수 ± 주의 사항 ® Kernel program은 일반적인 library를

9. 1. 3 Kernel Interface 함수 ± 주의 사항 ® Kernel program은 일반적인 library를 사용하지 못하고 kernel에서 export 해준 함수들 만을 사용할 수 있다. ± Kernel interface 함수 분류 ® Kernel 에서 제공하는 함수 중 kernel programming에 자주 사용되는 함수는 다음과 같이 분류할 수 있다. ® Port I/O ® Interrupt ® Memory ® Synchronization ® Kernel message 출력 ® Device Driver register 11

9. 1. 3 Kernel Interface 함수 ± I/O device 와 data를 주고 받기 위한

9. 1. 3 Kernel Interface 함수 ± I/O device 와 data를 주고 받기 위한 함수들 ® unsigned inb(unsigned port) Port에서 1 byte를 읽는다. ® unsigned inw(unsigned port) ® Port에서 2 byte를 읽는다. ® unsigned inl(unsigned port) ® Port에서 4 byte를 읽는다. ® ® unsigned outb(char value, unsigned port) Port에 1 byte value를 쓴다. ® unsigned outw(short int value, unsigned port) ® Port에 2 byte value를 쓴다. ® unsigned outl(long int value, unsigned port) ® Port에 4 byte value를 쓴다. ® 12

9. 1. 3 Kernel Interface 함수 ± I/O device 와 data를 주고 받기 위한

9. 1. 3 Kernel Interface 함수 ± I/O device 와 data를 주고 받기 위한 함수들 ® ® ® void insb(unsigned port, void *addr, unsigned long count) ® Port에서 count bytes를 읽어서 메모리의 addr 주소부터 저장 void insw(unsigned port, void *addr, unsigned long count) ® Port에서 16 bit * count 만큼 읽어서 메모리의 addr 주소부터 저장 void insl(unsigned port, void *addr, unsigned long count) ® Port에서 32 bit * count 만큼 읽어서 메모리의 addr 주소부터 저장 void outsb(unsigned port, void *addr, unsigned long count) ® Memory의 addr번지 에서부터 count bytes를 읽어서 port에 쓴다. void outsw(unsigned port, void *addr, unsigned long count) ® Memory의 addr번지 에서부터 count * 16 bit를 읽어서 port에 쓴다. void outsl(unsigned port, void *addr, unsigned long count) ® Memory의 addr번지 에서부터 count * 32 bit를 읽어서 port에 쓴다. 13

9. 1. 3 Kernel Interface 함수 ± 인터럽트의 설정 및 처리에 관한 함수(or 매크로)

9. 1. 3 Kernel Interface 함수 ± 인터럽트의 설정 및 처리에 관한 함수(or 매크로) ® cli()/sti() ® clear/set interrupt enable ® save_flags(unsigned long flag), restore_flags(unsigned long flag) ® status register의 내용을 저장하고 복원 ® save_flags(), restore_flags() 두 매크로는 같은 함수 안에서 호 출 되어야 한다. flag를 다른 함수로 pass해서는 안된다. ® int requst_irq(unsigned int irq, void (*handler)(int), unsigned long flags, const char *device) ® 커널로부터 IRQ를 요청하고, 이 IRQ에 대한 interrupt handler 를 install 15

9. 1. 3 Kernel Interface 함수 ± 인터럽트의 설정 및 처리에 관한 함수(or 매크로)

9. 1. 3 Kernel Interface 함수 ± 인터럽트의 설정 및 처리에 관한 함수(or 매크로) ® void free_irq(unsigned int irq) ® request_irq()에서 획득한 irq를 반납함 16

9. 1. 3 Kernel Interface 함수 ± Kernel에서 동적 메모리를 할당할 때 사용하는 함수

9. 1. 3 Kernel Interface 함수 ± Kernel에서 동적 메모리를 할당할 때 사용하는 함수 들 ® void * kmalloc(unsigned int len, int priority) 커널 메모리 할당. 128~131056 byte까지 가능 ® priority: GFP_BUFFER, GFP_ATOMIC, GFP_USER, GFP_KERNEL ® 물리적으로 연속적인 메모리를 할당한다. ® ® void kfree(void *obj) ® kmalloc()에서 할당 받은 커널 메모리를 반납 17

9. 1. 3 Kernel Interface 함수 ± 사용자 공간과 커널공간 사이에 데이터를 공유하기 위한

9. 1. 3 Kernel Interface 함수 ± 사용자 공간과 커널공간 사이에 데이터를 공유하기 위한 함수 ® unsigned long copy_from_user(void *to, const void *from, unsigned long n) ® 사용자 주소공간에서 n byte만큼 data 복사. ® unsigned long copy_to_user(void *to, const void *from, unsigned long n) ® 사용자 주소 공간에 n byte만큼 data 복사 ® void * memset(void *s, char c, sizt_t count) ® 메모리 s에 c를 count만큼 복사 ® put_user(datum, ptr) / get_user(ptr) ® 사용자 공간에 datum을 전달하거나 가져오기 위한 매크로 19

9. 1. 3 Kernel Interface 함수 ± 동기화 ® void sleep_on(struct wait_queue **q) ®

9. 1. 3 Kernel Interface 함수 ± 동기화 ® void sleep_on(struct wait_queue **q) ® q의 번지를 event로 sleep하며, uninterruptible ® void sleep_in_interruptible(struct wait_queue **q) ® q의 번지를 event로 sleep하며, interruptible ® void wake_up(struct wait_queue **q) ® sleep_on(q)에 의해 sleep한 task를 wakeup ® void wake_up_interruptible(struct wait_queuq **q) ® sleep_on_interruptible(q)에 의해 sleep한 task를 wakeup 20

9. 1. 3 Kernel Interface 함수 ± Standard out 으로 메시지를 출력하기 위한 함수

9. 1. 3 Kernel Interface 함수 ± Standard out 으로 메시지를 출력하기 위한 함수 ® printk(const char *fmt, …. ) printf의 커널 버전 ® printk(LOG_LEVEL_ message) ® LOG_LEVEL: KERN_EMERG, KERN_ALERT, KERN_ERR, KERN_WARNING, KER_INFO, KERN_DEBUG ® ®예 printk(“<1>Hello, World”); ® printk(KERN_WARNING”warning… n”); ® 21

± 9. 1. 3 Kernel Interface 함수 Device Driver register ® int register_xxxdev (unsigned

± 9. 1. 3 Kernel Interface 함수 Device Driver register ® int register_xxxdev (unsigned int major, const char *name, struct file_operations *fops) ® character/block driver를 xxxdev[major]에 등록 ® xxx: blk/chr ® int unregister_xxxdev(unsigned int major, const char *name) ® xxxdevs[major]에 등록되 있는 device driver를 제거 ® int register_netdev(const char *name) int unregister_netdev(const char *name) ® ® MAJOR(kdev_t dev)/MINOR(kdev_t dev) ® 장치번호dev로부터 major/minor 번호를 구함 22

9. 2 System Call User Application User Level Kernel Level API(시스템 라이브러리) System Call

9. 2 System Call User Application User Level Kernel Level API(시스템 라이브러리) System Call Interface File System Buffer Cache Process Management (IPC-interprocess Communication, scheduling, Memory Management Process Acounting, etc) Charater Block Device Driver Hardware Interface 23

9. 2. 1 What’s System Calls? ± user mode process와 kernel 간의 interface ®

9. 2. 1 What’s System Calls? ± user mode process와 kernel 간의 interface ® user mode process는 일반적으로 kernel 영역에 직접적으로 접근할 수 없다. kernel의 자료구조 및 hardware 에 대한 접근 불가 ® user mode process가 kernel이 가지고 있는 시스템의 상태 정보를 열람하거나 hardware에 접근하여 hardware를 통제하기 위해서는 kernel 과의 communication channel이 필요. User Application API(시스템 라이브러리) System Call Interface User Level Kernel Level File System Process Management (IPC-interprocess Buffer Cache Communication, scheduling, Memory Management Process Acounting, etc) Charater Block Device Driver Hardware Interface 24

9. 2. 3 System Call 추가 따라하기 ± 가정 ® /PXA 270/kernel/linux-2. 6. 11

9. 2. 3 System Call 추가 따라하기 ± 가정 ® /PXA 270/kernel/linux-2. 6. 11 -h 270 -tku_v 1. 1/ 에 target system용 kernel source code가 있다. ® 위 디렉토리를 앞으로의 설명에서 [kernel]로 대치한다. 28

9. 2. 3 System Call 추가 따라하기 ®calls. S 의 내용은 다음과 같다. *

9. 2. 3 System Call 추가 따라하기 ®calls. S 의 내용은 다음과 같다. * <kernel>/arch/arm/kernel 33

9. 2. 3 System Call 추가 따라하기 ± Kernel image를 /tftpboot에 복사 ® [kernel]

9. 2. 3 System Call 추가 따라하기 ± Kernel image를 /tftpboot에 복사 ® [kernel] 디렉토리에서 커널을 컴파일하고 생성된 커널 이미지를 target에 전송하기 위해 /tftpboot로 복사. # cd [kernel] # make menuconfig; make z. Image # cp [kernel]/arch/arm/boot/z. Image /tftpboot 37

9. 2. 3 System Call 추가 따라하기 ± System Call을 호출하는 user application작성 ®

9. 2. 3 System Call 추가 따라하기 ± System Call을 호출하는 user application작성 ® library를 만들지 않고 user application을 만드는 경우 unistd. h 에 정의된 매크로로 System Call 처리함수의 타입과 이름을 인자로 넘겨준다. *sys 접두어를 붙이지 않는다. /* linux/unistd. h */ #else #define _syscall 0(type, name) #define __syscall(name) “swit” __sys 1(__NR_##name)”nt” type name(void){ register long __res __asm__(“r 0”); __asm__ __volatile__{ __syscall(name) : “=r” (__res) : “lr”); __syscall_return(type, __res); } Software interrupt를 발생시키는 assemblier Unistd에서 정의한 symbol로 바꿔준 다. 38

9. 2. 3 System Call 추가 따라하기 ± 지금까지의 과정을 정리해 보면 다음과 같다.

9. 2. 3 System Call 추가 따라하기 ± 지금까지의 과정을 정리해 보면 다음과 같다. Kernel 수정 Application 작성 unistd. h 에 system call 번호 정의 calls. S 에 system call을 호출하는 System Call 처리함수 등록 application 작성 System Call 처리 함수 구현 Application 을 Target system에 download Kernel 재 컴파일 Applicatin 실행 Kernel을 Target system에 download 40

9. 2. 4 System Call 원리 이해 ± Linux 에서의 system call 처리 ®

9. 2. 4 System Call 원리 이해 ± Linux 에서의 system call 처리 ® Interrupt 처리 매커니즘 사용 ± Interrupt ® 주변 장치와 커널이 통신하는 방식 중 하나로 주변 장치가 자 신에게 발생한 비동기적 사건을 kerenl에게 알리는 메커니즘. Kernel CPU RTC IDT(IVT) disk tty network PIC 0 1 2 3 4 timer_interrupt() hd_interrupt() tty_interrupt() el 3_interrupt() Interrupt handlers timer_interrupt() hd_interrupt() … cdrom 41

9. 2. 4 System Call 원리 이해 ± System Call User task Kernel main

9. 2. 4 System Call 원리 이해 ± System Call User task Kernel main () { …. hyubscall() } /* [kernel]/arch/arm/kernel/entry-common. S*/ ENTRY(vector_swi) get_scno … adr tbl, sys_call_table … ldrcc pc, [tbl, scno, lsl #2] Name hybuscall System_call_table __NR_hybuscall 281 1 2 hybuscall() { …. swi …. 3 4 __NR_##name } 281 sys_exit() sys_fork() sys_read() sys_write() ……… sys_hybuscall( ) sys_hybuscall() { printk(“…”); } 42

Device Driver www. huins. com

Device Driver www. huins. com

목차 9. 3. 1 Device Driver란 9. 3. 1 Device Driver의 종류 9. 3.

목차 9. 3. 1 Device Driver란 9. 3. 1 Device Driver의 종류 9. 3. 3 Device Driver 작성 9. 3. 4 제작한 Driver 응용프로그램 만들기 9. 3. 5 Makefile 만들기 9. 3. 6 Driver를 커널에 모듈로 등록 및 해제하기 44

9. 3. 1 Device Driver란(1) ± ± 물리적인 hardware 장치를 다루고 관리하는 software user

9. 3. 1 Device Driver란(1) ± ± 물리적인 hardware 장치를 다루고 관리하는 software user application이 driver에게 요청을 하면, driver는 hardware를 구동시켜서 목적을 달성 major number와 minor number를 이용하여 각각의 devices을 구분하여 사용 device와 system memory 간에 data의 전달을 담당하는 kernel 내부 기능 45

9. 3. 1 Device Driver란(2) User application level task System call interface Kernel level

9. 3. 1 Device Driver란(2) User application level task System call interface Kernel level File system DDI (device driver interface) Char Driver Block Driver Network Driver Hardware level terminal keyboard Hard disk CD-ROM ethernet 46

9. 3. 2 Device Driver의 종류 Driver 종류 설 명 등록함수 명 Char Driver

9. 3. 2 Device Driver의 종류 Driver 종류 설 명 등록함수 명 Char Driver device를 file처럼 취급하고 접근하여 직접 read/write 를 수행, data 형태는 stream 방식으로 전송 EX) console, keyboard, serial port driver등 register_c hrdev() Block Driver disk와 같이 file system을 기반으로 일정한 blcok 단 위로 data read/write를 수행 EX) floppy disk , hard disk, CD-ROM driver 등 register_ blkdev() Network Driver network의 physical layer와 frame 단위의 데이터를 송수신 EX) Ethernet device driver(eth 0) register_ netdev() 47

9. 3. 3 Device Driver의 작성(1) ± 기본적인 device driver의 형태 Printf와 같은 기능의

9. 3. 3 Device Driver의 작성(1) ± 기본적인 device driver의 형태 Printf와 같은 기능의 커널 코드를 위한 함수 #include <linux/kernel. h> <linux/module. h> <linux/fs. h> <linux/init. h> module_init(function_init) module_exit(function_exit) static int device_open(); static int device_release(); ssize_t device_read(); static ssize_t device_write(); int init_function(); void cleanup_function(); static struct file_operations device_fops={. open = device_open, . release = device_release, . read = device_read, . write = device_write, }; 필요한 header. 모듈소스가 include해 야함 “파일시스템”헤더는 디바이스 드라이 버를 쓰는데 필요한 헤더파일 Header Files Module_init()과 module_exit() 정의됨 Module 설치 시 초기화 수행 insmod로 모듈을 적재하면 이 함수가 불려진다. 이 함수 안에서 regiseter_chrdev() 커널 Module 함수를 뷸러서 제거 모둘을 적재한다. 시 반환작업 수행 Function Prototypes rmmod로 적재된 모듈을 삭제할 경우 이 함수 가 불려진다. unregister_chrdev() 커널 함수를 불려서 모듈을 삭제한다. application에서 쓰는 함수와 driver에 서 쓰는 함수를 mapping Device Opertations 48

9. 3. 3 Device Driver의 작성(2) #include <linux/module. h> #include <linux/fs. h> #include <linux/kernel.

9. 3. 3 Device Driver의 작성(2) #include <linux/module. h> #include <linux/fs. h> #include <linux/kernel. h> 모듈을 커널에 적재하는 함수 register_chrdev()의 parameter로써 0으로 설정하면 major값 을 뒤에서부터 자동으로 빈자리로 등록시킨다 #include <linux/init. h> #define DEV_NAME “SKELETON" int skeleton_major = 0; int result; register_chrdev()함수가 return하는 값을 넘겨받음. 0보다 적으면 모듇 등록 실 패 application에서 open()함수로 driver를 호출할때 실행되 는 함수 /////// 함 수 정 의 /////// application에서 close()함수로 driver를 닫을때 실행 되는 함수 int skeleton_open (struct inode *inode, struct file *filp); int skeleton_release (struct inode *inode, struct file *filp); ssize_t skeleton_read (struct file *filp, unsigned int *buf, size_t count, loff_t *f_pos); ssize_t skeleton_write (struct file *filp, unsigned int *buf, size_t count, loff_t *f_pos); int skeleton_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned int arg); application에서 kernel로부터 넘어온 값을 처리하 int skeleton_init(); 는 함수 void skeleton_exit(); 49

9. 3. 3 Device Driver의 작성(3) int device_open (struct inode *inode, struct file *filp)

9. 3. 3 Device Driver의 작성(3) int device_open (struct inode *inode, struct file *filp) { printk("test_open start!!!n"); return 0; } int device_release (struct inode *inode, struct file *filp) { return 0; } ssize_t device_read (struct file *filp, unsigned int *buf, size_t count, loff_t *f_pos) { printk("read() ^^n"); Application에서 read함수를 호출하면 terminal 에 문자 출력 return 0; } ssize_t device_write (struct file *filp, unsigned int *buf, size_t count, loff_t *f_pos) { return 0; } 50

9. 3. 3 Device Driver의 작성(4) int device_ioctl(struct inode *inode, struct file *filp, unsigned

9. 3. 3 Device Driver의 작성(4) int device_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned int arg) { printk("ioctl() ^^n"); Ioctl()호출이 성공하면 문자 출력 return 0; } struct file_operations test_fops = {. open = device_open, . read = device_read, . write = device_write, . ioctl = device_ioctl, . release = device_release, }; module_init(skeleton_init); 파일연산구조체는 char device driver에 대한 모든 연산을 포함한다. 즉 여기 에 정의된 함수를 통해서 커널이 접근하게 된다. 즉 application에서 open함 수를 호출하면 이는 driver의 test_open()과 mapping되어있어 이를 실행하게 된다. insmod로 불려지는 함수 module_exit(skeleton_exit); rmmod로 불려지는 함수 51

9. 3. 3 Device Driver의 작성(5) int skeleton_init(void) { result = register_chrdev(test_major, DEV_NAME, &test_fops);

9. 3. 3 Device Driver의 작성(5) int skeleton_init(void) { result = register_chrdev(test_major, DEV_NAME, &test_fops); driver를 커널에 모듈로 등록하는 함수, result에 major 값이 넘어옴 if (result < 0) { printk(KERN_WARNING "%s: can't get major %dn", DEV_NAME, test_major); return result; } printk("<1> init module success!!. . %s major number : %dn", DEV_NAME, result); return 0; driver를 커널에서 삭제하는 } 함수 void skeletin_exit(void) { if( !unregister_chrdev(result, DEV_NAME) ) printk("%s cleanup_module success. . . n", DEV_NAME); else printk("%s cleanup_module fail. . . n", DEV_NAME); } 52

9. 4 제작한 Driver 응용프로그램 만들기 #include <stdio. h> <fcntl. h> <string. h> <errno.

9. 4 제작한 Driver 응용프로그램 만들기 #include <stdio. h> <fcntl. h> <string. h> <errno. h> File에 대한 제어와 설정 Error에 관한 header file int fd; mknod를 통하여 /dev에 만들어진 node(file)을 open한다. int main(int argc, char **argv) { if( (fd = open("/dev/SKELETON", O_RDWR)) < 3) { fprintf(stderr, "/dev/testdd device file open error!!. . %sn", strerror(errno)); return 0; } read(fd, 0, 0); ioctl(fd, NULL, 0); close(fd); return 0; } 53

9. 5 Makefile 만들기 크로스 컴파일러 경로 CC : = /opt/iwmmxt-1. 0. 0/bin/arm-linux-gcc KDIR

9. 5 Makefile 만들기 크로스 컴파일러 경로 CC : = /opt/iwmmxt-1. 0. 0/bin/arm-linux-gcc KDIR : = /PXA 270/kernel/linux-2. 6. 11 -h 270 -tku_v 1. 1 TEST_TARGET = userapp TEST_OBJS = userapp. o TEST_SRCS = userapp. c 모듈을 사용하게될 커널의 소스 위치 Compile에서 최종으로 모듈파일로 생성될 목적 파일 obj-m : = skeleton. o 모듈을 컴파일하는 make module 수행 응용프로그램 매크로 build: $(TEST_TARGET) make -C $(KDIR) SUBDIRS='pwd' modules $(TEST_TARGET) : $(TEST_OBJS) $(CC) -o $@ $(TEST_OBJS) clean: rm -rf *. o *. ko rm -f$(TEST_TARGET) 현재의 디렉토리를 다음 커널 디렉토리로 변경 현재 디렉토리를 커널 밑의 서브 디렉토리로 붙이기 타겟파일 컴파일 명령 54

9. 6 Driver를 커널에 모듈로 등록 및 해제하기(1) ± Char Device Driver 등록 방법

9. 6 Driver를 커널에 모듈로 등록 및 해제하기(1) ± Char Device Driver 등록 방법 ® 외부와 device driver는 file interface (node를 의미)를 통해 연결 ® Device driver는 자신을 구별하기 위해 고유의 major number를 사용 ± 장치의 등록과 해제 ® 등록 : int register_chrdev(unsigned int major, const char *name, stuct file_operations *fops) Major : 등록할 major number. 0이면 사용하지 않는 번호 중 자동으로 할당 ® Name : device의 이름 ® Fops : device에 대한 file 연산 함수들 ® ® 해제 : int unregister_chrdev(unsigned int major, const char *name) 55

9. 6 Driver를 커널에 모듈로 등록 및 해제하기(2) kernel device driver user program open

9. 6 Driver를 커널에 모듈로 등록 및 해제하기(2) kernel device driver user program open close read write system call file operations device_open device_close device_read device_write device 56

9. 6 Driver를 커널에 모듈로 등록 및 해제하기(4) ± mknod 명령으로 디바이스 드라이버에 접근할

9. 6 Driver를 커널에 모듈로 등록 및 해제하기(4) ± mknod 명령으로 디바이스 드라이버에 접근할 수 있는 장치 파일 생성 ® mknod [device file name] [type] [major] [minor] Ex] #mknod SKELETON c 252 0 ± C는 char device drive의미, block device drive는 b를 사용 mdev_t : 장치의 major, minor number를 표현하는 자료구조 ® MAJOR() : kdev_t에서 major number를 얻어내는 매크로 Ex] MAJOR(inode->i_rdev); ® MINOR() : kdev_t에서 minor number를 얻어내는 매크로 ® cat /proc/devices 명령으로 현재 로드된 디바이스 드라이버 확인 58