IT_Study/Personal Project

Linux에서 Application과 Device Driver의 interaction 구현

__Vivacé__ 2023. 4. 20. 22:42

Application과 Device Driver 개발

초기 상태, HW는 가상으로 존재


1. Device File 제작

# mknod : 특수 파일 만드는 유틸리티
# device file을 만들 address, type과 major, minor number 설정
$ sudo mknod /dev/nobrand c 100 0

# 파일의 권한 설정
$ sudo chmod 666 /dev/nobrand

File System에 Device file 등록


2. Device Driver 제작

nobrand.c

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3
  4 #define NOD_MAJOR 100
  5 #define NOD_NAME "nobrand"
  6
  7 MODULE_LICENSE("GPL");
  8
  9 // inode : Device File의 inode 정보를 가짐
 10 //         insmod 시, inode에는 주번호 / 부번호 정보도 적힌다.
 11 // filp : Device File이 App에서 어떤 형태로 읽혔는 지의 정보가 들어있음 (app에서 O_RDWR로 읽음)
 12 static int nobrand_open(struct inode *inode, struct file *filp){
 13     printk( KERN_INFO "welcome\n");
 14     return 0;
 15 }
 16
 17
 18 // printk : Linux kernel에서 메세지를 출력하는 함수
 19 // KERN_INFO : 메세지의 log level을 지정하는 함수 - 디버깅 및 정보 출력에 쓰임
 20 //             이외에 KERN_ERR, KERN_WARNING 등이 있음
 21 static int nobrand_release(struct inode *inode, struct file *filp){
 22     printk( KERN_INFO "release\n");
 23     return 0;
 24 }
 25
 26 // 구조체 지정 초기화를 이용한 방법
 27 // app level에서 "open" system call --> nobrand_open 함수가 호출
 28 // app level에서 "close" system call --> nobrand_release 함수가 호출
 29 static struct file_operations fops = {
 30     .open = nobrand_open,
 31     .release = nobrand_release,
 32 };
 33
 34 static int nobrand_init(void)
 35 {
 36     // Device driver가 insmod 될 때, chrdev를 등록
 37     // chrdev : 문자 기반 디바이스 파일을 지원하는 device driver를 개발하기 위한 interface
 38     // 실패 시, INIT FAIL 출력
 39     if( register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0){
 40         printk("INIT FAIL\n");
 41     }
 42     printk( KERN_INFO "hi\n");
 43     return 0;
 44 }
 45
 46 static void nobrand_exit(void)
 47 {
 48     // Device driver가 rmmod 될 때, chrdev 해제
 49     unregister_chrdev(NOD_MAJOR, NOD_NAME);
 50     printk( KERN_INFO "bye\n");
 51 }
 52
 53 module_init(nobrand_init);
 54 module_exit(nobrand_exit);
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

 - major : 디바이스 드라이버의 주 번호 / 커널 내부에서 디바이스 파일을 식별하는데 사용

 - name : 디바이스 파일의 이름을 지정

 - fops : 디바이스 파일에서 지원하는 입출력 연산을 정의하는 struct file_operations 구조체를 가리킴

 

함수의 기능

 1. 커널은 major 번호를 사용하여 디바이스 파일을 연결

 2. 해당 파일에 대한 입출력 요청을 처리할 수 있도록 fops 구조체를 연결

 3. 추후 poll() 함수를 사용하여 디바이스 파일에 대한 이벤트 감지 가능

 

 

Makefile

  1 # 커널 헤더 파일 위치 정의
  2 KERNEL_HEADERS=/lib/modules/$(shell uname -r)/build
  3
  4 # 컴파일러를 gcc로 설정
  5 CC = gcc
  6
  7 # 빌드 대상 응용 프로그램 이름 정의
  8 TARGET := app
  9
 10 # 커널 모듈 오브젝트 파일 이름 정의 / obj-m은 Kbuild에서 사용되는 변수 중 하나
 11 obj-m := nobrand.o
 12
 13 all : driver app
 14
 15 driver:
 16     make -C $(KERNEL_HEADERS) M=$(PWD) modules
 17
 18 app:
 19     $(CC) -o $@ $@.c
 20
 21 clean:
 22     make -C $(KERNEL_HEADERS) M=$(PWD) clean
 23     rm -f *.o $(TARGET)

- make all : 전체를 build

- make driver : driver 빌드

- make app : application 빌드

- make clean : driver와 application 제거

Device driver 소스 코드 작성


3. Application 제작

app.c

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <unistd.h>
  6 #include <stdlib.h>
  7
  8 int main(){
  9     int fd = open("/dev/nobrand", O_RDWR);
 10     if (fd < 0){
 11         printf("ERROR\n");
 12         close(fd);
 13         exit(1);
 14     }
 15
 16     printf("GO\n");
 17     close(fd);
 18     return 0;
 19 }

man page를 활용한 헤더 파일 추가

Device file을 open/close하는 system call 사용

App 소스 코드 작성


4. 동작 테스트

다른 Terminal을 오픈해서 커널 로그 모니터링을 통해 확인할 것

$ make

App과 Device driver가 build됨

$ sudo insmod ./nobrand.ko

insmod 명령어로 인해 Kernel이 device driver를 "module"로 관리 시작

$ ./app

App이 file descriptor로 Device file에 접근 및 system call을 이용해 여러가지 작용 가능

app Level 의 system call인 open() / close() 이 호출되면,
- Device File 을 통해서 system call 이 Device Driver 를 빌드한 결과물 ( .ko 파일) 에
- nobrand_open / nobrand_release 가 호출됨

 

$ sudo rmmod nobrand

rmmod 명령어로 인해 Kernel이 관리 중인 Device driver를 메모리에서 할당 해제


결과

1) 디바이스 파일 생성
    sudo mknod /dev/nobrand c 100 0
    sudo chmod 666 /dev/nobrand

2) Device Driver 를 커널에 등록 시
    insmod → nobrand_init() → register_chrdev() → hi

3) ./app 실행 시
    open() syscall → nobrand_open() → welcome
    close() syscall → nobrand_release() → release

4) Device Driver 를 커널에서 제거 시
    rmmod → nobrand_exit() → unregister_chrdev() → bye