안녕하세요
저번 시간에 이어, 가상 메모리에 대한 이야기를 본격적으로 시작하고자 합니다.
0. 가상 메모리는 무엇일까?
1. 가상 메모리의 작동 원리
0. 가상 메모리는 무엇일까?
가상 메모리(Virtual Memory), 즉 가상의 메모리입니다.
메모리는 CPU와 함께 프로세스 수행 시 중요한 역할을 담당하고 있습니다. CPU는 특정 프로그램 내, 함수를 무수히 실행한다면, 메모리는 해당 함수(코드)가 저장될 수 있는 공간을 제공합니다.
가상 메모리는 OS에서 실제 메모리 보다 큰 주소 공간을 프로세스에게 제공하기 위해 고안된 메모리 관리 기법입니다.
하지만, "그래서 도대체 뭐야?"라는 의구심이 계속 생깁니다.
우리가 윈도우즈 PC에서 Excel.exe 프로그램을 더블 클릭하면 엑셀 프로그램이 실행됩니다.
그러면, 엑셀의 여러 기능들이 실행되기 위해선 CPU와 메모리가 필요하겠죠?
여기서 Excel 프로세스만의 함수가 저장되는 공간을 가상 메모리로 이해해 주시면 됩니다.
엑셀 프로그램 이외에, 크롬을 열었다고 생각해 봅시다. 그러면, 크롬 작업을 위한 별도의 가상메모리가 하나 더 할당됩니다.
즉, 프로세스의 개별 작업 공간입니다.
가상 메모리가 없으면 물리 메모리(RAM)를 크롬이나 엑셀 등이 같이 공유했겠죠?
이러한 가상 메모리는 OS 커널이 생성/관리합니다.
위 사진을 통해 알 수 있듯이, 프로세스마다 개별 작업 공간이 존재하는 것을 볼 수 있습니다.
1. 가상 메모리의 작동 원리
그러면 가상 메모리는 어떻게 작동되는 것일까요?
먼저, 프로그램의 실행과정, 즉 프로세스 생성 과정을 이해해야 합니다.
처음에 실행되어야 할 파일, A.exe 가 SSD에 적재되어 있습니다.
1. User가 A.exe 실행 요청을 하면 A.exe 파일이 물리 메모리(RAM)에 로드됩니다.
2. 커널의 시스템 콜(execve)을 통해, A.exe 프로그램 경로와 실행에 필요한 인자를 커널에게 전달합니다.
3. 커널은 Process A를 생성합니다.
4. 커널은 Process A를 위한 가상 메모리 공간(Virtual Memory A)을 할당합니다.
5. execve 시스템 콜로부터 전달받은 프로그램 정보를 가상 메모리 공간에 매핑합니다.
직접, 프로그램을 실행하고 가상 메모리 매핑 과정을 살펴보겠습니다.
간단히, hello world를 print 하는 프로그램(ELF)을 만들었습니다.
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
[root@ip-10-0-131-237 test]# ls -al
total 28
drwxr-xr-x. 2 root root 34 Jun 15 18:44 .
dr-xr-x---. 12 root root 4096 Jun 15 18:44 ..
-rwxr-xr-x. 1 root root 17504 Jun 15 18:44 hello
-rw-r--r--. 1 root root 75 Jun 15 18:44 hello.c
[root@ip-10-0-131-237 test]# file hello
hello: ELF 64-bit LSB executable, x86-64
1. hello라는 프로그램을 실행해 보고, strace를 활용하여 시스템 호출을 추적하자.
strace -o strace.hello ./hello
[root@ip-10-0-131-237 test]# cat strace.hello
execve("./hello", ["./hello"], 0x7ffc237213e0 /* 37 vars */) = 0
brk(NULL) = 0x14df000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe417925e0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=30887, ...}) = 0
mmap(NULL, 30887, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe4b6531000
close(3) = 0
실제, execve 시스템 콜을 호출하는 것을 볼 수 있습니다.
2. hello 프로세스의 가상 메모리 할당 주소와 gdb 디버거를 통해 비교해 보자.
2-1. 위 소스 코드를 실행하면 실제 프로세스를 확인할 수 없으니, sleep 함수를 추가한 뒤, gdb를 통해 디버그 합니다.
[root@ip-10-0-131-237 test]# gdb ./hello
2-2. run을 입력하여 실행시킵니다.
(gdb) run
Starting program: /root/test/hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Hello, World!
2-3. /proc/pid/maps를 통해 hello 프로세스가 할당받은 가상 메모리 주소를 확인합니다.
[root@ip-10-0-131-237 292146]# cat maps
00400000-00401000 r--p 00000000 ca:04 10501005 /root/test/hello
00401000-00402000 r-xp 00001000 ca:04 10501005 /root/test/hello
00402000-00403000 r--p 00002000 ca:04 10501005 /root/test/hello
00403000-00404000 r--p 00002000 ca:04 10501005 /root/test/hello
00404000-00405000 rw-p 00003000 ca:04 10501005 /root/test/hello
00405000-00426000 rw-p 00000000 00:00 0 [heap]
7ffff7c00000-7ffff7c28000 r--p 00000000 ca:04 8544281 /usr/lib64/libc.so.6
7ffff7c28000-7ffff7d9d000 r-xp 00028000 ca:04 8544281 /usr/lib64/libc.so.6
7ffff7d9d000-7ffff7df5000 r--p 0019d000 ca:04 8544281 /usr/lib64/libc.so.6
7ffff7df5000-7ffff7df6000 ---p 001f5000 ca:04 8544281 /usr/lib64/libc.so.6
7ffff7df6000-7ffff7dfa000 r--p 001f5000 ca:04 8544281 /usr/lib64/libc.so.6
7ffff7dfa000-7ffff7dfc000 rw-p 001f9000 ca:04 8544281 /usr/lib64/libc.so.6
7ffff7dfc000-7ffff7e09000 rw-p 00000000 00:00 0
7ffff7fb5000-7ffff7fb9000 rw-p 00000000 00:00 0
7ffff7fc1000-7ffff7fc5000 r--p 00000000 00:00 0 [vvar]
7ffff7fc5000-7ffff7fc7000 r-xp 00000000 00:00 0 [vdso]
7ffff7fc7000-7ffff7fc9000 r--p 00000000 ca:04 8544277 /usr/lib64/ld-linux-x86-64.so.2
7ffff7fc9000-7ffff7fef000 r-xp 00002000 ca:04 8544277 /usr/lib64/ld-linux-x86-64.so.2
7ffff7fef000-7ffff7ffa000 r--p 00028000 ca:04 8544277 /usr/lib64/ld-linux-x86-64.so.2
7ffff7ffb000-7ffff7ffd000 r--p 00033000 ca:04 8544277 /usr/lib64/ld-linux-x86-64.so.2
7ffff7ffd000-7ffff7fff000 rw-p 00035000 ca:04 8544277 /usr/lib64/ld-linux-x86-64.so.2
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
(gdb) info proc
process 292146
cmdline = '/root/test/hello'
cwd = '/root/test'
exe = '/root/test/hello'
2-4. main 함수의 가상 메모리 주소를 확인합니다.
(gdb) info address main
Symbol "main" is at 0x401136 in a file compiled without debugging.
디버거를 통해, main 함수의 가상 메모리주소가 0x401136 임을 알 수 있습니다.
아래의 가상 메모리 내, 텍스트 세그먼트에 위치해 있음을 알 수 있습니다.
[root@ip-10-0-131-237 292146]# cat maps
00400000-00401000 r--p 00000000 ca:04 10501005 /root/test/hello
00401000-00402000 r-xp 00001000 ca:04 10501005 /root/test/hello
오늘은 가상 메모리의 매핑 과정을 눈으로 확인해 보았는데요. 실제 write 시스템 콜이 호출되면 Physical Memory Access 가 일어나게 됩니다.
어떻게 가상 메모리 주소만으로 물리 메모리 주소에 접근할 수 있을까요?
바로, 페이지 테이블이 존재하기 때문인데요!
다음 시간에는 매핑된 페이지 테이블을 확인해 보고 코드 수행 동작을 살펴보는 시간을 갖겠습니다.
오늘도 감사합니다.
'OS' 카테고리의 다른 글
가상메모리에 관하여(3) (0) | 2024.07.07 |
---|---|
가상 메모리에 관하여(1) (0) | 2024.06.01 |