필요한 파일의 내용을 메모리에 매핑함으로써 메모리에 직접 접근하는 방식을 통해 빠르게 내용을 읽고 쓸 수 있다. (기존의 방법은 read(), write() system call을 계속해서 사용했어야 했음)
memory mapping 과정
process가 memory mapping을 요청
운영체제가 file을 위한 virtual memory 영역을 만들어서 그 영역과 파일의 mapping을 수행한다.
프로세스는 파일의 내용을 메모리 읽듯이 읽고 쓸 수 있다.
커널의 경우에도 device memory를 kernel의 메모리에 map 할 때에 쓰이기도 한다.
Synchronization
프로세스는 memory의 mapping된 영역에 접근해서 읽고 쓸 텐데, 그러면 실제 file에는 변경사항이 언제 적용될까?
실제 disk는 읽고 쓰는 속도가 느리므로, 즉각적으로 업데이트를 바라는 것은 그냥 실제 file에 read, write를 쓰는 것과 다를 것이 없다.
그러면 synchronization이 이루어지는 방법?
msync() system call
mapped memory의 주소, synchronize 할 메모리 사이즈, flags를 인자로 받는다.
msync() 동작 후에 synchronization이 완료된다.
munmap() system call
기존 mapped된 메모리 영역을 해제한다.
mapped된 메모리의 주소와 메모리 영역의 사이즈를 인자로 받는다.
munmap() 동작 후에 synchronization이 완료된다.
truncate() & ftruncate()
파일의 크기를 조정하는 system call이다.
만약 실제 파일 크기보다 더 작은 사이즈로 조정한다면, 넘친 데이터는 버려지게 된다.
만약 실제 파일 크기보다 더 큰 사이즈로 조정한다면, 남는 영역은 zero나 null로 채워지게 된다.
파일에 큰 내용을 기입할 때 미리 사이즈를 늘려놓으면 더 빠르고 효율적으로 데이터를 집어넣을 수 있다(? 얘는 잘 모르겠음. 그냥 C++ STL vector가 늘어날 때마다 2배씩 사이즈 늘리는 식처럼 기존 write할 때에 동작하던거를 미리 사이즈 정해놓고 쓰면 더 효율 좋은 느낌인거 같긴한데 아닐수도)
shm_open 함수
shared memory object를 만드는 데에 쓰이는 함수.
shared memory object는 만들고 나서 ftruncate()를 통해 사이즈를 조정한 후, 그 다음에 mmap()을 통해서 메모리에 매핑을 하는데, 어차피 만들고 사이즈 조정할 때 메모리에서 작업을 하면 되는데 굳이 또 매핑을 해야 하는가? 아니면 실제 shared memory object가 존재하는 영역이 따로 있고 걔를 메모리에다가 매핑을 하는 것인가?
shm_open() 함수를 사용하고 나면 kernel 메모리에 shared memory object가 생성되고, 그 녀석의 file descriptor가 return 된다. (실제 kernel 메모리에 존재하게 된다.)
requested data가 memory에 있지 않으면 virtual memory management subsystem은 file system에서 memory의 physical page에 데이터를 복사한다. 그리고 이후의 작업은 이 mapping된 physical page에서 이루어 진다.
아직 virtual memory의 개념을 자세히 몰라서 헷갈리는 것 같은데, 약간 이렇게 생각하면 될 것 같다. 일단 virtual memory는 실제 physical memory를 가리키고 있고, 코드 상에서 특정 메모리 주소에 대한 access를 하면 그게 사실은 physical memory에 대해 access를 하고 있는 것이다. 이것을 memory mapping의 관점에서 보았을 때, 그냥 똑같다.(????) 횡설수설하지만, virtual memory상의 내용 수정이 이루어 졌다면 실제 file과 synchronize가 이루어 져야 한다고 시스템이 인식한다. (dirty 상태)
어쨌든 이것을 shared memory에 적용해 보면, mapping이 이루어 져서 각 프로세스의 virtual memory에 메모리 매핑이 되었고, 그런데 사실 이것은 kernel의 shared memory segment를 가리키는 것일 뿐이었다. 그저 여러 프로세스가 커널 상의 메모리를 동시에 가리키고 있었을 뿐이다. 그러니까 결국 작업 도중에는 msync나 munmap을 통해서 synchronize 해 줄 필요는 없고, 다 끝나고 나서 해도 무방하다. (물론 예외 상황이 있을 수는 있겠지만)
실제 mapping 된 memory가 deallocate되는 조건?
mapped된 모든 프로세스가 unmap을 하거나 종료가 되었을 경우.
synchronize는 munmap()을 호출하거나 프로세스가 종료가 되었을 경우 발생한다. Shared memory의 경우 한 프로세스가 munmap()을 호출한다고 해서 바로바로 synchronize가 발생하지는 않는다.
TMI
실제 프로세스들에서 사용중인 도중에 truncate()나 ftruncate()로 사이즈를 바꾸는 경우 어떻게 되는가?
mapping on files : 운영체제에 의해 다른 프로세스들의 mapping된 사이즈를 직접 바꿔준다.
shared memory : 안바꿔줌.
다른 이유 : mapping on files의 경우 process에 의해 바뀐 내용이 다른 프로세스들은 알 수 가 없지만, shared memory의 경우 다른 프로세스들이 바뀐 내용을 즉각적으로 알 수 있기 때문이라고 한다. 근데 왜 그런지는 모르겠음... 나중에 알게 될지도..?