[SW정글][핀토스] User Memory

JST·2023년 10월 11일
0

핀토스 VM

목록 보기
1/5

User space memory access from kernel

  • 유저의 가상 주소 공간에 접근하는건 커널이다. 즉 운영체제가 접근해야 한다.

리눅스 메모리

  • 리눅스에서는 유저 메모리와 커널 메모리가 독립적이다. 실제 주소 공간에도 분리되어 구현되어 있다.
  • 이는 각 프로그램이 하나의 메모리를 갖게 하는 환상을 줄 뿐만 아니라 각 주소 공간이 독립되어 있기 때문에 운영체제를 보호하고(protection) 각 프로세스 간 보안에도 안전하다(isolation)는 장점이 있다.
  • 하지만 각 구역을 독립하고 고립함으로써 보안을 확보할 수 있는 반면 그만큼 tradeoff가 생기는데, 각 프로세스 및 커널은 물리 메모리의 다른 영역을 참조하는 동일한 주소를 가질 수 있기 때문에 즉시 메모리를 공유할 수 없다.

주소 공간

주소 변환

  • 주소 변환을 통해 하드웨어는 명령어 반입, 탑재, 저장 등의 가상 주소를 정보가 실제로 존재하는 물리 주소로 변환함
  • 운영체제는 메모리의 빈 공간과 사용 중인 공간을 항상 알고 있어야 하고, 메모리 사용을 제어하고 관리한다.
  • 이 모든 작업의 목표는 프로그램이 자신 전용 메모리를 소유하고 그 안에 자신의 코드와 데이터가 있다는 환상을 만드는 것이다.
  • 많은 프로그램이 물리 메모리를 공유하기 때문에 이를 적절하게 위치시켜야 한다.

베이스 레지스터

base 레지스터 값은 가상 메모리와 실제 메모리 간 offset 개념

bound 레지스터

바운드 레지스터는 보호를 지원하기 위해 존재한다.

프로세서는 먼저 메모리 참조가 합법적인가를 확인하기 위해 가상 주소가 바운드 안에 있는지 확인한다. 위의 이미지로 치면 바운드 레지스터 값은 16KB이다. 그 이하는 운영체제 공간이기 때문에 건들면 안됨 => 이를 바운드 레지스터가 보호해주는 것.
만약 프로세스가 바운드보다 큰 가상 주소 혹은 혹은 음수인 가상 주소를 참조하면 CPU는 예외를 발생시키고 프로세스는 종료된다. 이러한 주소변환(하드웨어는 프로세스가 참조하는 가상 주소를 받아들여 데이터가 실제로 존재하는 물리 주소로 변환하는 과정)은 프로세스가 실행될 때 일어나며 그 이후에도 주소 공간이 이동할 수 있기에 동적 재배치라고 하는 것이다.

mmu

이 베이스와 바운드 레지스터는 앞서 말했듯 CPU 칩 상에 존재하는 하드웨어이며, 이들을 메모리 관리 장치(Memory management unit, MMU)라고 한다. 이들에 값을 변경하는 명령어를 제공하는 건 운영체제의 특권 명령(커널 모드)이다. 만약 프로세스 범위 바깥 메모리를 참조하려 하면 운영체제가 exception handler를 실행시킨다(보통은 프로세스를 종료시킴).

결론

주소 변환이라고 하는 가상 메모리 기법을 통해 운영체제는 프로세스의 모든 메모리 접근을 제어하며 접근이 항상 주소 공간의 범위 내에서 이루어지도록 보장한다. 이 기술을 가속화하는 것은 하드웨어의 지원으로, MMU라는 한 쌍의 레지스터(베이스, 바운드)가 가상주소로부터 실제 주소로 변환시킨다. 이 방식을 베이스 & 바운드(=동적 재배치)라고 한다. 하지만 위에서 말했듯, 위 내용은 아직 페이징 기법이 적용되지 않은 원시적인 형태의 베이스 & 바운드라 세그멘테이션을 마저 설명하고 아래 페이징까지 쭉 이어서 소개한다.

세그멘테이션

앞에서는 프로세스 주소 공간 전체를 메모리에 탑재하는 것을 가정했다. 하지만 이렇게 되면 단편화가 일어나 메모리 낭비가 심하다. 이를 해결하기 위한 아이디어가 세그멘테이션이다. 아이디어는 MMU에 하나의 베이스, 바운드 값이 존재하는 것이 아니라 세그멘트마다 베이스와 바운드 값이 존재한다. 쉽게 말해 코드/스택/힙 각 세그멘트에 대해 베이스와 바운드 값이 존재해 하나의 가상 주소 공간이 물리 주소 공간 상에서 연속적으로 붙어있는 게 아니라 세그멘트 단위로 흩어지게 만드는 것이다. 이렇게 하면 사용되지 않는 가상 주소 공간이 물리 메모리를 차지하는 것을 방지할 수 있다.


만약 힙의 마지막을 벗어난 주소에 접근하려고 하면 segment fault

페이징

가상 메모리를 활용하면 유저 프로그램은 가상 주소를 활용하여 메모리에 접근하려 할 것이다.

만약 운영체제가 부여해준 가상 주소가 굉장히 파편화되어 있다면 어떨까?

극단적으로 생각해서, 매 바이트가 각각 다른 물리 주소에 매핑(mapping)되어 있다고 해보자.

그러면 운영체제는 각 가상주소와 물리주소를 매핑하는 정보를 모두 갖고 있어야 할 것이다. 예를 들어 1MB를 할당했다고 하면, 약 백만 개의 매핑 정보를 갖고 있어야 하는 것이다. 각 매핑 정보가 2바이트 (가상주소 1바이트, 물리주소 1바이트라고 가정하면) 씩만 차지한다고 해도, 매핑 정보를 보관하기 위해 운영체제는 2MB 의 공간을 사용해야 한다. 배보다 배꼽이 더 큰 상황이다.

이런 문제를 해결하기 위해, 일정 크기의 블록으로 메모리를 떼어주는 페이징이라는 개념을 도입하게 된다. Pintos에서는 4KB를 페이지 사이즈로 잡는데, 그러면 1MB는 256개 페이지로 나눠지게 된다. 아까처럼 각 매핑 정보가 2바이트만 차지한다고 가정하면, 이번에는 매핑 정보를 위해 512바이트 (0.5KB, 약 0.0005MB) 의 메모리 공간만 사용하면 된다. 차지하는 메모리 공간의 크기가 현저히 적어진 것을 확인할 수 있다.

이렇듯 페이징은, 가상 메모리와 물리 메모리를 운영체제가 효율적으로 관리하기 위해 도입한 개념이다.

기본적으로 가상 메모리와 물리 메모리를 특정 크기 (x64-86 linux에서는 4KB)의 블록으로 나눠서 다루게 된다.

이전의 세그멘테이션에서는 힙과 스택이 확장하는 방향성 등을 고려했으나 여기서는 상관이 없다. 공간이 분할되어 있기 때문이다. 따라서 가상 주소에서 연속적인 공간을 요청하더라도 운영체제는 불연속적인 페이지 여러 개와 매핑해주면 된다.

페이지 테이블

주소 공간의 각 가상 페이지에 대한 물리 메모리 위치를 기록하기 위해 운영체제는 각 프로세스마다 페이지 테이블(page table)이라는 자료 구조를 유지한다. 이 페이지 테이블의 역할은 주소 공간의 가상 페이지 주소 변환 (address translation) 정보를 저장하는 것이다. 이 테이블을 통해 각 페이지가 저장된 물리 메모리의 위치를 확인할 수 있다.

vpn

프로세스가 생성한 가상 주소의 변환을 위해 아래 이미지와 같이 가상 주소를 가상 페이지 번호(virtual page number, VPN)와 페이지 내 오프셋 2개의 구성 요소로 분할한다. 가상 주소 번호(VPN)을 나타내려면 주소 공간 크기가 64바이트이니 각 주소 공간 하나에 대한 정보를 표기하기 위해 총 6비트가 필요하다. (26 = 64). Va5는 가상 주소의 최상위 비트이고 Va0은 최하위 비트이다. 주소의 최상위 2비트는 물리 주소의 페이지를 식별하는 값이다. 16바이트짜리 페이지가 4개 있으니 2비트(22=4)면 4개에 대한 정보를 각각 식별 가능하다. 따라서 앞 2비트는 가상 주소 번호인 VPN이다.

나머지 4비트 오프셋은 페이지 내에서 우리가 접근하기를 원하는 바이트의 위치를 나타낸다.(페이지도 하나의 공간이니 해당 공간에서 특정 정보에 접근하기를 원할 테니까)
이를 물리 주소로 어떻게 변환할까? 예를 들어 프로세스가 생성한 가상 주소가 21이라고 하자. 그러면 먼저 21을 이진수 010101(2)로 변환한다. 앞 번호 01은 VPN, 뒤 0101은 오프셋이다.

0개의 댓글