커널에서 관리하는 task_struct, mmap_struct, vmarea_struct 등등의 연관 관계에 대한 질문 입니다.
먼저 첨부하는 그림을 참조해 주시면 고맙겠습니다.
x86 시스템에서는 프로세스 마다 4G의 영역이 할당되고 그 중 0~3기가는 응용프로그램이 3~4기가는 커널이 사용한다고 알고 있습니다. 또한 응용프로그램이 사용하는 0~3기가 영역은 스택,힙,BSS,코드, 데이터 영역등으로 나뉘어서 사용되고 있다고 알고 있습니다.
그리고 커널이 사용하는 3~4기가의 영역에는 커널스택이 8K가 위치하게되고 커널 스택의 제일 아래에는 thread_info structure가 있어서 이 놈의 멤버인 task를 따라가 보면 current가 가르키는 task_structure를 찾을 수 있다고 알고 있습니다. 그리고 task_structure에는 mm이라는 포인터가 있는데 이놈을 따라가 보면 mmap_structe를 찾을 수 있고, 여기서 mmap 이라는 포인터를 따라가면 vmarea_structe를 찾을 수 있습니다. 여기서 다시 vmarea_structe에 있는 start와 end 포인터를 따라가면 연속된 메모리 공간을 찾을 수 있다고 알고 있습니다. 여기서 vmarea_struct는 next 포인터를 통해 다수개로 연결되어 있고요...
여기서 질문 있습니다. vmarea_struct의 start와 end가 가르치는 영역이 프로세스 마다 할당되는 0~3기가 영역을 가르키고 있는건가요 ? 즉, 제가 첨부한 그림이 맞게 그린건가요 ? 맞다면 start와 end 가르치는 영역은 스택, 혹은 heap, BSS 등등의 영역을 가르치게 되겠고 한 덩어리로 이루어진 virtual address는 같은 영역 즉,heap, 스택등을 가르키고 있을것 같은데요...맞나요 ?
고수님들의 답변 부탁드립니다.
첨부 | 파일 크기 |
---|---|
8.png | 31.21 KB |
네, 올바르게
네, 올바르게 알고계시네요.
다만 (어느 정도 트집입니다) 그림에서 next field로 연결된 리스트의 연결방향이 반대로 되었네요.
Address가 작은 놈이 리스트에서 먼저 위치하게 됩니다.
추가 질문좀 드려도 될까요?
headbang님 갑자기(?) 나타나셔서 근질근질한 부분을 아주 시원하게 긁어줘서 고맙습니다.
관련해서 두어가지 정도 질문좀 더 드리겠습니다.
(1) vm_area_struct의 start가 0x20000000의 값을 가지고 end가 0x2003ffff의 주소를 갖는다고 가정을 할때 해당 프로세스가 0x20021406에 있는 값을 읽으려고 한다면 mm_struct의 pgd_t 포인터형인 pgd를 따라가서 페이지 디렉토리를 찾은 후 0x20021406의 상위 10비트를 잘라서 0x80의 페이지 디렉토리 엔트리를 선택하고 다시 테이블 필드인 0x21을 찾아 페이지 프레임을 찾고 거기서 다시 offset 값인 12 비트를 잘라서 0x406값 만큽 더해서 해당 값을 찾게 되나요 ?
또한 여기서 vm_area_start와 vm_area_end가 가르치는 연속된 메모리 덩어리는 갖은 세그멘트 영역(예를 들자면 stack 영역 혹은 heap 영역등)을 가르치게 되는 건가요 ? 그렇다면 프로세스당 최소한 6개 이상의 vm_area_struct공간을 할당 될것 같은데요..여기서 6개의 이유는 text, data, bss, heap, shared libraries, stack 등등의 영역이 존재하기 때문에 그렇다고 생각하는 거구요..아래 2번 질문에 대한 답이 "맞다" 라면은 커널 쪽에도 스택 및 기타 등등의 세그멘트 영역이 존재하기 때문에 8~9개 이상의 vm_area_struct가 존재할거라 생각이 듭니다.
(2)vm_area_struct의 start와 end가 가르치는 영역이 0xc0000000 이상의 메모리 영역 즉, 유저공간의 메모기 영역이 아닌 커널 영역을 가르키기도 하나요 ?
(3)얼마전에 답변해 주신 글에 보면 understanding linux kernel 책을 보셨다고 하셔서 드리는 질문입니다.
책에 85 페이지에 보면 커널 스택에 대한 그림이 나와 있습니다. 2페이지에 할당되어 있다고 나와 있고요...여기서 커널 스택의 그림 옆에 주소값이 나와 있는데 가장 낮은 주소가 0x015fa000이고 가장 높은 주소가 0x015fbfff로 나와 있습니다. 커널 스택은 프로세스 주소 공간중 0xc000000이상의 주소에 위치해 있다고 알고 있는데요...그렇다면 커널 스택의 주소도 0xc000000이상의 주소 어딘가에 8K만큼 위치해 있어야 하는거 아닌가요 ?
알고 있는 내용들이 모두 어렴풋이 알고 있는것들이라 다시 보고 있는데, 고수분들한테 확인을 받지 않으면 영 찝찝해서 (이게 맞게 이해한건지 잘못 이해한건지..) 도통 넘어 갈 수 가 없네요...
그럼 답변 부탁드립니다.
(1) 페이지테이블을
(1) 페이지테이블을 찾아가는 과정은 정확합니다. 다만 가상주소에서 물리주소로의 변환은 소프트웨어적으로 하는게 아니라 하드웨어인 MMU가 자동으로 수행하게됩니다. (소프트웨어적으로 하면 엄청느리겠죠.)
프로세스 스위칭등을 통해 특정 프로세스 A가 CPU를 점유하게 되면 그 때 프로세스 A의 mm_struct 내 pgd 필드를 하드웨어 레지스터 cr3에 적게됩니다. 그럼 그 다음부턴 MMU는 프로세스 A의 페이지테이블을 이용해서 주소변환을 하게되죠.
프로세스당 최소한 6개 이상의 vm_area_struct 를 할당할 것인지는 솔직히 잘 모르겠습니다 (예외가 있을 수도 있겠죠. 예를 들면 shared library를 안쓰거나 해서요). 하지만 대부분의 프로세스는 그 정도 수의 vm_area_struct를 할당한다고 알고 있습니다.
(2) vm_area_struct의 start와 end가 가르치는 영역은 항상 유저공간의 메모리 영역입니다. 커널 영역은 절대로 가르키지 않습니다.
vm_area_struct를 사용하는 이유는 물리메모리를 효율적으로 사용하기 위해서입니다.
예를 들어 User Mode에서 malloc()을 통해 메모리를 할당했다고 하면 당장 해당하는 페이지프레임을 할당하지 않습니다.
단지 필요에 따라 힙 영역을 늘려주기면 합니다--힙 영역을 나타내는 vm_area_struct의 end가 증가합니다.
실제 페이지프레임을 할당해주는 과정은 다음과 같습니다.
가. malloc()으로 할당된 메모리를 접근한다.
나. 매핑된 페이지프레임이 없으므로 페이지폴트가 발생한다.
다. 페이지폴트 핸들러는 폴트를 일으킨 가상주소와 해당 접근이 정당한지 조사한다. 즉, 가상주소를 포함하는 vm_area_struct를 찾고 또 접근 권한을 조사하게됩니다.
라. 페이지프레임을 할당하고 페이지테이블에 등록한다.
즉, 위와 같이 하여 물리메모리 할당을 최대한 늦출 수 있어 효율적인 사용을 할 수 있습니다. 사용자 영역의 프로그램이란 것이 믿을 게 못돼서 malloc()할 때마다 물리메모리를 할당해주면 실제로는 접근도 안하고 프로그램이 끝날 경우도 많습니다. 아니면 malloc()한 시점과 실제 접근한 시점이 꽤 차이가 날 수도 있고요.
이와 달리 커널 영역에서는 kmalloc(), vmalloc() 등을 통해 필요한 물리메모리를 즉각적으로 할당받습니다.
(3) 그림에서는 8KB가 물리적으로 연속적이다는 것을 강조하기 위해 물리메모리주소를 나타낸게 아닌가 싶습니다.
답변 감사
답변 감사 드립니다.
(3)번 질문에 대한 대답과 관련해서... 가상주소로 나타낸다면 당근 0xc000000이상의 주소값에 커널스택이 존재한다는 말씀으로 이해하면 되는거죠 ? 책에 나와 있는 주소값은 물리주소를 의미하는 거고...
이거 질문이 계속 이어지네요...
커널 쓰레드 같은 경우는 mm 필드가 null이기 때문에 일반 프로세스와 같이 메모리 관리를 못해줄 것 같은데요...
커널 쓰레드내부에서 kmalloc() 으로 할당한 메모리 영역은 어디로 할당이 되며, 이에 대한 정보는 어디에(어느 descriptor에) 저장이 되남요 ?
질문하신 내용은
질문하신 내용은 9.2.1에 설명되어 있네요.
정리하자면 task_struct에 mm 필드와 active_mm 필드가 있고 주소변환에는 active_mm을 사용합니다. 커널 쓰레드의 active_mm 필드는 마지막으로 수행된 일반 프로세스의 mm 필드와 같습니다.
kmalloc()은 별다른 이슈가 없어보이네요.
kmalloc()은 slab allocator인 kmem_cache_alloc()을 호출해서 메모리 할당을 받습니다.
그런데 slab allocator가 관리하는 영역은 high memory 아래이기때문에 항상 가상주소를 가지고 있습니다. 따라서 kernel master page table에 등록하는 부가적인 작업은 없습니다.
alloc_pages()는 사용처에 따라 페이지프레임들을 ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM 등의 세가지 영역에서 할당할 수 있습니다.
alloc_pages()은 가상주소를 반환하지 않고 'struct page *' 형을 반환합니다.
할당한 페이지프레임은 필요에 따라--즉 ZONE_HIGHMEM에서 할당되어 가상주소가 없을때--
명시적인 kmap() 호출을 통해 가상주소를 할당하게되는데 이때 kernel master page table에 등록합니다.
vmalloc()의 경우에는 init_mm (프로세스 0 즉 swapper의 mm_struct)의 pgd 필드가 가르키는 kernel master page table에 등록이 됩니다.
실제 kernel control path가 이용하는 context(커널 쓰레드이든지 일반 프로세스이든지)의 페이지테이블에는 페이지폴트 일어난 후 페이지폴트 핸들러가 등록하게됩니다.
댓글 달기