반응형


메모리 관리 개념


어떤 프로그램이든 프로세스가 되어 프로세서에 적재되기 위해서는 메모리에 적재되어야 실행이 가능하다.

따라서 메모리는 중요한 작업 공간이고 한정된 메모리를 다중 프로그래밍 환경에서 이용하기 위해서는 여러 프로세스가 함께 메모리를 사용하므로 효율적인 관리를 해야한다.


이러한 메모리 관리를 위해 메모리 관리자가 존재하고, 메모리 관리 장치(MMU, Memory Management Unit)와 OS 관리 모듈과 함께 진행한다.


이때 몇가지 정책에 따라 메모리 관리를 진행한다.


적재 청책(Fetch Policy)은 디스크에서 메모리로 프로세스를 언제 가져와야 할지를 정하는 것이다.


배치 정책(Placement Policy)은 디스크에서 메모리로 가져온 프로세스를 어느 위치에 저장 할 것인지 정하는 것이다.


대치 정책(Replacement Policy)메모리가 충분하지 않을 때 현재 메모리에 적재된 프로세스 중 제거할 프로세스를 결정하는 방법이다.




논리적 주소, 물리적 주소, 메모리 매핑


메모리 주소는 크게 두가지로 나눌 수 있다.


1. 논리적 주소


논리적 주소는 가상 주소라고도 하며 프로그래머가 목적 코드가 저장된 공간과 프로그램에서 사용하는 배열, 구조체 등등이 이에 해당한다.


2. 물리적 주소


물리적 주소는 논리적 주소에 대응되는 실제 주소로 메모리 칩이나 디스크 공간에서 만든다.


이때 논리적 주소와 물리적 주소 변환은 MMU에서 관리한다.



MMUCPU에서 논리적 주소를 받아 고정 분할, 동적 분할, 페이징, 세그멘테이션 등등을 이용한 변환 방법을 사용하여 물리적 주소로 변환시킨다.




바인딩(Binding)


일반적으로 속성과 개체 사이 또는 연산과 기호 사이와 같은 연관 ( Association ) 

변수에 변수와 관련된 속성을 연관시키는 것

값들이 확정이 되어 구속(Bind) 된 상태 

함수 호출된 부분에서 함수가 위치한 메모리 번지를 연결


바인딩 타임(Binding Time)


프로그램에서 변수들이 갖는 속성이나 값이 완전히 결정되는 시간. 즉, 이름에 속성이 연결되는 시간



바인딩 타임의 종류(Variety of Binding Time)


1. 컴파일 시간 (Compile Time)

프로세스가 메모리에 적재될 위치를 컴파일 과정에서 알 수 있다면 이때 바로 물리적 주소를 생성 할 수 있다.


-원시프로그램을 번역할 때 이루어지며 . Data, Stack 등 메모리에 들어갈 변수가 정해짐.

-정적바인딩, 변수의 이름 또는 형과 프로그램 문장 구조


-고레벨 언어를 Binary File , Obj 파일로 전환 

** Obj 파일로 변환하는 과정에서 코드가 문법에 맞는지 검사 (Parsing)


2. 적재 시간(Load Time)

프로세스를 어디에 적재해야 할 지 컴파일 과정에서 알려주지 않으면 컴파일러는 대체 가능한 상대 주소를 생성한다.


-변수를 메모리로 적재할 때 (상대 주소가 결정됨) 운영 체제에서 메모리로 프로그램을 불러와 실행



3. 실행 시간(Run Time)

한 프로세스가 동일한 장소에서 작동한다면 적재 시간 과정에서 바인딩 할 수 있지만 프로세스를 실행하는 도중에 메모리의 한 세그먼트에서 다른 세그먼트로 이동한다면 바인딩은 수행 시간까지 지연된다.


-프로그램을 실행 할 때 이루어지며 변수의 값을 확정

-변수의 기억장소 할당

-실행 부터 종료 까지 변수 값을 바인딩


 

* 링커와 로더


링커 

모든 오브젝트 파일들을 하나의 오브젝트 파일로 합친다.(Executable file로 합친다)


로더

Executable file 을 읽는다. 

메인 메모리에 오브젝트 파일에있는 내용들을 올리고 

context들을 만들고 프로그램을 수행시킬 수 있도록 만든다. 

OS의 한 부분이다.



정적 바인딩 (Static Binding)

실행 시간 전에 일어나고, 실행 중에 변하지 않는 상태로 유지되는 바인딩

ex) int a; // 정수 타입(Integer Type)을 이름 'a'에 정적으로 바인딩



동적 바인딩 (Dynamic Binding)

실행 시간 ( Run-Time ) 중에 일어나거나 프로그램 실행 과정에서 변경되는 것

(정적 바인딩한 변수들도 실행 중에 변경 되면 동적 바인딩이다)

ex) int * ptr = malloc( sizeof(int) * 10 ); 



할당 (Allocation)

기억장소 할당 : 변수에 메모리 공간을 바인딩( Binding )하는 과정



정적 할당 (Static Allocation)

메모리 에 적재될 크기를 미리 아는 것

Run-Time 에 각 타입에 맞는 메모리 영역 ( Segment ) 에 할당


 

동적 할당 (Dynamic Allocation)

외부환경에 의해 변화하여 크기를 미리 알 수 없는 것

명시적인 명령어에 의해 실행되는 할당

동적할당이 공간은 힙영역( Heap Segment )

포인터나 참조 변수를 통해 접근이 가능




메모리 할당 방법


연속 메모리 할당은 메모리에 프로세스를 적재할 때 연속적으로 적재하는 방법이다.


연속 메모리 할당에는 고정 분할 방법가변 분할 방법이 있다.



고정 분할 방법


사용자가 쓸 수 있는 메모리의 범위 내에서 특정 크기로 잘라 분할된 메모리를 만드는 것이다.


이때 각 부분마다 분할된 시작 주소가 생성되고 각 분할에는 하나의 프로세스만 포함되게 된다.


아래 그림에서 보면 알 수 있듯이


분할 기준 레지스터(PBR)값에 논리적 주소를 더해 생성하는데 이때 논리적 주소가 분할된 메모리보다 크면 오류가 발생하고 작으면 내부 단편화가 발생한다.


즉, 분할 메모리 주소가 100~200이었는데 논리적 주소가 0~101이었다면 100 + 101이기에 100 ~ 201까지 참조하기에 분할 크기보다 커져 오류가 생기고


논리적 주소가 0~50이라면 100~150까지 참조하기에 151~200까지는 빈 공간이 되어 내부 단편화가 일어난다는 것이다.


이때 고정 분할에서 하나의 분할에는 하나의 프로세스만 오기에 내부 단편화가 일어나면 그 메모리는 프로세스가 종료하기 전까지 쓸 수 없게 된다.




가변 분할 방법


가변 분할 방법은 고정된 경계를 없애고 각 프로세스가 필요한 만큼 메모리를 할당하는 방법이다.


이때는 기준 레지스터와 경계 레지스터를 사용하여 각 프로세스의 분할 영역을 나타낼 수 있게되고 프로세서로 생성한 논리적 주소가 경계 레지스터 값 보다 커지면 오류가 발생한다.


그리고 고정 분할 방법과 달리 가변 분할 방법을 이용하면 프로세스가 계속해서 적재되고 빠져나감에 따라 외부 단편화가 생기게 된다.


가변 분할 방법에서는 프로세스를 어디 메모리 부분에 적재해야 할 지 고민해야한다.


그에따라 최초 적합 방법, 최적 적합 방법, 최악 적합 방법이 존재한다.


최초 적합 방법


최초 적합 방법은 메모리를 탐색하다가 프로세스가 들어갈 수 있는 첫번째 공간에 할당하는 것이다.


장점은 빠르게 프로세스를 적재 할 수 있지만, 단점은 공간 활용률이 떨어지게 된다는 것이다.


최적 적합 방법


최적 적합 방법은 메모리 공간 중 프로세스가 들어 갈 수 있는 가장 작은 공간에 할당하는 방법이다.


사용 가능 공간이 크기 순으로 정렬되어 있지 않으면 전체를 검색해야하고 정렬을 계속해야하는 단점이 있지만 사용 공간 이용률은 좋아지는 장점이 있다.


최악 적합 방법


최악 적합 방법은 프로세스를 메모리 공간 중 가장 큰 곳에 배치한다.

이때도 공간을 크기 순으로 정렬해야한다는 단점이 있지만 가장 큰 공간에 메모리를 할당하기에 가장 큰 공간을 사용하기에 가장 작은 공간을 만들어 사용하는 것 보다는 유리하다.


참고 :: https://zetawiki.com/wiki/%EB%A9%94%EB%AA%A8%EB%A6%AC%EB%B0%B0%EC%B9%98%EA%B8%B0%EB%B2%95,_%EC%B5%9C%EC%B4%88%EC%A0%81%ED%95%A9,_%EC%B5%9C%EC%A0%81%EC%A0%81%ED%95%A9,_%EC%B5%9C%EC%95%85%EC%A0%81%ED%95%A9



버디 시스템


버디 시스템은 고정 분할 방법이나 동적 분할 방법의 문제점을 개선하기 위해 나타났다.



버디 시스템은 프로세스의 크기에 맞게 메모리를 계속해서 2로 나누어 작은 버퍼를 만들어가며 프로세스의 크기에 최적화 되도록 만들어준다.


즉, 64KB 메모리에서 프로세스가 4KB를 요구한다면 메모리는 32,32->16,16->8,8->4,4로 나누어 총 32,16,8,4,4짜리로 변하게되어 4KB를 할당해준다. 이처럼 버디 시스템은 프로세스 크기보다 크거나 같을 때까지 계속해서 2개로 분할되는 특성을 가지게 된다.

하지만, 5KB같은 프로세스가 온다면 8KB를 할당해줘야 하기에 여전히 내부 단편화는 이뤄 질 수 밖에 없다. 




짚고 넘어가기

1. Data 영역 메모리에는 전역변수 그리고 static(정적) 변수가 들어간다.
2. Stack 영역 메모리는 힙영역과 만나게되면 Stack Overflow가 발생한다.
3. Context Switching이 일어나면 메모리 단편화가 일어납니다. 이를 해결하기 위해 페이징 기법이 나왔습니다.(그래도 단편화는 일어난다)
4. 아래 문서를 보면 알 수 있지만 힙보다 스택이 훨씬 더 할당 속도가 빠르다.
하지만 힙에서 메모리 풀 이용하면 엄청빠르다는데 이 부분에 대한 더 자세한 이야기는 찾아볼 수 없었다.(스택 vs 힙에 관해)
https://doc.rust-lang.org/book/first-edition/the-stack-and-the-heap.html
The key thing to understand here is that stack allocation is very, very fast.

5. 캐시 메모리 평균 접근시간 계산하기
캐시 메모리 평균 접근시간 = 캐시 HIT비율 * 캐시접근시간 + 캐시 MISS비율 * 주기억장치 접근 시간 

만약 CPU에서 캐시 접근 시간이 1이고 메모리 접근시간이 100이며 캐시 히트율이 90%라면?

캐시 접근시간 : 캐시 접근 시간(1) * 캐시 HIT비율(0.9) + 메모리 접근 시간(100) * 캐시 MISS비율(0.1) = 0.9 + 10 = 10.9








반응형