티스토리 뷰

System/Linux

Linux Device Driver 정리 (2)

Tribal 2017. 6. 1. 02:22

문자 디바이스(Character Device) 요청 처리 방식

  문자 디바이스는 블록 디바이스와 다르게 System Buffer를 사용하지 않기 때문에 요청 큐를 가지지 않는다. 블록 디바이스는 인터페이스를 이용해 제어를 하다가 System Buffer에 해당 내용이 비어있어 디바이스 드라이버까지 요청이 들어오면 요청 큐에 요청을 저장하여, request() 함수를 이용해 요청을 하나씩 꺼내어 처리하였다. 하지만 문자 디바이스는 System Buffer와 상관없이 디바이스 드라이버로 요청이 들어오고 문자 디바이스의 인터페이스를 이용해 바로 요청을 처리한다.

  • 블록 디바이스 : System Buffer 사용, request()를 이용해 요청 처리, System Buffer에 쓰기·읽기
  • 문자 디바이스 : User Process의 메모리 사용, 인터페이스(operations)를 이용해 요청 처리, User Process에 쓰기·읽기

문자 디바이스 관련 함수 정리

  • 문자 디바이스 드라이버 등록·해제 함수
    • register_chrdev() : 커널에 문자 디바이스 드라이버를 등록, 이를 처리하기 위한 기본 함수인 operation 등록

    • unregister_chrdev() : 커널에 문자 디바이스 드라이버를 해제

  • 디바이스 사용 횟수 증가·감소 함수
    • try_module_get() : 디바이스 사용 횟수를 증가시키는 함수(프로세스가 사용하고 있는데 급종료하면 안 되므로...)
    • module_put() : 디바이스 사용 횟수를 감소시키는 함수
  • 기타 함수
    • put_user() : 커널 메모리의 내용을 User Process 메모리로 변수의 길이를 검사해 1·2·4 바이트만큼 복사하는 함수
    • get_user() : User Process 메모리의 내용을 커널 메모리로 변수의 길이를 검사해 1·2·4 바이트만큼 복사하는 함수
    • copy_to_user() : 커널 메모리의 내용을 User Process 메모리로 지정한 길이만큼 복사하는 함수
    • copy_from_user() : User Process 메모리의 내용을 커널 메모리로 지정한 길이만큼 복사하는 함수
    • printk() : 커널 메시지 출력

문자 디바이스 드라이버 예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <linux/kernel.h>
#include <linux/module.h>
#include <linus/fs.h>
#include <asm/uaccess.h>
 
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *indoe, struct file *file);
static int device_release(struct inode *inode, struct file *file);
static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset);
static ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset);
 
#define SUCCESS 0
#define DEVICE_NAME "chrdev"
#define BUF_LEN 80
 
// Global variables
static int s_nMajor;
static int s_Device_Open = 0;
 
static char s_buf[BUF_LEN];
static char *s_buf_Ptr;
static struct file_operations fops = {
    .open = device_open,
    .release = device_release,
    .read = device_read,
    .write = device_write,
}
 
// Entry Function
int init_module(void) {
    s_nMajor = register_chrdev(0, DEVICE_NAME, &fops);
    if(s_nMajor < 0) {
        printk(KERN_ALERT "Registering char device failed with %d\n", s_nMajor);
        return s_nMajor;
    }
 
    return SUCCESS;
}
 
// Exit Function
void cleanup_module(void) {
    int ret = unregister_chrdev(s_nMajor, DEVICE_NAME);
    if(ret < 0)
        printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret);
}
 
// device interface
static int device_open(struct inode *inode, struct file *file) {
    static int counter = 0;
    if (s_Device_Open)
        return -EBUSY;
    s_Device_Open++;
    printf(s_buf, "I already told you %d times Hello World!\n", counter++);
    s_buf_Ptr = s_buf;
    try_module_get(THIS_MODULE);
    return SUCCESS;
}
 
static int device_release(struct inode *inode, struct file *file) {
    s_Device_Open--;
    module_put(THIS_MODULE);
    return 0;
}
 
static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset) {
    int count = 0;
 
    if(*s_buf_Ptr == 0)
        return 0;
 
    // Copy(s_buf_Ptr => buffer)
    while(length && *s_buf_Ptr) {
        put_user(*(s_buf_Ptr), buffer);    // 1 byte copy
        s_buf_Ptr++;
        buffer++;
        length--;
        count++;
    }
 
    return count;
}
 
static ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset) {
    int count = 0;
 
    if(*s_buf_Ptr == 0)
        return 0;
 
    // Copy(buffer => s_buf_Ptr)
    while(length && *s_buf_Ptr) {
        get_user(*(s_buf_Ptr), buffer);    // 1 byte copy
        s_buf_Ptr++;
        buffer++;
        length--;
        count++;
    }
 
    return count;
}
cs

1~2번 과정은 커널에 모듈 등록, 3번 과정은 디바이스 처리, 4~5번 과정은 커널에서 모듈 제거

  1. 모듈이 insmod 명령어로 로드되면 모듈의 Entry Function인 31번째 줄의 init_module()이 호출됨
  2. 32번째 줄에서 register_chrdev()가 호출되어, 커널에 Major Number. Character Device의 이름과 드라이버의 동작과 관련된 작업에 사용되는 operations 구조체 주소를 등록
  3. 장치를 사용하면 fops의 함수 포인터를 확인하여 행위에 맞는 연산처리 실시
    ex) 장치를 열면(mount) open, 장치를 닫으면(umount) release, 장치로부터 읽으면 read, 장치에 쓰면 write
  4. 모듈이 rmmod  명령어로 해제하면 모듈의 Exit Function인 94번째 줄의 cleanup_module()이 호출됨
  5. 43번째 줄에서 unregister_chrdev()가 호출되어, 커널에서 Character Device 제거

문자 디바이스(Character Device) 요청 처리 추가(재정리 필요...)

  기존의 블록 디바이스는 하드디스크와 같은 디스크 종류와 관련이 있고 파일 시스템을 사용해서 블록 단위로 처리한다. 하지만 문자 디바이스는 그렇지 않다. 이 차이가 디바이스 드라이버의 코드를 결정하기 때문에 중요하다.

  블록 디바이스의 디스크는 읽고 쓰기의 작업을 할 때, 블록(몇 번 섹터부터 얼만큼의 섹터만큼) 단위로 처리를 하기 때문에, 메모리에 대한 정보를 알려줘야 하고, 탐색 과정에 걸리는 시간 때문에 System Buffer를 사용하여 이전에 사용한 내용을 저장해둔다. 또한, 디스크를 탐색하는 시간도 최소화하면 좋다. 그래서 요청 큐에 요청을 저장해놓고, 연속된 섹터 등에 대해서는 merge 등의 작업을 수행하여 한 번의 접근으로 최대한의 효율을 낼 수 있도록 한다.

  문자 디바이스는 블록 단위로 처리하지도 않고, 프로세스의 메모리를 바로 사용한다. 회로 등을 통해서 데이터가 전송될 때 스트림으로 전송되기 때문에 응용 프로그램에서 커널과 직접 대응하여 메모리의 주소와 크기를 통해 바로 처리한다.


참고

 07_old.pdf

https://wiki.kldp.org/Translations/html/The_Linux_Kernel-KLDP/tlk8.html

http://studyfoss.egloos.com/5575220

https://kldp.org/node/35748

http://callgm.tistory.com/26


Block Device Driver를 공부하고 하니 이해가 쉽다.

'System > Linux' 카테고리의 다른 글

Reverse Shellcode  (0) 2017.08.12
Linux TLS(Thread Local Storage) 정리  (0) 2017.06.05
Linux Device Driver 정리 (1)  (0) 2017.05.28
Linux 커널 및 모듈 공부 기초  (0) 2017.05.27
ptmalloc2 Exploit 기법 정리  (0) 2017.04.26
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31