본문 바로가기

Programming/Linux_Kernel

mmap 을 이용한 process memory mapping

원문 : http://naito.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%A7%A4%ED%95%91memory-mapping


공부하면서 더 내용추가를 할 예정이다.




프로세스 메모리 매핑
대량의 데이터를 처리하는 하드웨어를 다루는 DD작성시 mmap은 필수적으로 구현해야..
app에서 dd로 hw를 제어할때 read,write,ioctl은 프로세스 메모리공간과 커널 메모리 공간사이의 메모리 전달과정이 수반되기 때문에 매우 비효율적이다. 고로 mmap을 이용해 직접 hw의 io주소 공간을 메모리 복사없이 직접적으로 사용할 수 있다.

mmap함수는 메모리 주소를 이용해 파일에 접근하도록 하는 함수다. 그러나 디바이스 파일에 적용할 경우 디바이스에서 제공하는 물리주소를 app에서 사용할 수 있게 한다.  사용법은 간단한다.
app가 동작하는 메모리 영역에 dd가 제공하는 물리주소를 매핑하면 된다.

app가 디바이스파일을 대상으로 mmap함수를 호출하면 커널은 메모리매핑에 대한 작업을 수행하고 물리주소를 얻을 수 있게  디바이스 파일의 파일 오퍼레이션 구조체에 선언된 mmap함수를 호출한다. 이 함수는 매핑을 수행하고 이렇게 처리된 주소는 app로 반환된다.

물리주소를 가상주소로 매핑하는 방법에는 두가지가 있다.
mmap함수를 사용해 app의 프로세스 가상 주소에 매핑하는 방법이고 또 하나는 ioremap함수를 사용해 커널의 가상주소에 매핑하는 것이다.


응용프로그램에서 호출하는 mmap()
이 함수는 매개변수로 전달된 디바이스 파일에 연결된 dd에게 start매개변수로 지정한 값을 시작주소로 주고, offset 매개변수가 가리키는 물리적 수조를 프로세스 메모리 공간 내에 매핑시킬 것을 커널에 요구한다.

char *addr = (char *)mmap(0, 4*4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x0b0000);

매핑한 영역을 해제하려면 munmap함수를 사용한다

munmap(addr, 4*4096);


DD의 mmap구현
app에서 mmap함수를 디바이스파일에 대해 호출하면 커널에서 적절하게 처리된 이후에 파일에 연결된 dd의 파일 오퍼레이션에 등록된 mmap함수가 호출된다. 적절한 처리란 커널 내부의 VM에서 app에서 호출한 mmap함수의 매개변수를 이용해 메모리 매핑이 가능하도록 mv_area_struct 구조체를 생성, 초기화한 후 전달하는 것을 말한다.

DD의 mmap함수 호출되면 remap_page_range함수를 호출해야 한다. 이 함수는 물리 주소를 가상메모리 영역으로 매핑한다. 

이 함수 호출전에 매핑정보의 유효성확인, 실제물리주소구하기, 물리주소의 특성 기술을 먼저 해야 한다.
매핑정보검사는 크게 두 가지인데 첫번째 PAGE_SIZE로 정렬되었는지, 두번째는 매핑 가능한 영역을 초과하는지여부다. 물리주소는  대부분 dd에서 제공한다. 이때는 app의 mmap함수에서 전달하는 offset값을 물리주소의 오프셋값으로 사용할 수 있다.  대표적인 디바이스파일은 /dev/mem이다. 물리주소의 특성은 두가지로 나뉜다.  I/O영역인지 표시하는 것이고 다른 하나는 예약 영역으로 표시하는 것이다.


I/O영역
일반메모리가 아닐 경우 VM_IO 플래그를 OR시켜야 하드웨어 이상으로 I/O영역의 주소 공간 접근이 실패하더라도 프로세서가 죽지 않는다.  


예약영역
VM_RESERVERD를 OR시켜 mmap대상이 되는 메모리를 스왑 아웃 대상에서 제외하도록 한다. 매핑된 메모리는 스왑아웃될 경우 문제가 발생할 수 있다. kmalloc과 같은 함수를 이용하여 할당했을 때 반드시 사용되지 않을 경우에는 해제시켜야 한다. 그렇지 않으면 app가 munmap함수를 호출할 때 문제가 발생한다. 


nopage 매핑방식
DD에서 mmap을 처리할 때 remap_page_range()함수를 이용하여 필요한 매핑을 직접 처리하는 방법과 nopage방식을 사용해 페이지 단위로 매핑하는 방법이 있다.

nopage방식은 app에서 mmap함수를 호출하여 프로세스에서 사용할 수 있는 주소를 먼저 요구한다. 이 방식은 remap...함수처럼 app에서 mmap함수를 호출하면 DD의 파일오퍼레이션 구조체에 정의된 mmap함수가 호출된다.

이 방식은 remap_page..함수를 수행하지 않기 때문에 app가 mmap을 통해 주소에 접근하면 페이지폴트가 발생한다. 커널은 이 공간이 예약된 주소 공간인지 확인하고 예약된 영역이면 vma->vm_ops->nopage에 선언된 함수를 호출한다. 이 함수는 페이지폴트가 발생한 가상주소페이지의 물리주소페이지를 DD에 요청한다.

DD에 선언된 nopage함수는 요청된 영역에 대한 검사를 수행하고 성공적으로 수행되어 해당 영역에 해당하는 물리주소를 반환하면 app는 해당영역의 페이지폴트에서 벗어난다. 이 다음부터는 페이지폴트가 발생하지 않는다.

nopage방식은 물리적인 I/O메모리공간을 app의 프로세스에서 사용하기보다는 DD에 의해 할당된 메모리 공간을 공유하기 위해 사용한다.  DMA버퍼공간이 그 예다.  nopage방식으로 다룰 때 가장 편리한 것이 바로 vmalloc함수로 할당한 공간이다.

struct page *xxx_nopage(struct vm_area_struct *vma, unsigned long addr, int *type);

addr에 전달된 가상주소에 대응하는 물리주소 페이지를 반환하도록 작성되어야 한다.
nopage함수에서는 보통 다음 세가지를 구현해야 한다.
요구한 addr에 대한 영역검사
요구한 addr에 대한 물리 주소 페이지 획득

구한 물리주소 페이지의 참조 수 증가