Chap 6 모듈프로그래밍및 디바이스드라이버구현

Similar documents
untitled

Embeddedsystem(8).PDF

슬라이드 1

Adding a New Dev file

PowerPoint 프레젠테이션

Chapter #01 Subject

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

untitled

<4D F736F F F696E74202D205BBAB0C3B75D20B8AEB4AABDBA20B5F0B9D9C0CCBDBA20B5E5B6F3C0CCB9F620B8F0B5A82E >

망고100 보드로 놀아보자 -13

교육지원 IT시스템 선진화

ECE30076 Embedded System Programming - LED Device Driver

(Microsoft PowerPoint - Device Driver [\310\243\310\257 \270\360\265\345])

(Microsoft PowerPoint - Device Driver [\310\243\310\257 \270\360\265\345])

교육지원 IT시스템 선진화

디바이스드라이버 (Device Driver) Driver is literally a subject which drive a object. 응용프로그램에서하드웨어장치를이용해서데이터를직접읽고쓰거나제어해야하는경우에디바이스드라이버를이용 하드웨어를제어하는프로그램과애플리케이션에서

KEY 디바이스 드라이버

슬라이드 1

Microsoft PowerPoint - 10-EmbedSW-11-모듈

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

Microsoft Word doc

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

Chap 5 루트파일시스템 (Root F/S)

임베디드시스템설계강의자료 6 system call 1/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>

PowerPoint 프레젠테이션

Microsoft PowerPoint - lab14.pptx

PowerPoint 프레젠테이션

K&R2 Reference Manual 번역본

슬라이드 1

커알못의 커널 탐방기 이 세상의 모든 커알못을 위해서

제1장 Unix란 무엇인가?

¼ÒÇÁÆ®-12È£-ÃÖÁ¾¿Ï¼º

Mango-AM335x LCD Type 커널 Module Parameter에서 변경하기

10.

이도경, 최덕재 Dokyeong Lee, Deokjai Choi 1. 서론

API 매뉴얼

vi 사용법

11장 포인터

PowerPoint 프레젠테이션

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

The Pocket Guide to TCP/IP Sockets: C Version

Microsoft PowerPoint - 03-Development-Environment-2.ppt

금오공대 컴퓨터공학전공 강의자료

06Àå

04디바이스드라이버

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

로봇SW교육원 강의자료

Microsoft PowerPoint - chap10-함수의활용.pptx

Microsoft PowerPoint - chap06-5 [호환 모드]

교육지원 IT시스템 선진화

The Pocket Guide to TCP/IP Sockets: C Version

제1장 Unix란 무엇인가?

PA for SWE2007

PowerPoint 프레젠테이션

Microsoft Word doc

Microsoft PowerPoint - Lecture_Note_5.ppt [Compatibility Mode]

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

이번장에서학습할내용 동적메모리란? malloc() 와 calloc() 연결리스트 파일을이용하면보다많은데이터를유용하고지속적으로사용및관리할수있습니다. 2

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>

BMP 파일 처리

Mango220 Android How to compile and Transfer image to Target

Microsoft PowerPoint - chap06-2pointer.ppt

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

Microsoft Word - LKP-RTD 사용자 설명서

<4D F736F F F696E74202D E20B3D7C6AEBFF6C5A920C7C1B7CEB1D7B7A1B9D62E >

Microsoft PowerPoint - additional01.ppt [호환 모드]

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

Microsoft Word - MPC850 SPI Driver.doc

PowerPoint 프레젠테이션

Microsoft PowerPoint APUE(Intro).ppt

Chap 7

제12장 파일 입출력

Microsoft Word doc

ABC 11장

윤성우의 열혈 TCP/IP 소켓 프로그래밍

Microsoft PowerPoint - IOControl [호환 모드]

Mango-IMX6Q mfgtool을 이용한 이미지 Write하기

Microsoft PowerPoint - chap01-C언어개요.pptx

망고100 보드로 놀아보자-4

Microsoft PowerPoint APUE(File InO).pptx

chap7.key

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070>

Lab 3. 실습문제 (Single linked list)_해답.hwp

PowerPoint Presentation

/chroot/lib/ /chroot/etc/

Chap 10 안드로이드커널 (Kernel)

chap10.PDF

Raspbian 설치 라즈비안 OS (Raspbian OS) 라즈베리파이 3 Model B USB 마우스 USB 키보드 마이크로 SD 카드 마이크로 SD 카드리더기 HDM I 케이블모니터

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202839C1D6C2F7207E203135C1D6C2F >

Microsoft PowerPoint Driver-Char-3-2.ppt

<4D F736F F F696E74202D203137C0E55FBFACBDC0B9AEC1A6BCD6B7E7BCC72E707074>

chap 5: Trees

Transcription:

Chap 6 모듈프로그래밍및 디바이스드라이버구현

Chap 6. 모듈프로그래밍및디바이스드라이버구현 1. 모듈프로그래밍 1.1. Kernel Module 1.1.1. Kernel Module 모듈은커널의구성요소로서, 리눅스시스템이부팅된후에동적으로 load, unload 할수있다. 이런특징으로인해커널을다시컴파일하거나시스템을재부팅하지않고도커널의일부분을교체할수있다. 우리가익히들어온디바이스드라이버, 파일시스템, 그리고네트워크프로토콜등도이런모듈로만들어져커널에포함된것이다. 커널에서는의존성이라는또다른특성이존재한다. 커널을구성하는모듈에는컴파일한커널의버전정보가들어가는데, 현재실행되고있는커널버전과같아야한다는의미이다. 버전이다르면에러가발생하게된다. 모듈의버전정보는리눅스커널소스 /include/linux/module.h에정의되어있다. #include <linux/list.h> #include <linux/stat.h> #include <linux/compiler.h> ( 중략 ) /* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE(name) /* Some toolchains use a `_' prefix for all user symbols. */ #ifdef CONFIG_SYMBOL_PREFIX #define MODULE_SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX #else #define MODULE_SYMBOL_PREFIX "" #endif #define MODULE_NAME_LEN MAX_PARAM_PREFIX_LEN struct kernel_symbol { unsigned long value; const char *name; }; struct modversion_info { unsigned long crc; char name[module_name_len]; }; struct module; 2

Chap 6. 모듈프로그래밍및디바이스드라이버구현 1.1.2. Kernel Link insmod (install module) 시, 위변수를이용하여버전검사를하게된다. 따라서모듈버전정보는전체모듈에서하나만존재해야한다. 커널에대한모듈프로그램 ( 예로서, 지금다루고있는디바이스드라이버 ) 이일반적인응용프로그램과다른점은 main() 함수대신에커널에 loading( 적재 ) 와 unloading( 삭제 ) 될때호출되는 int init_module(void) 함수와 void cleanup_module() 함수가존재한다는것이다. < 커널에모듈이 link 되는개념도 > 1.1.3. Module Programming Test 1 module_test.c ( hello.c ) 이모듈은함수두개를정의한다. 하나는커널이모듈을로드할때 (hello_init), 다른하나는모듈을제거할때 (hello_exit) 호출한다. module_init과 module_exit는각함수의임무를나타내는커널매크로이다. MODULE_LICENSE() 매크로는모듈에대한라이선스를명시하고, MODULE_AUTHOR() 매크로는모듈의저작자를명시한다. printk 함수는리눅스커널에들어있으며, 모듈에서사용할수있다. 이함수는표준 C 함수인 printf와유사한동작한다. 커널은 C 라이브러리없이독자적으로구동하기때문에이같은함수가별도로필요하다. 모듈에서는 printk를항상호출할수있다. 이는 insmod로모듈을로드한후, 모듈이커널에링크되고나면커널의 3

Chap 6. 모듈프로그래밍및디바이스드라이버구현 심벌테이블을참조할수있기때문이다. KERN_ALERT라는문자열은메시지의우선순위이다. 이모듈에서는높은우선순위를지정했다. 기본우선순위를지정하면실행중인커널버전의 klogd 데몬시스템구성등에따라메시지가적재적소에나타나지않을지도모르기때문이다. /* Module example FILE : hello_module.c */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huins"); static int module_begin(void) { printk(kern_alert "Hello, Wellcome to module!!\n"); return 0; } static void module_end(void) { } printk(kern_alert "Goodbye, Exit module!!\n"); module_init(module_begin); module_exit(module_end); 아래와같이 Makefile 을만들어위에서만든소스를컴파일한다. 2 Makefile 아래와같이 Makefile을만들면된다. 다만 KDIR 은커널소스디렉터리를의미하기때문에사용자환경에맞추어서수정하면된다. obj-m := hello_module.o KDIR := /work/achroimx6q/kernel PWD := $(shell pwd) all: driver driver: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* 4

Chap 6. 모듈프로그래밍및디바이스드라이버구현 rm -rf *.o 3 컴파일모듈소스디렉터리에서 make를입력하여컴파일한다. <Host PC> root@ubuntu:/# cp -a /media/achro-i.mx6q-1/dvd-1_src/examples/linux/module/module.tar.gz /root/temp root@ubuntu:/# cd /root/temp root@ubuntu:~/temp# tar xvf module.tar.gz -C /work/achroimx6q/ root@ubuntu:/# cd /work/achroimx6q/module root@ubuntu:/work/achroimx6q/module# make 4 실행임베디드보드를부팅한다음 NFS를통하여 hello.ko 파일을보드로복사한다. 복사가완료되면, insmod를통해서 hello.ko를커널에적재한다. 호스트에서컴파일된모듈을 /nfsroot 디렉터리로복사한다. <Host PC> root@ubuntu:/work/achroimx6q/examples/linux/module# mkdir /nfsroot ( 해당디렉터리가없다면생성 ) root@ubuntu:/work/achroimx6q/examples/linux/module# cp hello_module.ko /nfsroot <targt board> minicom 실행 -> board power on -> achroi.mx 진입 -> ifconfig 를이용한 ip 셋업 -> 리눅스호스트 mount Achro-i.MX6Q 에서는 mount 를통해호스트와연결한다. <TargetBoard> root@achroimx6q:/# mkdir /mnt/nfs ( 해당디렉터리가없다면생성 ) root@achroimx6q:/# mount -t { 리눅스호스트 ip}:/nfsroot /mnt/nfs o rw,rsize=1024,nolock 연결이완료되면마운트된해당경로로이동하여 insmod 를실행하거나, Achro-i.MX6Q 의파일시스템으로해당드라이버를복사한후에 insmod 를이용하 여커널에적재한다. 5

Chap 6. 모듈프로그래밍및디바이스드라이버구현 모듈이커널에적재되면 Hello, Wellcome to module!! 이라는문자열이출력되고, 커널에서내리게되면 "GoodBye, Exit Module!!" 이라는문자열이출력된다. <TargetBoard> root@achro_imx6q:/# cp /mnt/nfs/hello_module.ko /root root@achro_imx6q:/# cd /root root@achro_imx6q:/root# insmod hello_module.ko Hello, Welcome to module!! root@achro_imx6q:/root# rmmod hello_module GoodBye, Exit Module!! 1.1.4. Usage Count 리눅스시스템은각모듈들이안전하게제거될수있도록 usage count를유지한다. 마운트된파일시스템을사용중에는제거할수없는것처럼, 시스템은 usage count 정보를가지고대상모듈이현재사용중인지판단하고, 사용중이라면제거하지못하도록한다. 이런 usage count는 <linux/module.h> 에정의되어있는아래세가지매크로를이용하여조작할수있다. 1 MOD_INC_USE_COUNT - 현재모듈의 usage count 를 1 만큼증가시킨다. 2 MOD_DEC_USE_COUNT - 현재모듈의 usage count 를 1 만큼감소시킨다. 3 MOD_IN_USE - 현재 usage count 값이 0 인지를판별한다. 만약 usage count를제대로관리하지못하면더이상필요하지않은모듈도제거할수없게된다. 개발과정에서는필요하지않은모듈을제거못하는경우를자주경험하게된다. 예를들어, 모듈의 NULL pointer 참조로인해서실행되던프로세스가갑자기죽어버리게되면 usage count를감소시키지못하는경우가발생하게된다. 이런경우실행중인프로세스가없음에도불구하고 usage count값이 0이되지않을수있다. 개발중에는 MOD_INC_USE_COUNT와 MOD_DEC_USE_COUNT를의미없는연산으로재정의 6

Chap 6. 모듈프로그래밍및디바이스드라이버구현 하여아예 usage count를사용하지않거나, ioctl() 함수를사용하여 usage count를강제로 0으로만드는 ( 실제완성된모듈에서는이런방법들을쓰지않지만 ) 등의방법들을써서시간을절약하기도한다. 현재모듈의 usage count는 /proc/modules 를읽어보면알수있다. 이파일은현재시스템에적재되어있는모듈들을보여주며, 다음과같은형태를가지고있다. 아래는모듈관련리눅스명령어이다. 명령어용도 insmod rmmod lsmod module 을설치 (install) 실행중인 modules 을제거 (unload) Load 된 module 들의정보를표시 depmod 커널내부에적재된모듈간의의존성을검사한다. modprobe modinfo 모듈간의존성을검사하여그결과누락된다른모듈을찾아서 적재한다. 목적파일을검사해서관련된정보를표시 7

2. 디바이스드라이버 2.1. 리눅스디바이스드라이버본장에서는리눅스디바이스드라이버에대한정의와종류그리고구성요소에대해기술한다. 2.1.1. 정의디바이스 (Device) 란 LCD, USB, Ethernet, PCMCIA, CF, AUDIO 등과같이컴퓨터시스템 (Flash Memory, SDRAM등의 ) 이외의주변장치를말하고, 드라이버 (Driver) 란이러한하드웨어장치를제어하고관리하는방법을컴퓨터시스템 ( 즉, 운영체제 ) 에게알려주는프로그램을말한다. 우리는보통이드라이버를디바이스드라이버라고부르는데, 디바이스를구동시키기위해서는반드시필요한프로그램이라고생각하면된다. 리눅스커널에서지원하는드라이버도있지만 ( 예를들면, 마우스, 키보드, Floppy 등 ) 그렇지않은경우에는해당드라이버를찾아설치하거나만들어주어야한다. 이런디바이스드라이버의기능을간략하게요약하면 물리적인하드웨어장치를다루고관리하는소프트웨어로서 Major number( 주번호 ) 와 Minor mumber( 부번호 ) 를이용하여각디바이스를구분하여사용하는커널의일부분이다. 응용프로그램이하드웨어 ( 즉, 디바이스 ) 를제어할수있도록인터페이스를제공해주는코드이며, 프로그래머가하드웨어에독립적인프로그램을작성하도록도와준다. 표준화된 call을이용하여디바이스와정보를주고받는역할을하며, UNIX의기본적인특징중하나인추상화장치를다루는데사용한다. 모든하드웨어장치들은보통의파일처럼보이지만파일을다루는데쓰이는표준 system call (open, read, write, close 등 ) 을사용한다. 2.1.2. Device Driver와 Kernel 어플리케이션에서디바이스드라이버를제어하려면아래그림과같은방법으로하드웨어와통신할수있다. 어플리케이션은어플리케이션영역 (Application Area, User Area) 에서만사용된다. 사용중, 디바이스드라이버에접근할필요가있으면응용애플리케이션의프로세스는시스템콜 (System Call, trap) 을발생시킨다. 시스템콜이발생되면리눅스커널영역에서는해당장치와연결하기위해서 VFS를통해디바이스드라이버로값을전달하거나받아오게된다. 디바이스드라이버에서응용프로그램에있는데이터를가져올때는 copy_from_user() 와같은함수를이용하고, 해당장치에서받아온값을응용영역으로보낼때는 copy_to_user() 와같은함수를이용하여데이터를전달한다. 일반적인하드웨어제어는문자 (Character) 디바이스드라이버가이용되지만, 8

CDROM이나 HDD와같이한번에많은데이터가송수신되어야되는경우라면블록 (Block) 디바이스드라이버로구현되기때문에중간에버퍼캐시를두게된다. 네트워크디바이스드라이버는일반적인드라이버와는약간다르다. 네트워크디바이스인경우에는네트워크서브시스템을통해서네트워크장치와연결된다. 본교재에서는 Achro-i.MX6Q 의개발자보드혹은본체에부착되어있는문자 디바이스위주로진행이된다. < 리눅스디바이스드라이버등록 Block Diagram > 디바이스드라이버를커널에등록시키기위해서 insmod를실행하면, 디바이스드라이버의 init_module() 이수행된다. 해당모듈이수행되면디바이스드라이버의초기화부분이수행된다. 사용자프로그램에서장치를사용하려면 open() 을통해서해당장치를호출 (call) 하고, 해당디바이스드라이버의 dev_open() 함수가구동된다. 정상적으로장치가호출되면 return을통해장치를제어할수있는디스크립터를반환한다. 반환된스크립트가 -1이면장치호출이실패한경우이며, 표준입력, 표준출력, 표준에러를제외한값이 (3~) 이된다. 장치가정상적으로열리게되면, 응용프로그램내에서는 read(), write(), ioctl() 등을이용해서디바이스드라이버를호출한다. 응용프로그램에서호출된 read(), write(), ioctl() 은커널의파일오퍼레에션구조체에등록되어있는각각의함수를실행하고결과를리턴값으로반환한다. 응용프로그램에서 close() 함수를호출하면, 다시시스템콜이발생하고해당시 9

스템디바이스드라이버의 dev_release() 가실행되어장치에대한처리를한다. < 리눅스디바이스드라이버등록도식도 > 2.2. Device Driver의분류 2.2.1. Device Driver 구성디바이스드라이버는디바이스관리를위한인터페이스구현함수와자료구조의집합이라고할수있다. 커널은이렇게구현된인터페이스를통해물리적인디바이스에게 IO 함수를요청하고제어할수있다. 디바이스드라이버는이같은인터페이스를통해디바이스에접근하는데이터형식이다. 2.2.2. Major number, Minor number 문자와블록디바이스드라이버는파일형태의노드를통해서사용자프로그램과데이터송 / 수신이가능하다. 때문에데이터송수신을위해서파일형태의노드정보를커널에게알려주어야하며, 그방식은다음과같다. 함수의첫번째인수는특정하드웨어장치를구분하는 Major 번호를사용한다. 따라서종류가다른하드웨어장치에는다른 Major 번호를부여한다. 예를들면, UART는 4를사용하고, SCSI Disk는 8을사용한다. Minor 번호는하드웨어특성이 ( 같은종류의하드웨어장치 ) 10

같지만여러개있을때, 각각의장치를분류하기위해서사용하는번호이다. UART나 SCSI 디바이스는여러개의장치를사용할수있기때문에 Minor번호로분류하게된다. 커널은이렇게만들어진각디바이스의주번호와부번호 (major, minor number) 를이용해서특정장치에접근하게된다. 새로운디바이스드라이버를만들때는 include/linux/major.h 또는 /proc/devices 파일을참조하여다른디바이스드라이버와충돌하지않도록주의해서번호를할당해야한다. 2.2.3. Device Driver 종류 1 Character Device Driver 문자 (char) 디바이스는 ( 파일처럼 ) 바이트스트림형태로참조할수있다. 문자디바이스드라이버가이러한활동을구현할책임이있다. 대개문자드라이버는최소한 open, close, read, write 시스템호출을구현한다. 문자열콘솔 (/dev/console) 과직렬포트 (/dev/ttys0을비롯한 tty계열 ) 가문자디바이스의보기로서스트림형태의추상화를잘표현하고있다. 문자디바이스는 /dev/tty1과 /dev/lp0 과같이파일시스템노드라는수단을통해서참조한다. 문자디바이스와일반적인파일과의차이점이라면, 파일은항상앞뒤로이동이가능한반면에, 문자디바이스는데이터채널에불과하므로순차적으로만참조할수있다는사실이다. 그렇지만데이터영역처럼취급하여앞뒤로이동할수있는문자디바이스가없는것은아니다. 주로프레임그래버가여기에해당하는데, 응용프로그램은 mmap또는 lseek을이용하여가져온이미지전체를참조할수있다. 파일처럼사용. 파일접근에필요한 open, close, read, write는시스템콜로구현. 일반적으로순차적접근만허용. /dev/ttys0, /dev/lp1과같은파일시스템노드로접근. X root@ubuntu File Edit View Terminal Help root@ubuntu:/# ll /dev/ttys* crw-rw---- 1 root dialout 4, 68 10월 6 09:01 /dev/ttys1 crw-rw---- 1 root dialout 4, 68 10월 6 09:01 /dev/ttys2 crw-rw---- 1 root dialout 4, 68 10월 6 09:01 /dev/ttys3 crw-rw---- 1 root dialout 4, 68 10월 6 09:01 /dev/ttys4 crw-rw---- 1 root dialout 4, 69 10월 6 09:01 /dev/ttys5 crw-rw---- 1 root dialout 4, 70 10월 6 09:01 /dev/ttys6 crw-rw---- 1 root dialout 4, 71 10월 6 09:01 /dev/ttys7 위출력화면에서노드파일 (/dev/ttys1) 을확인해보면다음과같은의미를가진다. 11

c 는 Character Device, 주번호는 4 번, 부번호는 68, 디바이스이름은 ttys1 사용자가노드파일을만들때는다음과같이하면된다. <Host PC> root@ubuntu:/# mknod /dev/ttys1 c 4 68 문자디바이스드라이버등록위에서언급한것처럼, 디바이스드라이버를추가한다는것은새로하나의주번호 (Major number) 를디바이스드라이버에할당한다는의미이다. init_ module 함수가모듈을초기화할때, 커널소스디렉터리의 /include/linux/fs.h에정의되어있는디바이스등록함수를주번호로호출함으로써이루어진다. /* fs/char_dev.c */ #define CHRDEV_MAJOR_HASH_SIZE 255 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); extern int register_chrdev_region(dev_t, unsigned, const char *); extern int register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name, const struct file_operations *fops); extern void unregister_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name); extern void unregister_chrdev_region(dev_t, unsigned); extern void chrdev_show(struct seq_file *,off_t); static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) { return register_chrdev(major, 0, 256, name, fops); } static inline void unregister_chrdev(unsigned int major, const char *name) { unregister_chrdev(major, 0, 256, name); } - extern int register_chrdev(unsigned int, const char *, struct file_operations 12

*) 첫번째인수 : Major 번호로만약 0 값을주면 Major 번호중사용하지않는번호로동적할당한다. 두번째인수 : 디바이스 name 즉장치이름이며, /proc/devices에나타난다. 세번째인수 : 파일연산함수 ( 다음에자세히설명할것이다.) 리턴하는값 : 0이나양수이면정상, 음수이면실패. 내 PC 에등록되어있는디바이스드라이버를확인해보려면다음과같은명령어 를실행한다. 13

X root@ubuntu File Edit View Terminal Help root@ubuntu:/# cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttys 5 /dev/tty 5 /dev/console 5 /dev/ptmx 5 ttyprintk 6 lp 7 vcs 10 misc ( 중략 ) 251 hidraw 252 bsg 253 watchdog 254 rtc Block devices: 1 ramdisk 259 blkext 7 loop 8 sd 9 md 11 sr 65 sd 66 sd 252 device-mapper 253 virtblk 254 mdp 문자디바이스장치해제하기 리눅스시스템에서모듈을삭제할때에는주번호도함께제거해야한다. cleanup_ module 함수에서디바이스장치해제함수를사용한다. 14

- extern int unregister_chrdev(unsigned int, const char *) 첫번째인자 : 해제하고자하는장치주번호 (Major nuber) 두번째인자 : 해제하고자하는장치이름 (Device name) 커널은디바이스이름을처음주번호를할당할때의이름과비교한다. 이름이 틀리거나, 제거하고자하는디바이스가없거나, 혹은, 주번호가경계를벗어난값 을가지면에러코드인 -einval 을리턴하게된다. 파일연산 (Files Operation) 사용자영역과디바이스드라이버역역, 즉커널영역은파일인터페이스를통해데이터를주고받을수있다. 디바이스드라이버는 register_chrdev() 함수로드라이버를등록할때사용하는함수인자, file_operations 구조체를이용하여서로 access 한다. file_operations 구조체는 file 구조체의일부이다. file 구조체는소스리스트중자주쓰이는것을먼저살펴보고, file_operations 구조체에대해서설명한다. 아래그림은./include/linux/fs.h 에서캡처한것이다 15

struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); }; unsigned int f_flags; 응용프로그램에서 open( /dev/mem,o_rdonly) 할때사용하는플래그로서, O_RDONLY, O_RDWR, O_NONBLOCK, O_SYNC 등의플래그설정값을말한다. loff_t f_pos; read/write/lseek함수와같이파일의위치를변경하기위해사용된다. 16

const struct file_operations *f_op; 드라이버메서드를가리키는포인터. void *private_data; 드라이버 / 모듈의내부데이터를유지하며, 전역변수는사용을 자체해야한다. Character Device file operation 내용 loff_t (*llseek) (struct file *, loff_t, int); 파일에서현재읽고쓰는위치를이동하는함수 ssize_t (*read, *write) (struct file *, char *, size_t, loff_t *); 장치에서데이터를읽거나, 쓴다. int (*readdir) (struct file *, void *, filldir_t); 디렉터리에만사용하는함수 - 디바이스노드에서는 Null' 값을리턴한다. unsigned int (*poll) (struct file *, struct poll_table_struct *); 현재실행중인프로세스를대기 queue에넣는다. int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 디바이스에종속적인함수나커맨드를만드는데사용한다. 각디바이스장치에대한고유한커맨드를프로그래머가임의로만들수있다. int (*mmap) (struct file *, struct vm_area_struct *); 디바이스의메모리를프로세스메모리에맵핑시키는함수 int (*open) (struct inode *, struct file *); 디바이스를 open int (*flush) (struct file *); 디바이스 flush int (*release) (struct inode *, struct file *); 디바이스해제 int (*fsync) (struct file *, struct dentry *, int datasync); 디바이스 flush int (*fasync) (int, struct file *, int); flag in fasync flag에변화가있는디바이스를확인할때쓴다. int (*lock) (struct file *, int, struct file_lock *); 파일에락을건다. - 파일연산 (File Operation) example :./driver/char/misc.c 17

static const struct file_operations misc_proc_fops = {.owner = THIS_MODULE,.open = misc_seq_open,.read = seq_read,.llseek = seq_lseek,.release = seq_release, }; 2 Block Device Driver 블록 (block) 디바이스도문자디바이스와마찬가지로 /dev/ 디렉터리에서파일시스템노드로참조한다. 블록디바이스는파일시스템을수용할수있는디바이스로서 CDROM과 HDD등이해당된다. 대다수유닉스시스템에서블록디바이스는완전한블록을하나이상전송하는 I/O작업만을수행할수있다. 이때블록의길이는대개 512바이트 ( 또는 2의배수로증가하는더큰값 ) 이다. 리눅스에서는응용프로그램이블록디바이스를문자디바이스처럼읽고쓸수있으며, 한번에전송할수있는바이트수에는제한이없다. 결과적으로블록디바이스와문자디바이스는커널이내부적으로데이터를관리하는방법, 즉커널의드라이버소프트웨어인터페이스에만차이가있을뿐이다. 사용자입장에서는두디바이스사이에차이점이없으나, 블록디바이스는문자디바이스와는완전히다른커널접근인터페이스에의존한다. 디스크와같이파일시스템을가질수있으며, 블록단위로만데이터를송 수신한다. Linux에서는한번에여러크기의바이트를전송할수있으며, 커널이나하드웨어인터페이스만문자디바이스와차이가있을뿐, 사용자에게는파일형태로접근하는문자디바이스와동일하게보인다. 블록디바이스드라이버는 Buffer Cache를통해 Random Access가가능하며, 예로써, HDD, Floppy disk, Ram disk, CD-ROM 등이있다. 문자디바이스드라이버와마찬가지로주번호로구분하지만문자디바이스와는별개로관리되기때문에주번호가겹쳐도상관이없다. 블록디바이스의파일시스템은버퍼캐시를통해서접근하지만버퍼캐시를이용하는일반 read/write 동작과는차이가있다. 이를위해서추가적인구조체가존재하며, 이구조체를통해서실질적인 read/write를수행하는함수를구현해야한다. 블록의크기, 섹터의크기, 전체크기등의설정은따로관리해야한다. 18

블록디바이스드라이버등록 블록디바이스드라이버도문자디바이스드라이버처럼주번호의해구분된다. extern int register_blkdev(unsigned int, const char *, struct block_device_operations *); 첫번째인자 : Major 번호, 0이면동적할당 두번째인자 : 블록디바이스이름 세번째인자 : 블록디바이스장치에대한파일연산함수 리턴값 : 0이나양수이면정상, 음수이면에러 블록디바이스드라이버해제 extern int unregister_blkdev(unsigned int, const char *); 첫번째인자 : Major 번호 두번째인자 : 블록디바이스이름 - 파일연산 (File Operation) 블록디바이스드라이버의파일연산은문자디바이스드라이버와차이점이있기때문에간단히언급하고자한다. 아래그림은./include/linux/fs.h 에서캡처한것이다. 19

struct block_device { dev_t bd_dev; /* not a kdev_t - it's a search key */ int bd_openers; struct inode * bd_inode; /* will die */ struct super_block * bd_super; struct mutex bd_mutex; /* open/close mutex */ struct list_head bd_inodes; void * bd_claiming; void * bd_holder; int bd_holders; bool bd_write_holder; #ifdef CONFIG_SYSFS struct list_head bd_holder_disks; #endif struct block_device * bd_contains; unsigned bd_block_size; struct hd_struct * bd_part; /* number of times partitions within this device have been opened. */ unsigned bd_part_count; int bd_invalidated; struct gendisk * bd_disk; struct list_head bd_list; /* * Private data. You must have bd_claim'ed the block_device * to use this. NOTE: bd_claim allows an owner to claim * the same device multiple times, the owner must take special * care to not mess up bd_private for that case. */ unsigned long bd_private; }; /* The counter of freeze processes */ int bd_fsfreeze_count; /* Mutex for freeze */ struct mutex bd_fsfreeze_mutex; Block device 의 operation 은 include/linux/blkdev.h 에기술되어있다. 20

struct block_device_operations { int (*open) (struct block_device *, fmode_t); int (*release) (struct gendisk *, fmode_t); int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*direct_access) (struct block_device *, sector_t, void **, unsigned long *); unsigned int (*check_events) (struct gendisk *disk, unsigned int clearing); /* ->media_changed() is DEPRECATED, use ->check_events() instead */ int (*media_changed) (struct gendisk *); void (*unlock_native_capacity) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); /* this callback is with swap_lock and sometimes page table lock held */ void (*swap_slot_free_notify) (struct block_device *, unsigned long); struct module *owner; }; < Block Device file Operation 예 > 일반적으로블록디바이스드라이버에서 read/write/fsync 함수는따로구현하지 않고 block_read(), block_write(), block_fsync() 함수를그대로사용한다. open 이 나 release 는문자디바이스드라이버처럼구현하여사용한다. 그러나 ioctl 는조금 21

차이가있다. 그것은수많은블록디바이스드라이버가사용할것으로예상되는공통적인 ioctl 커맨드들을지원해야한다는점이다. 보통그공통적인커맨드는 - BLKGETSIZE : 장치의크기를섹터의개수로환산해리턴 - BLKFLSBUF : 버퍼캐시를 flush - BLKRAGET : 미리읽어올데이터크기 - BLKRASET : 미리읽어올데이터셋팅 - BLKROGET : flag 얻어오기 - HDIO_GETGEO : 하드디스크의구조읽어오기등이바로그것이다. - 파일연산 (File Operation) example : floppy static const struct block_device_operations floppy_fops = {.owner = THIS_MODULE,.open = floppy_open,.release = floppy_release,.locked_ioctl = fd_ioctl,.getgeo = fd_getgeo,.media_changed = check_floppy_change,.revalidate_disk = floppy_revalidate, }; - Buffer Cache State 블록디바이스는이미언급한것처럼버퍼캐시를이용하여데이터를주고받는 다. 여기서는간단하게버퍼캐시상태에대해설명하도록하겠다. 22

- Device Driver 와 Buffer Cache - BH_Uptodate 현재버퍼는유효한데이터를가지고있다. - BH_Dirty 디스크에있는데이터와버퍼의데이터는다르다. - BH_Lock 현재버퍼가 Lock되어있다. ( 처리중에있다 ) - BH_Req 해당하는블록이데이터를요청. - BH_Mapped 버퍼가현재디스크에 mapping 되어있다. - BH_New 버퍼가새로운것이며아직 write 되지않았다. - BH_Protected 버퍼가보호되고있는상태. 이버퍼는 free 되지않는다. 3 Network Device Driver 모든네트워크트랜잭션은인터페이스, 즉다른호스트와데이터를교환해주는디바이스를거친다. 대개인터페이스라면하드웨어를가리키지만, 루프백인터페이스처럼순수소프트웨어디바이스일경우도있다. 네트워크인터페이스는데이터패킷을전송하는책임이있다. 이작업은커널의네트워크하위시스템이주도한다. 하지만인터페이스자체는각트랜잭션을실제전송할패킷으로사상하는방법을알지못한다. ( 특히 TCP를사용하는 ) 네트워크연결은스트림형태가많지만네트워크디바이스는대체로패킷전송과수신을위주로설계한다. 네트워크드라이버 23

는개별연결에대하여전혀아는바없이패킷만을처리할뿐이다. 네트워크인터페이스는스트림형태의디바이스가아니어서 /dev/tty1처럼파일시스템노드로쉽게사상할수없다. 유닉스에서는 (eth0과같은 ) 고유한이름을할당하여인터페이스를참조하지만, 파일시스템에는이이름에상응하는항목이존재하지않는다. 커널과네트워크디바이스드라이버사이에서일어나는통신은문자드라이버나블록드라이버와완전히다르다. read와 write 함수대신에커널은패킷전송에관련된함수를호출한다. 현재등록된네트워크디바이스의정보는다음에서볼수있다. <Host PC> root@ubuntu:/# cat /proc/net/dev 네트워크디바이스드라이버는내부적으로크고복잡하기때문에여기서는다루지않는다. 리눅스를사용하는가장큰이유중하나는네트워크를이용할수있기때문인데, 기본설정에관한 ifconfig, route 등의유틸리티뿐만아니라네트워크에대한기본지식을먼저습득하고네트워크디바이스드라이버를공부하기바란다.