https://core.ewha.ac.kr/publicview/C0101020140425151219100144?vmode=f
이제 메모리 관리 부분에 들어간다.
메모리라는 것은 주소로 접근하는 매체이다
그래서 메모리에는 주소가 매겨진다.
주소에는 두가지 종류가 있다.
1. 논리적 주소(가상주소)
프로그램마다 가지고있는 메모리 주소
각 프로세스가 시작할때 0번지부터 시작하는 가상주소를 가지고 있다.
2. 물리적 주소
실제 물리적인 메모리의 주소
메모리에 프로그램이 어디에 올라가는 가이다.
-> 물리적인 메모리는 0번지부터 (하나로 관리된다)
물리적인 메모리 아래부분에는 운영체제 커널이 들어가있고
상위 주소에는 여러 프로그램들이 섞여서 올라가있다.
프로그램마다 0번지 부터 시작하는 독자적인 주소가 있지만 이것이 실행되려면 물리적인 메모리 어딘가에 올라가야하고 그럼 주소가 바뀌게됨 -> 주소 변환(주소 바인딩) 이라고함
그럼 주소 변환이 언제 일어나는가?
변환방법이 3가지가 있다고 한다.
symbolic address은 무엇인가? -> 프로그래머가 함수 이름 혹은 변수 이런것으로 주소를 표시한다 그것을 뜻한다.
컴파일시 논리적 주소로 변환되는것이다.
주소변환은 크게 3가지 시점에서 일어난다.
1. 컴파일시 이루어지는 방법(컴파일 타임 바인딩)
2. 실행이 시작될때 (로드 타임 바인딩)
3. 실행중에 바뀌는것(런타임 바인딩)
언제 어떻게 주소 바인딩이 이루어지는것은 실제 예시를 보면서 알아보자
처음에 소스코드가 있다. 그때는 symbolic address로 작성되어있다.
다음 이제 컴파일을 통해서 실행파일로 변환되면 논리적 주소로 바뀌게된다(숫자로 된 주소)(0번지 부터 순차적으로)
이제 실행이되려면 물리적인 메모리에 올라가야하고 물리적인 메모리에 주소가 결정되는것을 주소 바인딩이라고 부른다.
1. 컴파일타임 바인딩
컴파일 시점에 이미 물리적 메모리 주소가 결정되는것
컴파일 타임 바인딩을 쓴다면 실제 가상 메모리 주소 그대로 물리적 메모리에도 올리게 된다.
즉 컴파일 시점에서 앞으로 쓸 메모리 주소 그대로 논리적 메모리를 가져가는것이다. -> 이렇게 결정된 코드를 absolute code(절대코드)라고 부름
주소 다른데 비어있어도 무조건 정해진 주소에만 올라가야함
매우 비효율적이다. -> 근래의 환경에서는 사용되지 않는다 .
과거에 프로그램 하나만 돌아가는 환경일때 사용되곤함
메모리에 올라가고 싶은 위치를 바꿀것이라면 컴파일을 다시해야함
2. 로드 타임 바인딩
프로그램이 시작되어서 메모리에 올라갈때 물리적인 메모리 주소가 결정되는것이다.
실행시 비어있는 메모리에 매칭해서 올리는것이다.
컴파일러가 재배치가능 코드로 생성한다.
3.런타임 바인딩
로드타임 바인딩 + 실행도중에 주소값이 바뀔수 있다는것이다.
실행도중에 메모리 값들이 옮겨다닐수있다. 메모리 주소상에서
요즘의 운영체제는 런타임 바인딩을 지원한다.
cpu가 어떤 메모리 주소를 요청할때 마다 바인딩을 체크해봐야한다.
-> 하드웨어 적인 지원이 필요하다(MMU 라는 하드웨어를 통해 지원)
cpu 는 logical address 을 취급한다.
컴파일해서 실행파일을 만들더라도 실행파일 내에 코드는 논리적 주소로 적혀있다 그것을 변환해서 메모리에 올려놓는다 한들 코드자체는 논리적 주소로 이루어져있기 때문에 cpu 는 논리적주소에 맞춰져있는 값들을 메모리에서 읽어오려한다(이런상황에서 번지수를 바꾸려면 재컴파일을 해야하는것임).
그래서 cpu가 메모리 몇번지의 내용을 달라고 요청을하면(논리적주소) 주소 변환을해서 물리적인 메모리위치를 찾은다음에 그내용을 cpu에 전달해야하는것이다.
컴파일 타임 바인딩과 로드타임 바인딩은 메모리 변환이 별거없다 애초에 컴파일 타임 바인딩은 주소변환이 필요없고 로드타임 바인딩도 그냥 일정 숫자를 더해주는것 뿐이다.
근데 런타임 바인딩은 중간에 바뀔수 있으니 그때그때 마다 내용들이 어디올라가있는지 주소변환을 새로해야한다.
MMU는 주소변환을 지원하는 하드웨어적인 요소이다.
기본적인 MMU 에서는 레지스터 두개를 이용해서 주소변환을 하게된다.
사용자 프로그램은 logical address 만 다룬다 실제 메모리 주소를 볼수도 없고 볼필요도 없다.
cpu에서 logical address 어느거를 들고와서 가지고 오라고 요청한다.
가장 기본적인 MMU는 relocation register(base register)와 limit register 를 이용해서 주소 변환을 한다.
이제 그림을 보자면
왼쪽 아래에 버츄얼 메모리로 0번지부터 3000번지까지 프로세스 p1이 존재한다
그상황에서 cpu가 346 logical address 를 요구한것이다.
프로세스 p1의 가상메모리에서 0번지로부터 346번째 떨어져있는 메모리를 요청한 것이다.
그리고 현재 물리적 메모리는 0번지가 14000번지와 매칭이된다.
이런경우 물리적 메모리에 올라가져있는 시작위치랑 논리적 위치를 더해서 cpu에게 전해주면 될것이다.
이런경우 base register 에다가 프로그램의 시작위치를 저장해놓는다(물리적 메모리의 시작위치)
주소변환할때는 논리주소에 시작위치를 더해서 물리적 주소를 얻게된다.
그리고 한가지 체크를 더 하게 되는데
limit register 를 통해서 프로그램의 크기를 담고있는데 이 프로그램이 접근할수 있는 메모리 범위를 제한하는것이다.
만약 이프로그램이 악의적인 프로그램이라 그프로그램의 범위를 벗어나는메모리를 요구할수도 있기 때문에 그것을 제한하는 용도로 본인의메모리 범위만 접근할수 있도록 제한 하는것이다.
그래서 cpu에서 요청했을때 메모리를 가져오는 과정을 나타낸 자료를 봐보자
우선 최초에 요구할때 limit register 를 통해서 정상적인 접인지부터 확인한다.
만약 여기서 비정상적인 접근이라면 trap 이 걸린다.
trap이 걸리면 cpu 권한이 운영체제에 넘어가고 trap이 왜 걸렸는지 따져보고 악의적인 프로그램이라면 응징힌다.
그다음 올바른 접근이였다면 base register 를 통해서 변환해서 메모리에서 꺼내다가 cpu에 전해준다.
이제 용어 설명부터 해볼것이다.
1. dynamic loading
다이나믹이 동적이라는 뜻이다. 로딩은 메모리에 올린다는 뜻이다.
즉 프로그램을 동적으로 메모리에 올린다는 뜻이다.
동적으로 올린다는것은 메모리에 필요할때마다 그때그때 올린다는것이다. -> 해당 루틴이 호출될때마다 올린다는것
메모리를 통으로 올리고 이런것이 아님
애초에 프로그램은 방어적으로 만들어지는 경우가 많기 때문에 거의 불리지않는 오류처리 루틴같은것들이 많다.
-> 이런코드들은 실행이되지않을 경우가 많으므로 메모리에 안올라가는것이 효율적이다.
근데 dynamic loading 은 운영체제가 관여하는것이 아니라 프로그래머가 직접 구현하는것이다.
기존에 배웠던 메모리를 일부분올리고 내리고 하는 운영체제가 관리하는것과는 다른개념이다 그러한 페이징은 시스템에 의해서 일어나는것이다.
근데 그럼 프로그래머가 언제 메모리에 어떻게 올라가는지 프로그래머가 일일히 관리하는가? -> 아니다 그것은 os가 지원하는 라이브러리를 통해 관리하는경우가 많다.
어쩃든 페이징 기법과는 다른것이다.
dynamic loading 과 overlays 랑 뭐가다른가? 필요한 부분만 올린다는것은 같은것 아닌가?
역사적으로 시작점이 다르다
overlay는 초창기 컴퓨터 시스템에서 메모리크기가 워낙 작아서 프로그램 하나를 메모리에 올려서 실행시킬수가 없었음
그래서 프로그래머가 프로그램을 쪼개서 일부분만 실핼되도록 프로그래머가 수작업으로 만드는것이였다. -> 매우 복잡하고 힘듬(운영체제의 지원이없다)
swapping은 프로세스를 메모리에서 통째로 쫓아내는것이다(하드디스크로)
이제 쫓아내지는 장소(하드디스크를) backing store 라고 부른다.
메모리에서 통째로 쫓겨나서 하드디스크로 내려가는것을 swap out 이라고 부르고
메모리로 다시올라오는것을 swap in 이라고 부른다.
swapping 은 중기스케줄러가 뭘 swap out 시킬지 결정한다 그래서 swapper라고도 부른다.
메모리에 너무많은 프로그램이 올라와있으면 시스템이 굉장히 비효율적이기 때문에 중기스케쥴러가 일부 프로그램을 골라서 하드디스크로 쫓아내는것이다.
중기 스케쥴러는 어떤 프로세스를 쫓아내는가?
cpu 우선순위가 낮은 프로그램을 쫓아낸다.
스와핑 시스템이 적용되려면 바인딩 시점과 연관되어서 생각해야한다
만약 컴파일 타임 바인딩이나 로드타임 바인딩이라면 쫓겨났다가 다시 올라올 때 무조건 원래 메모리 주소로 올라와야 할 것이다.
-> 스와핑이 효율적이기 힘들다.
스와핑이 효율적으로 적용되려면 런타임 바인딩이 적용되어야한다.
스와핑 같은경우에는 방대한 양의 메모리를 올리고 내리는것이기 때문에(프로그램을 올리고 내리는것이니) -> 걸리는 시간이 swap 되는 양에 비례하는 transfer time 이다(실제적으로 데이터를 올리고 내리는 시간이 많이 걸린다.)
파일 입출력같은경우에는 디스크헤드가 움직이는 시간이 주를 차지하고 사실상 transfer time 은 아주 조금이다.
링킹: 프로그램을 작성후 컴파일후 링크해서 실행파일을 만든다 -> 여러군데 나뉘어진 컴파일된 파일들을 묶어서 실행파일을 만드는 과정이다. 라이브러리 같은것 내가 애초에 코드 파일 나뉘어져있지만 실행파일 코드에 포함되는것(포함시키는게 링킹)
-static linking
프로그램 실행파일 코드에 애초에 시작할때부터 링킹되어있음
-dynamic linking
라이브러리 실행시 linking 됨
실행파일에 stub라는 라이브러리 코드를 찾기위한 작은 포인터값 코드만 두고 나머지는 포함하지 않음
그래서 라이브러리 호출에 이르면 그 라이브러리가 어디있는지 찾는다. -> 별도에 파일형태로 존재하는것을 찾음
메모리에 이미 올라와있으면 그냥 사용하고 아니면 디스크에서 읽어온다.
이것을 예시를 들어보면
print 가 들어있는 프로그램을 100명이 짜서 그 100개를 정적 링킹으로 돌리면 print 코드 내용 100개가 중복되게 메모리에 올라와있을것이다.
이것을 정적링킹으로 돌리면 print코드 내용은 하드에 그대로 있고 호출될때만 메모리에 올라오고 다른 프로그램에서도 일단 메모리위에 같은것이 있는지 찾고난후 없으면 디스크로 찾으러가고 있다면 그냥 그것을 사용하게된다.
다이나믹 링킹을 해주는 라이브러리를 shared library 라고 부른다. -> 운영체제의 도움이 필요하다.
용어설명 끝
이제 물리적인 메모리를 어떻게 관리할것인가를 살펴볼것이다.
위의 그림이 물리적인 메모리를 보여주는 그림이다.
물리적인 메모리의 낮은주소 영역은 항상 운영체제 커널이 상주하고 있고
높은주소 영역은 사용자 프로그램들이 올라가게 된다.
사용자 프로그램이 올라가는 영역을 관리하는 방법으로 크게 두가지가 있는데
1.contiguous allocation (연속할당)
프로그램이 메모리에 올라갈때 한방에 통으로 올라가는 방법
2.noncontiguous allocation(불연속 할당)
프로그램을 구성하는 주소공간을 잘게 쪼개서 일부분씩 올라갈수 있게 하는방법
현대의 시스템은 당연히 불연속 할당을 사용하고있고 페이징 기법역시 사용하고있다.
연속할당은 두가지 방법으로 나눠 볼수있다.
-고정분할 방식
프로그램이 들어갈 사용자 프로그램 영역을 미리 파티션으로 나눠놓는것임 (프로그램이 안들어가도 이미 다 나눠져있다.
사용자 프로그램이 들어갈 공간을 애초에 나눠져있는 것이다.
프로그램 B는 크기가 분할 2에는 들어갈수없어 3에 들어간다.
그럼 낭비되는 공간이 2가지 나오는데 외부조각,내부조각이라는용어로 나눠서 이야기한다.
-가변분할 방식
사용자 프로그램이 들어갈 영역을 미리 나눠놓는것이 아니다.
프로그램이 실행될 때마다 차곡차곡 메모리에 올려놓는 방식이다.
프로그램 이 중간에 끝났는데 거기를 어느 프로그램이 차지하지 않게되면 외부조각이 되는것이다.
(프로그램의 크기들이 균일하지 않기 때문에 프로그램이 실행되고 끝나고 하는 과정에서 외부조각이 만들어지게된다.)
-> 가변분할의 경우 내부조각은 없다.
외부 조각: 올리려는 프로그램보다 메모리 공간의 크기가 작아서 사용이 안된것을 외부 조각이라 한다.
내부 조각: 프로그램이 할당했는데 그 크기보다 분할의 크기가 커서 남는부분
가변분할 방식을 사용하게되면 Hole 라는 가용 메모리 공간이 생긴다.
이러한 hole 들은 산발적으로 여러공간에 흩어져서 만들어지게된다.
운영체제는 물리적인 메모리에서 어느 부분이 할당되었는지 어느부분이 hole 인지 관리하고있는다.
이제 프로그램이 실행될 때 hole 가 여러군데가 있을텐데 어느 hole 에다가 프로그램을 집어넣을것인가? 라는 문제가 있다.
-> Dynamic storage allocation problem
가변분할 방식에서 가장 적정한 hole 을 찾는문제
-first fit
그냥 크기가되는 hole 중 가장 첫번째 hole 에 집어넣는것이다.
-best fit
hole 의 크기를 다 살펴본후 가장 잘맞는 hole 를 배정한다.
크기가 n과 가장 근접한 hole에 넣는다.
hole 를 분석하는 시간이 걸린다.
-worst fit
가장큰 hole 에 할당한다.
-> 모든 hole 또 찾아야하고
상대적으로 아주큰 hole 들이 생성되게됨
안좋은 방법임
실험적으로 속도나 공간사용율 측면에서 first fit 과 best fit이 효율적이다.
프로그램들을 쭉 밀어서 재배치해서 hole들을 모아서 큰hole 로 만드는 방법을 compaction이라고 부름 -> 디스크 조각모음의 메모리 버전이라고 생각하면된다.
근데 이것 매우 비용이 많이드는 작업이다 바인딩을 다 일일히 점검해야하는등 무거운 작업이다.
아무때나 할수있는것도 아니고 run time binding이 지원될때만 할수있는 방법이다.
전체적으로 미는것이아니라 특정 프로그램만 옮겨서 효율적으로 compaction을 할수있는데 그것을 결정하는것 조차 간단한 문제가 아ㅣ니다.
이제 불연속 할당에대해 다룰것인데
크게 페이징 기법과 세그먼테이션 방식으로 나눌수 있다.
paging 기법은 프로그램을 구성하는 주소공간을 같은 크기의 페이지로 잘라서 페이지단위로 물리적인 메모리에 올려놓거나 backing store에 내려놓거나 하는방법이다.
로지컬 주소공간을 페이지 단위로 잘라서 올리고 내리고를 하듯이 물리적 메모리 또한 미리미리 다 페이지 크기 단위로 잘라놓을것이다.
이렇게 미리 잘라놓는것을 page frame(물리적인 메모리의 공간) 라고 부른다.
이렇게 페이징 방법을 사용하면 hole 들의 크기가 균일하지 않아 어디에다가 넣어야하는지 계산해야하는것, compaction(hole들 합치는것) 이런것들이 필요가 없다.
이런 불연속 할당을 사용하게 되면 주소 할당(바인딩)이 복잡해짐
MMU가 복잡해질것이다 -> 주소변환을 페이지 단위로 해야할것이다.
segmentation 기법은 프로그램의 주소공간을 의미있는 단위로 자르는것이다.
프로그램은 code,data,stack 로 나뉘어있으니
코드 세그먼트,데이터 세그먼트, 스택 세그먼트 로 나눠서 각각의 세그먼트를 필요시에 물리적메모리에 다른위치에 올려놓을수 있게하는 방법이다.
각각의 함수단위로 segmentation을 나눌수도 있는것이다.
segmentation 은 의미 단위이기 때문에 크기가 균일하지는 않다.
-> 크기가 다르기 때문에 적절한 hole 찾는 문제같은것들이 동일하게 일어난다.
어쩃든 결론적으로 보자면 잘라서 여기저기다 흩뿌려서 올릴수 있다는것이다.
페이징 기법
페이징의 기본개념은 프로세스를 구성하는 주소공간을 동일한 크기의 페이지로 나누고
페이지는 일부는 백킹스토어에 있고 일부는 메모리에 올라가있는데
페이지 단위로 불연속적으로 메모리에 올라가게 됨
물리적 메모리 공간을 나눠놓은 것을 frame 이라고 부르고
논리적 메모리 공간을 자른것을 page 라고 부른다.
운영체제는 비어있는 frame을 관리하게되고
주소변환을 page 단위로 이뤄져야하므로 레지스터 두개만으로는 불가능하고 page table 이라는것을 써서 주소변환을 하게됨
page table 은 프로그램을 구성하는 주소공간의 페이지가 물리적 메모리 어디에 올라가있는지 page table에다가 표시를 해놓는 일종의 배열이다.
외부조각은 발생하지않는다. -> 같은 크기로 잘라놓았으니
내부조각은 발생할수있다. -> 페이지의 크기의 배수로만 프로그램이 짜져있지않으니 마지막 자른부분은 page 단위보다 적을수있다.
'cs > 운영체제' 카테고리의 다른 글
20강 Memory Management 3 (1) | 2023.02.01 |
---|---|
19 강 Memory Management 2 (1) | 2023.02.01 |
17강 Deadlocks 2 (0) | 2023.01.24 |
16강 Deadlocks 1 (0) | 2023.01.24 |
15강 Process Synchronization 4(Concurrency Control) (0) | 2023.01.24 |