디바이스 드라이버와 응용 사이 데이터 교환


- 커널과 응용 프로그램 사이 데이터 교환

1. 응용 프로그램은 직접 커널 메모리 영역에 접근할 수 없음.

* 메모리 접근 예외 발생.

2. 커널은 응용 프로그램이 전달한 자신의 메모리 주소를 이용해 데이터 교환.

* 읽기/쓰기를 하기 전에 메모리 주소가 변경되면 문제 발생.

* 프로세스는 상황에 따라 sleep 상태로 전이될 수 있음.

* sleep 상태에서 깨어나면 할당된 메모리 주소가 유효하지 않을 수 있음.

3. 메모리 복사 전에 주소가 유효한지 검사하는 특별한 함수 지원.



- 디바이스 드라이버와 응용 사이 데이터 교환 함수.



- 커널 공간 데이터를 사용자 공간으로 전달.

1
2
3
4
5
#include <asm/uaccess.h>
 
unsigned long clear_user(void __user *to, unsigned long n);
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
put_user(x, ptr);
cs

* to, x : 사용자 공간 데이터 포인터

* n : 크기

* from, ptr : 커널 공간 데이터 포인터

- 안전하게 커널 데이터를 사용자 공간으로 복사.

1. 복사전에 필요에 따라 clear_user()를 호출해 사용자 데이터 공간을 0으로 초기화.

2. copy_to_user()를 호출해 n만큼 복사. 정상적으로 복사되면 반환 값은 0.

3. put_user()는 copy_to_user()대신 구조체나 배열을 제외한 기본형에 간편하게 적용.

* 1byte, 2byte, 4byte 복사 시에 사용.

* 컴파일러가 형 검사후 pu_user1(), put_user2(), put_user4()로 변환함




- 사용자 공간 데이터를 커널 공간으로 전달

1
2
3
4
#include <asm/uaccess.h>
 
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);
get_user(x, ptr);
cs

* to, x : 커널 공간 데이터 포인터

* n : 크기

* from, ptr : 사용자 공간 데이터 포인터

- 안전하게 사용자 데이터를 커널 공간으로 복사.

1. copy_from_user()를 호출해 n만큼 복사. 정상적으로 복사되면 반환 값은 0.

2. get_user() 는 copy_from_user() 대신 구조체나 배열을 제외한 기본형에 간편하게 적용.



1
2
3
4
5
#include <asm/uaccess.h>
 
long strnlen_user(const char __user *s, long n);
strlen_user(str);
long strncpy_from_user(char *dst, const char __user *src, long count);
cs

* s, str, src : 사용자 공간 데이터 포인터.

* n, count : 크기

* dst : 커널 공간 데이터 포인터

- NULL로 끝나는 사용자 공간의 문자열을 안전하게 커널 공간으로 복사.

1. strlen_user()를 호출해 복사할 문자열 크기 파악. 반환 값은 NULL을 포함한 크기.

2. strnlen_user()는 strlen_user()와 동일하며 최대 버퍼 크기 이내에서 파악.

3. strncpy_from_user()를 호출해 count 만큼 복사. 정상적으로 복사되면 반환 값은 NULL을 제외한 크기. 실패하면 -EFAULT




- 데이터 교환 드라이버 구현 예제

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
101
102
103
104
105
106
107
108
109
110
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
    
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
 
#include <linux/slab.h>
 
void * memory_buffer;    // 커널에서 동작 할당된 메모리 주소 저장.
ssize_t cur_len;    // 현재 버퍼에 저장된 데이터 크기
 
#define BUF_SIZE 16        // 동적으로 할당할 메모리 크기
 
static ssize_t drv_simple_buffer_read(struct file * file, char * buf, size_t length, loff_t * ofs)
{
    ssize_t retval;
 
    if(*ofs > 0){
        retval = 0;
        goto out;
    }
 
    retval = cur_len;
 
    // 버퍼 내용을 사용자에게 전달
    if(copy_to_user(buf, memory_buffer, retval)){
        retval = -EFAULT;
        goto out;
    }
 
    printk("drv_buffer_read: [%dbyte -> %dbyte]\n", length, retval);
    *ofs += retval;
 
out:
    return retval;
}
 
static ssize_t drv_simple_buffer_write(struct file * file, const char * buf, size_t length, loff_t * ofs)
{
    ssize_t retval;
 
    if(length > BUF_SIZE){
        retval = -ENOMEM;
        goto out;
    }
 
    cur_len = retval = length;
 
    // 사용자가 전달한 데이터를 버퍼에 저장.
    if(copy_from_user(memory_buffer, buf, retval)){
        retval = -EFAULT;
        goto out;
    }
 
    printk("drv_buffer_write: [%dbyte -> %dbyte]\n", length, retval);
 
out:
    return retval;
}
 
static struct file_operations drv_simple_buffer_fops = 
{
    .owner = THIS_MODULE,
    .read = drv_simple_buffer_read,
    .write = drv_simple_buffer_write,
};
 
static struct miscdevice drv_simple_buffer_driver = 
{
    .minor = MISC_DYNAMIC_MINOR,
    .name = "drv_simple_buffer",
    .fops = &drv_simple_buffer_fops,
    .mode = 0777,
};
 
static int drv_simple_buffer_init(void)
{
    // memory_buffer에 도적으로 BUF_SIZE만큼 메모리 할당.
    memory_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
    if(!memory_buffer){
        printk("error: allocating memory for the buffer\n");
        return -ENOMEM;
    }
    
    // 메모리 초기화
    memset(memory_buffer, 0, BUF_SIZE);
    
    return misc_register(&drv_simple_buffer_driver);
}
 
static void drv_simple_buffer_exit(void)
{
    if(memory_buffer){
        //동적으로 할당 다은 메모리 제거.
        kfree(memory_buffer);
    }
 
    misc_deregister(&drv_simple_buffer_driver);
    
}
 
module_init(drv_simple_buffer_init);
module_exit(drv_simple_buffer_exit);
 
MODULE_AUTHOR("PlanX Studio");
MODULE_DESCRIPTION("drv_simple_buffer");
MODULE_LICENSE("Dual BSD/GPL");
cs






출처 : 인지소프트웨어 기술포럼 ( 전유진 강사님 수업자료)


+ Recent posts