728x90
운영체제
17. 물리 메모리 크기의 극복 : 메커니즘
- 스왑 공간이 추가되면 운영체제는 실행되는 각 프로세스들에게 큰 가상 메모리가 있는 것 같은 환상을 줄 수 있다. 멀티 프로그래밍 시스템 ( 컴퓨터의 사용률을 높이기 위해 ‘동시에’ 여러 프로그램들을 실행시키는 시스템)이 발명되면서 많은 프로세스들의 페이지를 물리 메모리에 전부 저장하는 것이 불가능하게 되었다. 그래서 일부 페이지들을 스왑아웃 하는 기능이 필요하게 되었다. 멀티 프로그래밍과 사용 편의성등의 이유로 실제 물리 메모리보다 많은 용량의 메모리가 필요하게 되었다. 이것이 현대 Virtual Memory의 역할이다.
17.1 스왑공간
- 가장 먼저 할 일은 디스크에 페이지들을 저장할 수 있는 일정 공간을 확보하는 것이다. 이 용도의 공간을 스왑공간(Swap page)이라고 한다. 스왑공간이라 불리는 이유는 메모리 페이지를 읽어서 이곳에 쓰고(swap out), 여기에 페이지를 읽어 메모리에 탑재 시키기(swap in) 때문이다. 스왑공간의 입출력 단위는 페이지라고 가정한다. 운영체제는 스왑공간에 있는 모든 페이지들의 디스크 주소를 기억해야 한다.
- 스왑공간의 크기는 매우 중요하다. 시스템이 사용할 수 있는 메모리의 최대 수를 결정하기 때문이다. 스왑공간을 이용하면, 시스템에 실제 물리적으로 존재하는 메모리 공간보다 더 많은 공간이 존재하는 것 처럼 가장할 수 있다.
- 스왑공간에만 스왑을 할 수 있는 것은 아니다. 예를들어 프로그램이 실행한다고 할 때, 이 실행 프로그램의 페이지들은 디스크에 존재한다. 프로그램 실행되면 각 페이지들은 메모리로 탑재된다.(프로그램 실행시 한번에 모두 탑재되거나 또는 필요시 한 페이지씩 탑재 할 수 있다.) 물리 메모리에 추가 공간을 확보해야 할 때, 코드 영역의 페이지들이 차지하는 물리 페이지는 즉시 다른 페이지가 사용할 수 있다. 코드가 저장되어 있는 파일 시스템 영역이 스왑목적으로 사용되는 셈이다. 해당 페이지들은 디스크에 원본이 있으므로, 언제든지 다시 스왑 - 인이 가능하기 때문이다.
17.2 Present Bit
- 하드웨어는 present bit를 사용하여 각 페이지 테이블 항목에 어떤 페이지가 존재하는지 표현한다. present bit가 1로 설정되어 있다면, 물리 메모리에 해당 페이지가 존재한다는 것이다. 만약 그 비트가 0으로 설정되어 있다면 메모리에 해당 페이지가 존재하지 않고, 디스크 어딘가에 존재한다는 것을 나타낸다. 물리 메모리에 존재하지 않는 페이지를 접근하는 행위를 페이지 폴트(page fault)라고 한다.
- 페이지 폴트가 발생하면, 페이지 폴트를 처리하기 위해 운영체제로 제어권이 넘어간다. 페이지 폴트 핸들러(page fault handler)가 실행된다.
17.3 Page Fault
- TLB 미스의 처리 방법에 따라, 두 종류의 시스템이 있다. 하드웨어 기반과 소프트웨어 기반이다. 둘 중 어느것이던, 페이지 폴트가 발생하면 운영체제가 그 처리를 담당한다.
- 페이지 폴트 발생 시, 운영체제는 페이지 테이블 항목에서 해당 페이지의 디스크 상 위치를 파악하여, 메모리로 탑재한다.
- 디스크 I/O가 완료되면 운영체제는 해당 페이지 테이블 항목의 PFN값을 탑재된 페이지의 메모리 위치로 갱신한다. 이 작업이 완료되면 페이지 폴트를 발생시킨 명령어가 재실행된다. 재실행으로 인해 TLB 미스가 발생될 수 있다. TLB 미스 처리 과정에서 TLB값이 갱신된다. 최종적으로, 마지막 실행시에 TLB에서 주소 변환 정보를 찾게되고, 이를 이용하여 물리주소에서 원하는 데이터나 명령어를 가져온다.
- I/O 전송 중에는 해당 프로세스가 차단된(blocked) 상태가 된다. 페이지 폴트 처리 시 운영체제는 다른 프로세스들을 실행 할 수 있다. I/O 실행은 매우 시간이 많이 소요되기 때문에 한 프로세스의 I/O 작업과 다른 프로세스의 실행을 중첩(Overlap)시키는 것은 멀티 프로그램된 시스템에서 하드웨어를 최대한 효율적으로 사용하는 방법 중 하나다.
17.4 메모리에 빈 공간이 없으면?
- 메모리에 여유 공간이 없다면 탑재하고자 하는 새로운 페이지를 위한 공간을 확보하기 위해 하나 또는 그 이상의 페이지들을 먼저 페이지 아웃(page out) 하려고 할 수도 있다. 교체(replace) 페이지를 선택하는 것은 페이지 교체정책(page-replacement policy)이라고 한다.
17.5 페이지 폴트의 처리
- 하드웨어 처리 과정에서 TLB 미스 발생 시, 세 가지의 중요한 경우가 있다.
- 첫째, 페이지가 존재하며 유효한 경우이다. 이 경우 TLB 미스 핸들러가 PTE에서 PFN을 가져와서 명령어를 재시도 한다.
- 두 번째, 페이지가 유효하지만 존재하지 않는 경우다. 페이지 폴트 핸들러가 반드시 실행되어야 한다. 프로세스가 사용 할 수 있는 제대로 된 페이지이기는 하지만 물리 메모리에 존재하지 않기 때문이다.
- 세 번째, 페이지가 유효하지 않은 경우다. 프로그램 버그등으로 잘못된 주소를 접근하는 경우의 처리를 나타낸다. 이 경우에는 PTE의 다른 비트는 의미가 없다. 하드웨어는 이 무효한 접근이 운영체제 트랩 핸들러에 의해 처리되도록 한다. 이떄 문제를 일으킨 프로세스는 종료될 수 있다.
17.6 교체는 실제 언제 일어나는가
- 메모리의 항상 어느 정도의 여유공간을 비워두기 위해서, 대부분의 운영체제들은 여유공간에 관련된 최댓값(HW : high watermark) 과 최솟값(LW : low watermark)를 설정하여 교체 알고리즘 작동에 활용한다. 동작방식은 다음과 같다. 운영체제가 여유공간의 크기 최솟값보다 작아지면 여유공간 확보를 담당하는 백그라운드 쓰레드가 실행된다. 이 쓰레드는 여유공간의 크기가 최댓값에 이를 때 까지 페이지를 제거한다. 이 백그라운드 쓰레드는 일반적으로 스왑데몬(swap daemon) 또는 페이지 데몬(page daemon)이라고 한다. 충분한 여유 메모리가 확보되면 이 백그라운드 쓰레드는 슬립모드로 들어간다.
PintOS
Argument Passing
- 프로그램에 대한 인자를 성정해야 한다.
“/bin/ls -l foo bar” 인자가 들어왔을 때
- 명령어를 단어로 분할한다. : /bin/ls, -l, foo, bar
- 각 문자열과 널 포인터를 스택에 오른쪽에서 왼쪽으로 순서대로 푸시한다.
- argv[0]를 가장 낮은 가상 주소에 위치시킨다.
- 8의 배수로 내림 정렬한다.
- %rsi를 argv로 지정하고 %rdi를 argc로 설정한다.
- fake return address를 푸시한다.
- 다른 함수들과 동일한 스택 프레임 구조를 같기 때문이다.
Process_exec()
- command line 제한이 128바이트 이므로 parse 배열 길이는 65로 지정했다.
- strtok_r 함수를 활용해 parsing한 결과를 담고, argument_stack 함수를 호출할 때 전달한다.
- 인자의 개수와 argv 시작 주소를 각각 rdi와, rsi에 저장한다.
- fake return address에서 8만큼 더한 값이 rsi에 들어간다.
int
process_exec (void *f_name) {
char *file_name = f_name;
bool success;
/* We cannot use the intr_frame in the thread structure.
* This is because when current thread rescheduled,
* it stores the execution information to the member.
* 스레드 구조에서 intr_frame을 사용할 수 없습니다.
* 현재 스레드가 다시 예약되면 실행 정보를 회원에게 저장하기 때문입니다.
*/
struct intr_frame _if;
_if.ds = _if.es = _if.ss = SEL_UDSEG;
_if.cs = SEL_UCSEG;
_if.eflags = FLAG_IF | FLAG_MBS;
/* We first kill the current context */
process_cleanup ();
char *box[64]; //parsing 한 문자열들이 들어갈 box
char *token, *save_ptr;
int num = 0;
for (token = strtok_r (file_name, " ", &save_ptr); token != NULL; token = strtok_r (NULL, " ", &save_ptr))
{
box[i] = token;
num++;
}
/* And then load the binary */
success = load (file_name, &_if); // 디스크에서 메모리로 로드 userstack 포인터를 초기화한다.
argument_stack(box , num ,&_if.rsp); // parsing한 데이터를 스택에 넣어주는 함수
_if.R.rdi = num;
_if.R.rsi = (char*)_if.rsp + 8;
hex_dump(_if.rsp, _if.rsp, USER_STACK-(uint64_t)_if.rsp, true);
/* If load failed, quit. */
palloc_free_page (file_name);
if (!success)
return -1;
/* Start switched process. */
do_iret (&_if);
NOT_REACHED ();
}
Argument_stack()
- 프로그램의 이름과, 인자 문자열을 넣어준다.
- 스택은 아래 방향으로 커지므로 스택에 인자를 추가할 때 오른쪽에서 왼쪽 방향으로 넣어준다.
- 정렬 패딩을 넣어준다.
- 각 문자열을 넣고 8byte로 맞춰주기 위해 패딩을 넣아준다.
- 인자 문자열 종료를 나타내는 0을 넣어준다.
- 각 인자 문자열의 주소를 넣어준다.
- fake return address를 넣어준다.
728x90
'Study > TIL(Today I Learned)' 카테고리의 다른 글
24.03.16 운영체제, PintOS (1) | 2024.03.16 |
---|---|
24.03.15 운영체제, PintOS (1) | 2024.03.16 |
24.03.13 운영체제 (1) | 2024.03.14 |
24.03.12 운영체제, CS, PintOS (3) | 2024.03.12 |
24.03.11 운영체제, KEYWORD (2) | 2024.03.11 |