Unix & Linux/Device Driver

리눅스, 디바이스 드라이버 간단한 예제

LEEHANDS 2022. 1. 21. 13:55
반응형

참고사이트: https://reakwon.tistory.com/150

디바이스 드라이버(Device Driver)

OS가 모르는 장치를 연결하면 컴퓨터는 드라이버를 설치하라고 요청할 것입니다.

컴퓨터는 이 장치가 무엇인지 모르기 때문입니다. 그래서 이장치를 동작할 때 어떻게 동작해야하는지에 대한 프로그램을 따로 설치해야 합니다.

알게모르게 설치되는 것이 디바이스 드라이버

시스템 콜이 호출되면 커널 영역 내부로 호출이 전달 됩니다.

커널 내부에 있는 가상 파일 시스템으로 전달, 디바이스 드라이버가 인터페이스를 통해서 하드웨어를 제어 합니다.

 

커널 영역이라함은 무엇?

일반 사용자가 접근할 수 없는 커널만의 영역을 말함

커널 영역 메모리를 참조하거나 데이터를 변경할 수 없다.

디바이스 드라이버가 커널 쪽에 위치해 있으므로 커널 영역 프로그램이라고 보시면 됨

커널의 메모리를 손대는 곳이기 때문에 안전성에 주의해야합니다.

리눅스 디바이스는 세가지가 존재 ( 캐릭터 디바이스, 블록 디바이스, 네트워크 디바이스 )

 

모듈 (Module)

커널의 일부분인 프로그램으로 커널에 추가 기능이 모듈로 관리 된다.

디바이스 드라이버를 만들고 추가할 때 커널의 모듈로 끼워넣으면 됩니다. 모듈은 커널의 일부분

디바이스 드라이버가 하드웨어를 동작해야하기 때문에 커널의 일부분으로 동작해야 한다.

그래서 커널 모듈로 동작되어지는데 이때 착각하지 말아야하는 점은 모듈은 커널의 일부 그리고 모듈에서 디바이스가 동작합니다.

즉 모듈은 디바이스 드라이버가 아니며 디바이스 드라이버가 모듈로 커널의 일부분으로 추가되어 동작하는 것

 

모듈이라는 개념이 없을 때 디바이스 드라이버를 만들었다면 커널이 바뀌었기때문에 다시 커널 컴파일을 해야하는 과정이 있었다.

커널 컴파일 과정은 오래걸리는 작업이라 모듈 개념이 도입된 이후 위의 과정없이 모듈을 설치하고 해제할 수 있다.

이 모듈에서 장치를 등록하거나 해제할 수 있다. 예를  들어 문자형 장치를 등록할 때 아래의 함수 이용

 

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

함수가 어떤 역할을 하는지 몰라도됩니다.

우선 모듈이 어떻게 등록되는지 보도록 하겠습니다.

아주 간단한 모듈 하나를 만들어 보도록 하겠습니다.

우선 C프로그래밍을 하듯이 아래 코드를 작성

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>


static int __init my_init(void){
        printk("hello, kernel!\n");
        return 0;
}

static void __exit my_exit(void){
        printk("goodbye, kernel!\n");
}

module_init(my_init);
module_exit(my_exit);

맨 위의 3개의 헤더파일(linux/module.h, linux/kernel.h, linux/init.h)는 다 포함시켜주어야합니다.

우리가 맨 처음 모듈을 설치할때 초기화하는 module_init과 제거할때 호출되는 module_exit이 module.h에 정의되어있습니다. 이 매크로 함수의 매개변수를 우리가 정의한 함수로 전달해주면 됩니다. 이 무슨 역할을 하는지는 아래에서 보시면 됩니다.

 

#define module_init(x)  __initcall(x);
//...//
#define module_exit(exitfn)                                     \
        static inline exitcall_t __maybe_unused __exittest(void)                \
        { return exitfn; }                                      \
        void cleanup_module(void) __copy(exitfn) __attribute__((alias(#exitfn)));

커널 소스코드 내 linux/include/linux/module.h 위치

 

혹시 객체지향 프로그래밍에서 생성자와 소멸자를 배우셨나요?

그때 객체의 초기설정은 생성자에서, 메모리해제 작업등 자원을 되돌려주는 마무리작업을 소멸자에서 하지 않나요? 그런 역할과 비슷하다고 보시면 됩니다.

printk는 커널 모듈에서 메시지 내용을 출력할때 사용합니다.

커널 메시지는 dmesg 명령으로 볼 수 있습니다. 여기서 주의할 것은 printf가 아니라 printk라는 점!

어쨌든 코드를 짰다면 이제 컴파일을 해야하는데 Makefile을 이용해야합니다. 방법은 이렇습니다. 

KERNDIR=/lib/modules/$(shell uname -r)/build
obj-m+=mymodule.o
objs+=mymodule.o
PWD=$(shell pwd)

default:
        make -C $(KERNDIR) M=$(PWD) modules

clean:
        make -C $(KERNDIR) M=$(PWD) clean
        rm -rf *.ko
        rm -rf *.o

insmod

모듈을 설치하는 명령어 , install module 

" insmod [module name] "

dmesg | tail -1

모듈이 등록됨 확인

 

lsmod

설치된 모듈도 보고싶다면 lsmod.

rmmod

백문이 불여일견 아래를 보자

 

 

커널영역에서의 프로그래밍은 어려운 작업이고

그것에 따른 세세한 조작을 할 수 있게 만들어 줍니다.

예를들어 커널 모듈을 통해 우리는 시스템 콜을 후킹할 수 있다.

모듈은 커널의 일부분이며 반드시 디바이스 드라이버로 동작하지 않을 수 있다.

가장 간단한 리눅스 모듈 입니다.

반응형

'Unix & Linux > Device Driver' 카테고리의 다른 글

디바이스 드라이버 구조  (0) 2022.01.07