[Research] Linux Kernel Basic - (7) 디바이스 드라이버(Device Driver) 2편

[Research] Linux Kernel Basic - (7) 디바이스 드라이버(Device Driver) 2편


서론

지난 포스트에서는 커널의 종류인 모놀리식 커널과 마이크로 커널을 알아보았으며 적재 가능 모듈(LKM)이 어떠한 것 인지, 적재 가능 모듈(LKM)으로 사용되는 디바이스 드라이버에 대해 언급하였다. 해당 포스트에서는 캐릭터 디바이스 드라이버(Character Device Driver)를 직접 구현해보며 연구한 것을 토대로 포스트를 작성하겠다.


디바이스 드라이버

디바이스 드라이버의 경우 디바이스 자체에 대한 정보를 세부적으로 알 필요가 없으며 가상파일시스템(VFS)을 이용하여 파일에 대한 접근을 통해 디바이스를 제어할 수 있다. 해당 말을 일반적으로 풀어내면 디바이스 드라이버의 경우 추상화된 장치에 접근하여 정형화 된 인터페이스(open, read, write)과 같은 형태로 접근이 가능하다는 말이다.

User Application 측면

  • 하드웨어 장치, 즉 디바이스의 경우 하나의 파일로 인식할 수 있다.
  • 정형화된 인터페이스 시스템 호출을 통해 실제 하드웨어 장치에 대한 접근/제어가 가능하다.

Operating System 측면

  • 리눅스에서 디바이스는 파일로 취급되며 액세스가 가능하다.
  • 파일로 취급되므로 정의된 file operation을 기준으로 접근/제어가 가능하다.
  • 각각의 디바이스는 디바이스를 식별하기 위한 Major Number와 Minor Number를 가지고 있다.

Major Number & Minor Number

$ ls -al /dev/*
crw-rw----   1 root tty       7,   0 Sep  5 17:33 vcs
crw-rw----   1 root tty       7,   1 Sep  5 17:33 vcs1
crw-rw----   1 root tty       7,   2 Sep  5 17:33 vcs2
crw-rw----   1 root tty       7,   3 Sep  5 17:33 vcs3
crw-rw----   1 root tty       7,   4 Sep  5 17:33 vcs4
crw-rw----   1 root tty       7,   5 Sep  5 17:33 vcs5
crw-rw----   1 root tty       7,   6 Sep  5 17:33 vcs6
.

Major Number(주번호)의 경우는 커널에서 디바이스 드라이버를 구분/연결하는 용도로 사용되며 같은 디바이스 종류를 지칭한다. 1byte로 구성된다. Minor Number(부번호)의 경우는 디바이스 드라이버 내에 장치를 구분하기 위해 사용되며 각 디바이스의 부가적인 정보를 나타내며 2byte로 구성된다. 이를 통해 하나의 디바이스 드라이버가 여러 개의 디바이스를 제어 가능하다.


모듈 프로그래밍

chardev.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// source: chardev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_AUTHOR("G4EG4EG4E");
MODULE_DESCRIPTION("sample character device driver.");
MODULE_LICENSE("Dual BSD/GPL");

static int __init custom_init(void) {
printk("[+] Init Custom Device\n");

return 0;

}

static void __exit custom_exit(void) {
printk("[-] Exit Custom Device\n");
}


module_init(custom_init);
module_exit(custom_exit);

다음과 같은 디바이스 드라이버 모듈을 작성하였다. module_init의 경우는 해당 모듈이 등록될 경우 실행되는 함수이며, module_exit의 경우는 해당 모듈이 해제될 경우 실행되는 함수이다.


Makefile

1
2
3
4
5
6
7
8
9
10
obj-m += chardev.o
PWD := $(shell pwd)

KDIR:=/lib/modules/$(shell uname -r)/build

all:
$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

모듈의 소스코드를 컴파일하기 위해서는 Makefile을 작성해야 한다. 커널 버전과 동일한 형태로 빌드를 진행해야 해당 시스템에서 동적으로 로드할 수 있는 모듈이 된다. 컴파일이 정상적으로 될 경우 chardev.ko 바이너리를 확인할 수 있으며 해당 바이너리가 LKM이다.


커널 모듈의 등록과 해제

$ sudo insmod chardev.ko
$ dmesg 
[301222.511894] atkbd serio0: Unknown key released (translated set 2, code 0x72 on isa0060/serio0).
[301222.511896] atkbd serio0: Use 'setkeycodes 72 <keycode>' to make it known.
[301226.623243] atkbd serio0: Unknown key pressed (translated set 2, code 0x72 on isa0060/serio0).
[301226.623250] atkbd serio0: Use 'setkeycodes 72 <keycode>' to make it known.
[301226.623564] atkbd serio0: Unknown key released (translated set 2, code 0x72 on isa0060/serio0).
[301226.623566] atkbd serio0: Use 'setkeycodes 72 <keycode>' to make it known.
[301300.320388] [+] Init Custom Device
.

insmod 명령어를 통해 해당 커널 모듈을 등록할 경우 custom_init 함수가 실행되는 것을 확인할 수 있다.


$ sudo rmmod chardev
$ dmesg 
[301222.511894] atkbd serio0: Unknown key released (translated set 2, code 0x72 on isa0060/serio0).
[301222.511896] atkbd serio0: Use 'setkeycodes 72 <keycode>' to make it known.
[301226.623243] atkbd serio0: Unknown key pressed (translated set 2, code 0x72 on isa0060/serio0).
[301226.623250] atkbd serio0: Use 'setkeycodes 72 <keycode>' to make it known.
[301226.623564] atkbd serio0: Unknown key released (translated set 2, code 0x72 on isa0060/serio0).
[301226.623566] atkbd serio0: Use 'setkeycodes 72 <keycode>' to make it known.
[301300.320388] [+] Init Custom Device
[301311.542920] [-] Exit Custom Device
.

rmmod 명령어를 통해 해당 커널 모듈을 해제할 경우 custom_exit 함수가 실행되는 것을 확인할 수 있다.


한줄평

아몬드 넥스트 레벨💁

참고

b30w0lf님의 운영체제(Operating System)
https://butter-shower.tistory.com/29?category=715664