본문 바로가기

Linux

copy_to_user(), copy_from_user(), kernel space and user space

반응형

결론 : User space와 Kernel space는 주소 체계가 다르다!


왜?

 


유저가 어플리케이션을 실행시키면 커널은 실행시킨 어플리케이션을 위해 메모리를 할당해 준다. 

그림에서 유저 스페이스에 속하는 어플리케이션이 2개 있다.

커널 메모리상에서는 각각 [0x3000:0x6000], [0x8000:0x9000] 에 속하지만

각 어플리케이션은 자신이 0x0000부터 시작한다고 생각한다.

두 번째 어플리케이션에서 변수 x의 경우,

 유저 스페이스에서 0x100에 위치하지만 실제 커널 스페이스에서는 0x8100이다,

이처럼 커널 스페이스와 유저스페이스 사이엔 관점의 차이가 있기 때문에 

시스템 콜을 통해 포인터를 사용해 두 영역(커널 스페이스<->유저 스페이스)을 넘나들 때에는

커널 스페이스에 있는 코드는 유저 스페이스의 주소를 바로 사용하면 안 된다. 



ioctl을 사용하여 device driver(kernel space에 속하는 애)를 test할 application을 만들었다.  

이 application(user space에 속하는 애)에서 선언한 

int test_result[TEST_CASE];   

int형 배열을 ioctl 파라미터로 넘긴 적이 있다. 

driver의 ioctl에서 인자로 전달받은 배열에 테스트 결과 값을 써주기를 바라면서...


프로그램을 돌려본 결과 에러가 발생하였고,

발생한 에러는 다음과 같았다.

Unable to handle kernel access to user memory outside uaccess routines at virtual address


바로 위에서 말한 서로간의 view가 다르기 때문에, 넘겨준 주소(유저 스페이스)를 커널 스페이스에서 바로 사용할 수 없었다.


문제를 해결하는 방법은 의외로 간단하다.

include/linux/uaccess.h 

copy_from_user(), copy_to_user() 함수를 사용하면 된다.  


[형태]

include/linux/uaccess.h 
int copy_to_user(void __user* to, const void* from, unsigned long n)

[기능]

커널 스페이스의 주소 from 을 base로 데이터 n바이트를 유저 스페이스에 있는 주소 to에 copy한다.

[반환값]

복사 실패한 byte 의 수. 

성공 시 : 0



[형태]

include/linux/uaccess.h 

int copy_from_user(void* to, const void __user* from, unsigned long n)

[기능]

유저 스페이스에 있는 주소 from을 base로 데이터 n바이트를  커널 스페이스의 주소 to에 copy한다.

[반환값]

복사 실패한 byte 의 수. 

성공 시 : 0




--> 위 함수를 이용하여 error가 난 상황은 다음과 같이 해결하였다.

(driver 내에 ioctl에 엮은 함수)

1
2
3
4
5
6
7
8
9
10
11
12
13
void driver_test_run(..., void *address_from_user)
{
    int testcase_result[NUM_TESTCASE];
 
    ...
 
    for (i = 0; NUM_TESTCASE; i++)
        testcase_result[i] = testcase[i].run_test();
 
    copy_to_user(address_from_user, testcase_result, sizeof(testcase_result));
    
    ...
}
cs


반응형

'Linux' 카테고리의 다른 글

system() 함수 (c언어로 쉘 호출하기)  (0) 2020.10.23
리눅스 에러코드 (linux error code)  (0) 2020.10.23