안녕하세요. 현재 임베디드 1년차 개발자로 일하고 있고 메모리 기법 관련하여 공부를 하는 중 궁금한 점들이 생겨 고민고민 해봐도 답을 찾지 못해 이렇게 질문드립니다.
1. 보통의 강의나 기술블로그를 보면 하나의 프로세스가 실행되었을 때 메모리 영역을 어떠한 방식으로 할당하는지에 대해서만 나와있고 여러 프로세스가 동시에 동작 중일 경우엔 어떻게 할당하는지는 나와있지 않습니다...
만약 백그라운드로 3개의 프로세스가 동작 중일 때,
1) 메모리 유저영역은 한 번에 하나의 프로세스가 독점하며 Context switching할 때 프로세스들이 번갈아 독점하는 식
2) 3개의 프로세스가 메모리 유저영역에서 각자의 영역들을 커널로부터 할당받는 식
이 중에 어느 방법으로 커널이 메모리를 할당하나요? 아니면 이외 방법으로 할당을 하는 것인가요?
2. Flash memory나 hard disk 같은 저장매체에 있는 문서를 수정할 때 눈에 보이는 건 실제로 Ram에 있는 Data이고 저장을 해야만 저장매체로 Data가 옮겨지는 것으로 알고 있습니다.
그러면 문서를 수정하고 있을 때의 Data는 메모리의 커널 영역에 위치하는 것인가요?
3. 만약 메모리 크기보다 큰 실행파일을 실행했을 때(32bit 커널, 4GB메모리, 파일용량8GB),
커널은 이 실행파일을 메모리에 어떠한 방법으로 적재하고 관리를 하는 것인가요?
댓글 5
-
문c(문영일)
2018.10.17 22:28
-
인그니야
2018.10.18 21:15
정성스런 답변 감사합니다~!
답변을 보고 '가상 메모리 매핑 테이블로 인해 가상 주소로 봤을 때는 선형적으로 위치해있는 것처럼 보이나 실제 물리 주소로는 뒤죽박죽 되어있고 Fault로 인해 다시 재정립한다는 것' 등 예전에 공부했던 내용들이 다시 떠오르기도 하고, 몰랐던 내용들도 알게 되었습니다ㅎㅎ
2번 질문 같은 경우에는 Text 베이스인 vi 였는데 두 경우 모두 설명해주셔서 놀랐습니다. 제가 약간의 착각을 했던 것이 실행파일을 실행했을 때는 메모리에 Code, Data, Heap 등등의 영역으로 배치가 되는데 반해 포맷 없이 그냥 Text인 파일을 수정할 때는 Text파일이 메모리에 다르게 적재될 것이라고 생각했는데, 지금 생각해보니 일반 Text파일을 수정할 때도 결국은 Editor 프로그램, 즉 실행파일을 실행함으로써 이를 통해 수정하기 때문에 이 때는 Text파일이 아닌 Editor가 적재되는 것이란 걸 깨달았습니다.
그렇다면, 실제 화면에서 보이는 건 Editor 프로그램 내부의 데이터들일 것이고 저장을 하면 I/O Event로 저장매체에 옮겨지는 것이겠군요.
3번에서는 '유저 공간이라 함은 하나의 유저 프로세스가 사용될 수 있는 공간이 최대 3G이며, 여러 개의 프로세스 입장에서는 각자 3G의 공간을 사용할 수 있다는 뜻 입니다.' 이라고 하셨는데,
이 말씀은 여러 프로세스가 커널로부터 각자 가상 유저 영역을 3GB씩 할당 받는 다는 말씀이신가요? 아니면 1번-2)처럼 가상 유저영역 중에 여러 프로세스가 각자 자기들만의 영역을 할당받는 것이 맞는 건가요(이 말은 여러 프로세스가 사용할 수 있는 영역들을 합한 최대 값이 3GB라는 의미입니다.)???
그리고 크기가 큰 실행파일 같은 경우는 디렉터리 용량이 몇 십 GB인 게임을 생각했어요. 실행파일이 위치해있는 디렉터리의 크기가 몇 십GB이니 실행파일은 최소 몇 GB는 될 것이라 생각했던 건데 돌이켜보니 그 게임실행파일 하나의 크기를 확인해보지 않았었네요. 나중에 PC방 가면 한 번 확인해봐야 겠네요.
-
문c(문영일)
2018.10.19 10:05
글 재주가 부족해 제 생각을 글로 전달하는 일이 저에게는 매우 어려운 일입니다. 그럼에도 불구하고 잘 파악하시니 저도 놀랍습니다.
2번의 경우는 정말 잘 이해하신 것 같습니다.
3번은 프로세스마다 각각 3G 가상 주소 공간이 주어집니다. 조금 더 정확히 말씀드리자면 유저 공간이라 함은 생성되는 유저 프로세스 마다 4G의 가상 주소 공간이 주어지고, 커널에서 사용되는 1G의 공간을 제외하고, 나머지 3G를 유저용으로 사용합니다. 이의 관리를 위해 해당 유저 프로세스용 페이지 테이블이 생성됩니다. 만일 사용자가 5개의 application을 수행 시키면 5개의 유저용 3G 가상 공간이 생성됩니다.
문c 블로그 - http://jake.dothome.co.kr
-
인그니야
2018.10.23 10:59
음... 그렇다면 여러 프로세스 간 Context switching이 일어나는 때마다 Ram에는 그 때 주도권을 잡은 프로세스의 4GB공간(데이터)이 번갈아 적재되겠군요.
커널 없는 FW레벨에서 MMU개념으로만 보니 단순하게 생각했어요. 이런 쪽 개념 공부하는 거 정말 재미있네요ㅎㅎ
-
문c(문영일)
2018.10.23 17:33
네. 유저 프로세스가 바뀔 때 마다 이에 해당하는 유저 가상 공간을 바꿔줍니다. 리눅스 커널에서는 mm 스위칭으로 불립니다. 여러 cpu를 사용하는 SMP 환경에 실제 적용된 코드는 바꿔 주는 동작도 조금 복잡합니다. 나중에 도전해보세요.
문c 블로그 - http://jake.dothome.co.kr
.
안녕하세요? 답변이 점점 길어졌습니다. ^^
1. 번 -> 2)
각각의 프로세스가 각각 malloc() 요청을 하면 커널에 요청하기 전에 먼저 유저 공간에 위치한 glibc의 힙 관리자에게 메모리를 요청합니다. 힙 관리자가 일정 부분 보유한 힙 메모리를 요청한 프로세스에 할당합니다. 만일 힙 관리자가 보유한 메모리가 부족한 경우 커널에 요청하여 다시 힙 메모리를 채웁니다.
유저 프로세스가 할당 받은 메모리는 다른 프로세스와 공유하지 못합니다. 그러나 같은 프로세스에서 생성한 자식 스레드에게는 기본적으로 공유됩니다. 이렇게 한 프로세스 내부가 아닌 다른 프로세스 간에 메모리를 공유하여 사용하려면 공유 파일을 이용하거나 공유 메모리(shm)를 할당하여 사용해야 합니다. 그렇지 않으면 IPC(socket, mq, pipe, ...)를 통해 데이터를 전달하는 방법으로 사용합니다.
태스크 스케줄러가 프로세스를 여러 cpu로 로드 밸런싱하며 이쪽 저쪽 cpu로 프로세스가 옮겨다닙니다. 이 때 처음 할당 받은 메모리는 디폴트로 여러 cpu에서 모두 공유되는 상태입니다. 물론 DRAM의 속도가 느려 cpu간에 L1, L2, ... 캐시를 이용합니다. 이렇게 캐시를 사용할 때 여러 cpu를 사용하는 SMP 시스템에서 캐시 coherency 문제가 발생하며 이를 H/W 및 커널이 S/W로 보완하고 있습니다.
참고로 각 프로세스가 할당받은 메모리는 처음 사용하기까지는 실제 메모리 페이지를 할당한 상태가 아닙니다. 처음 액세스할 때 페이지 fault가 발생한 후에야 커널이 새로운 페이지를 할당하여 사용자 프로세스에 전달합니다. 커널은 이렇게 lazy 할당을 하여 메모리를 효율적으로 사용합니다.
참고: User virtual maps (brk) | 문c - http://jake.dothome.co.kr/user-virtual-maps-brk/
MM(Mapped Memory) Fault Handler | 문c - http://jake.dothome.co.kr/mm-fault/
Understanding glibc malloc | sploitfun -
https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/
2번 -> 문서 프로그램마다 데이터를 처리하는 방법은 다릅니다. 노트 패드처럼 파일을 모두 읽어 별도의 유저 공간 메모리에 할당하여 수정할 수 있습니다. 또한 vi 에디터와 같이 파일을 유저 공간에 매핑만 해 두고 커서를 이동하는 곳의 데이터만 유저 공간의 메모리에 할당하여 수정할 수도 있습니다. 즉 파일과 관련 없는 공간에서의 메모리 조작은 저장하기 전까지는 파일과 전혀 관련이 없습니다.
참고로 파일을 유저 공간에 오픈(open, fopen, ...)하여 처리할 때 커널이 처리하는 방법도 말씀 드리겠습니다. 처음 open을 하면 커널은 요청한 유저 가상 공간에 메모리의 할당 없이 해당 파일을 그대로 공간만 매핑합니다. 첫 페이지를 읽어야 할 때, 그 위치는 할당된 물리 메모리가 없으므로 page fault가 발생하고, 다시 커널은 이의 페이지 할당을 합니다. 그렇게 메모리 페이지가 준비된 후에는 파일 시스템의 버퍼에서 검색합니다. 버퍼에서 검색될 때 빠른 처리를 위해 파일의 한 페이지를 읽어올 때도 읽어올 파일의 커서(lseek 등으로 이동)가 위치한 해당 페이지뿐만 아니라 곧 읽어오리라 예상되는 다음 여러 페이지를 추가로 더 요청하여 page fault를 줄입니다. 만일 버퍼에 존재하지 않는 페이지인 경우 하드 디스크의 실린더 및 섹터에 접근해야 힐 떼 매우 느린 입출력을 수행합니다. 성능을 높이기 위해 파일 시스템에 따라 실제 물리적인 디스크 요청은 최소 수십~수백K 이상으로 구성된 블록 단위의 접근을 수행하여 커널의 버퍼 공간으로 읽어옵니다.
파일을 포함한 유저 메모리 할당은 커널의 일부 메모리 시스템의 이해만으로는 해결되지 않습니다. 실제 커널에서 수행되는 파일 처리에 대한 백그라운드를 명확히 이해하려면 다음과 같은 항목들을 알아야 하므로 매우 많은 분석이 필요합니다.
1) 메모리 관련
- 페이지 테이블 관련(MMU, TLB, pgd, pud, pmd, pte)
- 메모리 모델(page 디스크립터 및 mem_map, zone)
- 버디 시스템(zone, 버디 page 할당)
- page fault를 이용한 lazy allocation
- 페이지 회수 시스템(LRU, pagevecs, kswapd)
2) 파일 관련
- 파일 캐시 매핑(mmap)
- Virtual File System
- 파일시스템(ext2, ..)
3번 -> 32비트 커널을 사용하는 시스템에서는 PC 기준으로 1G는 커널이 사용하는 공간이고, 나머지 3G는 유저 공간입니다. 유저 공간이라 함은 하나의 유저 프로세스가 사용될 수 있는 공간이 최대 3G이며, 여러 개의 프로세스 입장에서는 각자 3G의 공간을 사용할 수 있다는 뜻 입니다. 하나의 프로세스가 3G를 초과하는 파일을 접근할 때 일부만 읽어 처리하는 방식으로 3G가 넘는 파일도 처리할 수 있습니다. 다만 실행 파일은 3G를 초과할 수 없습니다. 실행 파일의 경우 코드 뒷부분에 위치한 데이터 영역에 대한 위치를 잡아줘야 하는데 실행 파일이 3G를 초과하는 순간 영역을 확보할 수 없게 됩니다. 실제 3G 넘는 실행 파일은 저도 본 적이 없습니다.
1~2번에서 설명해 드린 바와 같이 실제 할당받은 메모리 공간뿐만 아니라 오픈된 파일도 실제 프로세스가 해당 페이지에 접근하기 전에는 실제 물리 메모리를 사용하지 않습니다. 1G 실행 파일도 실행하면 일부만 메모리에 적재하고 새로운 데이터 및 코드로 접근할 때마다 실제 물리 메모리를 추가로 사용하게 됩니다.
문c 블로그 - http://jake.dothome.co.kr