하나의 컴퓨터 시스템을 여러 사용자가 사용하기 때문에, 각 프로세스들이 사용하는 라이브러리 함수들을 프로세스들이 공유하는 것이 더 합리적이다!
-> 페이지를 프로세스간에 공유할 수 있는 기법이 필요하다.
코드가 들어있는 페이지
-> Pure Code여야 한다. (= reentrant code: 재진입성 코드)
한 프로세스가 특정 코드를 실행하면서 그 코드 자체를 변경시키는 경우가 있다. 이런 코드를 'Impure Code'라고 한다.
프로세스가 특정 코드를 실행하면서 그 코드 자체를 변경시키지 않고, 있는 그대로 실행만 하는 경우의 코드.
데이터가 있는 코드
어떠한 제한 없이 프로세스 간 공유가 가능하다.
write가 가능한 데이터라면 변경이 가능한 데이터이기 때문에, concurrency control 메커니즘(synchronization, mutual exclusion)을 가진 상태에서 공유해야 한다.
메모리의 페이지 프레임 p'에 데이터 페이지가 있다고 하자. 이 p'를 P1과 P2가 모두 공유하는 상황이라고 생각해보자.
p1은 n1개의 페이지를, p2는 n2개의 페이지를 갖고 각자의 PMT를 갖고 있는 상태이다.
이 경우 data page는 두 프로세스에게 공유되고 있는 상황이다.
코드로 이뤄진 page frame p'을 p1과 p2가 공유하려고 한다고 생각해보자.
각자 (k1, d)와 (k2, d)의 virtual address 를 generation 하면 residence bit = 1이므로, page frame number로 (p', d)의 real address가 되어서 접근할 것이다.
그러나 이것은 공유 페이지이기 때문에 p1이던 p2이던 문제가 생기게 된다.
따라서 같은 페이지 번호인 'k'에서 해당 페이지 프레임 p'을 공유해야 한다.
그렇게 된다면 Branch를 (k, d)로 할 수 있고 두 프로세스 모두 문제 없이 p'의 코드를 실행할 수 있다.
- 명령어를 Fetch한 후, 해독한다.
- 명령어 A를 fetch한다.
- 명령어 B를 fetch한다.
- A와 B를 더한다.
- C에 저장한다.
A번지와 B번지가 모두 Address Mapping을 했는데, 해당 페이지가 메모리 안에 있어서(residence bit = 1) fetch했다고 가정해보자.
중간에 이렇게 멈추게 되면, program counter는 이미 다음 명령어를 가리키기 때문에 현재 명령은 실행하지 못하고 건너 뛰는 결과가 되어버린다.
다시 프로세스가 실행되면 이 Instruction을 다시 실행하도록 'Exception Handling'을 해야 한다 !
1, 2, 3, 4번 사이에 레지스터값을 바꾸거나 하는 등의 변경사항은 원래대로 cancel 시키고, program counter의 값도 현재 명령어로 바꾸어놓는 일을 해야 한다.
➡ 따라서 Virtual Memory를 사용하는 컴퓨터 시스템은 모든 기계어 명령들을 Page Fault가 발생할 경우를 위해 Restartable Instruction으로 만들어야 한다!
✔ 평균 메모리 접근 시간(ma) : 100ns
✔ 평균 페이지 서비스 시간(pagingTime: page fault로 하드디스크에서 페이지 읽어서 메모리에 두는 시간) : 8ms
✔ Page Fault Rate : p (0 <= p <= 1)
✔ EAT(Effective Acces Time) :
(1-p) ma + p pagingTime = (1-p) 100 + p 8,000,000 = 100 + 7,999,900 * p
만약 p = 1/1000이라면 EAT = 8.1microseconds
8.1microseconds는 메모리 접근시간의 약 80배에 해당한다. ( = 80 * ma)
즉 프로세스의 실행 시간도 80배 느려지는 것이다.
virtual memory를 사용하지 않는 프로세스가 virtual memory를 사용하면 80배 느려지는 것이다.
만약 성능을 딱 10% 저하까지만 허용한다면,
EAT = 100 + 7,999,999 * p < 110
p < 1/800,000 이어야 한다.
즉 80만번에 한번만 page fault가 발생하도록 해야 virtual memory 시스템이 합리적으로 돌아갈 수 있다.
유닉스, 리눅스 운영체제에서 한 프로세스가 실행하다가 다른 프로세스를 실행시키는 시스템 콜이다. 즉, 부모 프로세스가 fork() 로 자식 프로세스를 생성한다.
자식 프로세스는 부모 프로세스의 모든 것을 물려받기 때문에 pid 빼고 모든 context가 동일하다. 즉, duplicate된 프로세스나 다름 없다.
만약 메모리에 parent가 page를 4가지 가지고 있었는데, fork 시스템 콜로 child process를 만들면, page 역시 4개 copy되어야 한다. 그런데.. 꼭 이렇게 copy해야 할까?!
어차피 똑같은 4개의 페이지를 쓰게 할거면 이미 있는 부모의 4개 페이지를 공유하도록 하자 !
위처럼 parent process의 PMT와 child process의 PMT가 PAGE를 공유한다. 이 때 child process와 parent process의 PMT에 해당 공유되는 페이지는 CoW로 마킹이 되어 있다.
그런데 만약 parent가 페이지를 변경하거나 update하게 되면, child process는 영향을 받으면 안 되므로 해당 페이지는 copy되어야 한다!