Open kangchanguk opened 7 months ago
운영체제는 이 명령어와 데이터 묶음을 읽고 실행하여 프로그램에 생명을 불어 넣는것!!
원하는 수 만큼의 프로세스를 동시에 실행할 수 있게 함
하나의 프로세스를 실행하고 얼마 후 중단시키고 다른 프로세스를 실행하는 작업을 반복하면서 실제 하나 또는 소수의 CPU로 여러 개의 가상 CPU가 존재하는 듯 작업
많은 개체들이 자원을 공유하는 방식
실행 중인 프로그램
프로세스를 간단하게 표현하기 위해서는 실행하는 동안 접근했거나 영향을 받은 자원의 목록을 작성
운영체제는 새로운 프로세스를 생성할 수 있는 방법을 제공
운영체제는 강제로 프로세스를 제거할 수 있는 인터페이스를 제공
때로는 어떤 프로세스의 실행 중지를 기다릴 필요가 있기 때문에 대기 인터페이스가 필요
프로세스의 일시 정지 등 각종 제어 기능 제공
프로세스 상태 정보를 제공하는 인터페이스 제공
어떻게 프로그램이 프로세스로 변형이 되는가??
프로그램은 디스크 또는 ssd에 실행파일 형식으로 존재, 코드와 데이터는 메모리에 저장
현대에는 코드나 데이터가 필요할 때 필요한 부분만 메모리에 탑재
unix 시스템의 프로세스 생성에 관해 논의 프로세스 생성을 위해 fork()와 exec() 시스템 콜을 사용
프로세스 생성을 위해 해당 시스템 콜을 제공
pid: 프로세스 실행이나 중단과 같이 특정 프로세스를 대상으로 작업을 해야할 경우 프로세스를 지칭하기 위해 사용
1) 실행파일의 이름과 약간의 인자가 주어지면 해당 실행 파일의 코드와 정적 데이터를 읽어 들여 현재 실행 중인 프로세스 코드 세그멘트와 정적 데이터 부분을 덮어 씀
2) 힙과 스택 등 다른 주소 공간들도 새로운 프로그램 실행을 위해 초기화 됨
3) argv 같은 인자를 전달하여 프로그램을 실행 ( 현재 실행 중인 프로그램을 다른 실행 중인 프로그램으로 대체)
cpu 가상화를 하기 위해서는 운영체제는 여러 작업들이 동시에 진행되는 것처럼 보이도록 물리적인 CPU를 공유
한 프로세스를 잠시동안 실행하고 다른 프로세스를 잠깐 실행하는 등 cpu 시간을 나눠 씀
제한적 직접 실행이라는 방법이 나옴!!
운영체제)
해당 방식을 사용할 경우 하기와 같은 문제를 일으킴
1) 프로그램을 직접 실행시키면 프로그램이, 운영체제가 원하지 않을 일을 하지 않는다는 것을 어떻게 보장할 것인가? 2) 프로세스 실행 시, 운영체제는 어떻게 프로그램의 실행을 중단하고 다른 프로세스로 전환시킬 수 있는가??
이러한 문제점들을 개선하는 과정에서 제한적이라는 개념이 도입됨
프로세스는 입출력 연산을 비론한 다른 제한된 연산을 수행해야 함
하드웨어는 두가지 실행모드를 제공하여 운영체제를 도움
사용자 모드: 응용프로그램은 하드웨어 자원에 대한 접근 권한이 제한되어 잇음
커널 모드: 운영체제가 컴퓨터 모든 자원에 대한 접근 권한을 가짐
사용자 프로세스에게 제한 작업 실행을 허용하기위해서 하드웨어는 사용자에게 시스템 콜을 제공
시스템 콜 수행을 위해서 trap, return-from-trap 명령어를 통해 사용자 모드를 변경함
커널은 부팅 시에 트랩 테이블(trap table) 만들어 시스템을 통제한다.
컴퓨터가 부트될 때 커널 모드에서 동작하여 특정 명령어로 하드웨어에게 트랩 핸들러(trap handler) 의 위치를 알려준다. 하드웨어는 이 위치를 저장하고 있다가 시스템 콜의 고유 번호를 통해 처리가 가능하다.
직접 실행의 두 번째 문제는 프로제스의 전환이다.
협조 방식: 시스템 콜 호출시 까지 대기
협조(cooperative) 방식은 각 사용자 프로세스가 비정상적인 행동은 하지 않을 것으로 가정
CPU를 장기간 사용하는 프로세스들이 다른 프로세스가 사용할 수 있도록 주기적으로 CPU 를 반납할 것이라 믿음
협조 방식을 사용하는 운영체제는 yield 시스템 콜로 다른 프로세스에세 CPU 를 할당할 수 있는 기회 제공
응용 프로그램이 비정상적인 행동을 하면 트랩이 일어나 CPU 획득하여 해당 프로세스 종료
협조 방식의 스케줄링 시스템은 근본적으로 수동
시스템 콜 호출을 기다리거나 불법적인 연산을 대기
프로세스가 무한 루프에 빠져서 시스템 콜을 호출할 수 없으면 문제 발생
비협조 방식: 운영체제가 제어권 확보
비협조 방식은 타이머 인터럽트(timer interrupt) 로 프로세스를 중단하고 인터럽트 핸들러(interrupt handler) 를 실행
인터럽트 핸들러는 운영체제의 일부로
인터럽트를 처리하는 과정에서 실행중인 프로세스를 중단하고 운영체제에게 CPU 제어권을 넘겨 필요한 작업을 수행
운영체제의 스케줄러(scheduler) 는 실행중인 프로세스를 계속 실행할 지, 전환할 지를 결정한다. 프로세스 전환을 결정하면 운영체제는 문맥 교환(context switch) 코드를 실행한다.
문맥 교환은 실행 중인 프로세스의 레지스터 값들을 커널 스택 같은 곳에 저장하고 실행될 프로세스의 레지스터 값을 복원하는 것이다.
인터럽트나 트랩을 처리하는 도중에 다른 인터럽트가 발생할 때 주의가 필요하다.
간단한 해결책으로 인터럽트를 처리하는 동안에는 인터럽트를 불능화 하는 것이다.
하지만 이러한 기법은 인터럽트를 장기화 불능화하는 경우 손실되는 인터럽트가 발생될 수 있어서 신중하게 사용해야 한다.
운영체제는 커널 내부의 자료 구조들이 락(lock) 으로 보호되기 때문에 내부에서 다수의 작업들이 동시에 진행되는 것이 가능하다.
하지만 구성과 작동이 매우 복잡해지고 이 때문에 문제점과 버그들이 발생한다.
스케줄링 정책 개발을 위해서는 하기의 의문점들을 정의해야한다.
일정 시간 실행한 후 실행 큐의 다음 작업으로 전환하는 방법
타임 슬라이스 or 퀀텀이라고 부름
타임 슬라이스의 길이는 타이머 인터럽트 주기의 배수여야함(타이머가 10msec 마다 인터럽트를 발생시키면 타임 슬라이스는 10, 20 등의 배수가 되어야함)
타임 슬라이스 길이에 따라 라운드 로빈 성능이 결정됨(응답 시간 기준)
그러나 너무 짧으면 context 스위칭 비용때문에 전체 성능이 떨어짐
context 스위칭 비용과 응답 시간 모두 고려해야함!!
반환시간을 기준으로 해서는 효율이 낮아짐.....
mifq는 주어진 프로세스가 짧은 작업이라고 가정하여 높은 우선순위를 부여하고 짧은 작업이 아니라면 천천히 우선순위를 떨어뜨린다.
1) 기아상태 발생: 시스템에 너무 많은 대화형 작업이 존재하면 모든 CPU 시간을 소모하게 될 것 이고 긴 실행 시간 작업은 CPU 시간을 할당 받지 못함
2) 자신에게 유리하도록 프로그램 작성이 가능: 타임슬라이스를 99%까지 사용하고 CPU를 양도한다면 우선순위 유지가 가능 3) CPU 위주 작업이 대화형 작업으로 바뀜
단점: cpu 사용현황이나 pass 값을 유지하고 갱신해야한다.
해당 방식은 느림, 메모리가 커지면 어케할겨.... 프로세스 전환 시 프로세스를 메모리에 그대로 유지하면서, 운영체제가 시분할 시스템을 효율적으로 구현할 수 있게 해주는 것이 중요!!
스택과 heap은 확장을 위해 주소공간 양 끝단에 위치
운영체제는 물리 메모리를 공유하는 다수의 프로세스에게 어떻게 프로세스 전용의 커다란 주소공간이라는 개념을 제공할 수 있는가??
가상화의 목표
void func() {
int x;
}
1) 메모리 할당 잊기 2) 메모리 부족하게 할당받기(버퍼 오버플로우) 발생
여러 CPU에 작업을 어떻게 스케줄 해야하는가??
단일 CPU 하드웨어와 멀티 CPU 하드웨어의 근본적인 차이
단일 CPU 시스템에서는 하드웨어 캐시 계층이 존재
캐시는 지역성에 기반함
지역성
하나의 시스템에서 여러 프로세서가 존재허고 하나의 공유 메인 메모리가 있는 경우
캐시 일관성 문제가 일어날 수 있음
CPU들이 동일한 데이터 또는 구조체에 접근할 때 올바른 연산결과를 보장하기 위해서는 상호배제를 보장하는 동기화 기법이 많이 사용
구조체를 원자적으로 갱신하기 위해서는 락이 필요
mutex를 할당하고 루틴의 시작에 lock, unlock을 추가하면 문제를 해결할 수 있지만 CPU개수가 증가할수록 동기화된 자료 구조에 접근하는 연산은 매우 느려지게 됨
1) 확장성 결여: 다수의 CPU에서 제대로 동작하기 위해 락을 삽입하지만 락이 성능 저하를 초래
2) 캐시 친화성 문제: 캐시 친화성 방지를 위해 되도록 하나의 CPU에서 프로세스가 실행되도록 함(구현과 기법이 복잡)
하드웨어는 주소 변환을 통해 명령어 반입, 탑재, 저장 등의 가상 주소를 정보가 실제 존재하는 물리주소로 반환
운영체제가 주소변환시 추구하는 사항
프로그램의 모든 메모리 참조를 실제 메모리 위치로 재지정하기 위하여 하드웨어가 주소를 변환함
하드웨어 만으로는 메모리 가상화 구현이 힘듬(운영체제가 관여해야함)
void func()
int x = 3000;
x = x + 3;
컴파일러가 변환한 어셈블리 코드
128 : movl 0x0 (\%ebx), \%eax;
132 : addl \$0x03, \%eax;
136 : movl \%eax, 0x0(\%ebx);
한 쌍의 레지스터를 가지고 주소 변환에 도움을 주는 프로세서의 일부를 메모리 관리 장치(MMU)라고 함
베이스 바운드 방식의 가상 메모리 구현을 위해서 운영체제가 반드시 개입되어야하는 중요한 세개의 시점이 존재
프로세스 가상메모리공간 전체를 연속적으로 물리 메모리에 탑재 하게 됨으로서 프로세스의 힙과 스택 사이의 공간이 낭비되는 현상이 발생한다. 이를 해결하기 위해 세그멘테이션(segmentation)이라는 일반화된 base-bound 기법이 등장하게 된다.
논리적인 의미를 가지는 가변크기의 공간으로 메모리를 나눠서 물리 메모리에 올리게 되면 외부 단편화가 발생할 수 있는 태생적인 한계를 가지고 있음
동일 크기의 공간으로 분할하는 아이디어 => 페이징(paging)
페이징에서는 프로세스의 주소공간을 고정 크기의 단위로 나누게 되고 물리 메모리 또한 페이지 프레임(page frame)이라는 고정크기로 나눔
위의 그림과 같이 64바이트의 작은 주소 공간을 16바이트짜리 페이지 4개로 나눌수 있고 이를 물리 메모리의 페이지 프레임에 탑재 한것을 볼 수 있음.(물리 메모리의 페이지 프레임 또한 16바이트)
주소 공간의 각 가상 페이지에 대한 물리 메모리의 위치를 기록하기 위해서 운영체제는 각 프로세스마다 페이지 테이블이라는 자료구조를 가지고 있다. 페이지 테이블의 주요 역할은 주소 변환이다.
64바이트의 가상주소로 가정 하였으므로 6비트가 필요하고 각 페이지의 크기를 16바이트로하고 총 4개로 나눴기 때문에 2개의 비트로 VPN을 나타낼 수 있다.
가상 주소 21을 변환해 보면 아래와 같다.
페이지 테이블을 가지고 가상 페이지가 어떤 물리 페이지 프레임에 저장되어 있는지 찾아 낼수 있고 offset을 통해 해당 물리 페이지 프레임에서 얼만큼 떨어져 있는지도 알아낼 수 있기 때문에 주소 변환이 가능해 진다.
실제 32비트 주소 공간에서 4KB의 페이지를 사용한다고 하였을 때의 경우에 대해서 가상 주소 공간을 아래와 같이 나눔
VPN : 20비트 사용
OFFSET : 12 비트 사용 (10비트 = 1KB 이기 때문에 2비트만 추가 하면 4KB를 나타낼 수 있음)
페이지 테이블은 총 2^20(거의 백만) 개의 정보를 저장 하고 있어야 되고 페이지 테이블 항목(page table entry, PTE)마다 4바이트가 필요하다고 한다면 각페이지 테이블을 저장하기 위해 4MB의 메모리가 필요
프로세스 100개만 실행 시켜도 400MB의 메모리가 필요하게 되므로 이는 꽤 큰 공간이고 현대의 64비트 주소 공간을 가정하게 된다면 아주 커지게 된다.
-페이지 테이블의 크기가 크기 때문에 이를 MMU안에 유지할 수 없고 운영체제의 가상메모리에 저장된다.
우선 페이지 테이블이 VPN을 인덱스로 하는 단순한 배열 자료 구조(선형 페이지 테이블)라고 가정하고 생각을 할 수 있다. 이는 이후에 페이징의 문제를 해결하기 위해 고급 자료 구조로 변환된다. VPN 인덱스에 접근하면 PTE에 저장된 정보를 확인 할 수 있음
하드웨어에 페이지 테이블 베이스 레지스터(page table base register)는 페이지 테이블의 시작 위치를 저장하고 있다.
모든 메모리 참조에 대해 먼저 페이지 테이블에서 변환 정보를 반입 해야하기 때문에 한번의 추가적인 메모리 참조가 필요하게 된다.
메모리 참조는 비용이 비싸기 때문에 프로세스가 느려지게 됨
페이지 테이블 자체가 큰 메모리를 차지 한다.
주소 변환을 위한 추가적인 메모리 참조로 인해 느려진다.
변환 색인 버퍼(translation-lookaside buffer, TLB)는 MMU의 일부로서 자주 참조되는 가상 주소의 변환 정보를 저장하는 하드웨어 캐시 주소 변환 캐시(address-translation cache)
가상 메모리 참조시, 하드웨어는 먼저 TLB를 확인
CISC(complex instruction set computers): 복잡한 하드웨어 명령어들로 구성된 컴퓨터 시스템
TLB 미스 처리를 하드웨어에서 처리한다. 하드웨어는 페이지 테이블에 대한 정보를 가지기 위해 page table base register를 가짐
Valid Bit 특정 항목이 유효한 변환 정보를 가지고 있는지 표현
Protection Bit 페이지가 어떻게 접근될 수 있는지 표현(읽기, 쓰기, 실행)
TLB 캐시가 가득 찻을 때 어떤 부분을 교체해야 할지 정하는 정책이 필요하고 이는 디스크와 메모리 간의 페이지 스와핑 부분과 유사하다.
가장 대표적인 정책으로는 최저 사용 빈도(least-recently-used, LRU) 방식이 있다. 사용되지 않은 오래된 항목일수록 다음에 사용될 확률이 적다는 가정에 근거한다.
BUT!!! 세그멘테이션의 고질적인 문제점인 유연함 부족은 해결하지 못함
1) Page Directory 에는 PDE가 존재한다. 이 PDE의 PFN의 값으로 접근하면 Page Table(페이지 테이블)이 나온다. 2)Page Table(페이지 테이블)에는 PTE가 존재한다. 이 PTE의 PFN 값으로 접근하면 찾고자 하는 주소의 PFN(Page frame number)이 나온다. 3) 얻어낸 PFN과 OFFSET을 결합시켜 최종 주소를 얻어낸다.
1) 주소공간의 크기에 비례하여 페이지 테이블 공간이 할당 2) 페이지 테이블을 페이지 크기로 분할함으로써 메모리 관리에 용이
정의: 페이지 폴트는 프로세스가 참조하려는 가상 메모리 페이지가 현재 물리적 메모리에 로드되어 있지 않을 때 발생
목적: 프로세스가 필요로 하는 페이지를 효율적으로 로드하고 관리하는데 필요
1) 페이지 폴트 발생: 프로세스가 메모리에 접근하려 할 때, 해당 가상 주소에 대응하는 페이지가 물리적 메모리에 없으면 페이지 폴트가 발생
2) 인터럽트 처리: 페이지 폴트는 운영 체제에 의해 처리되는 인터럽트, 운영 체제는 이 인터럽트를 받고 현재 CPU의 상태를 저장한 후 페이지 폴트 처리 루틴을 실행
3) 페이지 로딩: 운영 체제는 필요한 페이지를 찾아 물리적 메모리로 로드, 이 페이지는 디스크의 스왑 영역이나 해당 파일 시스템에서 가져올 수 있습니다.
4) 페이지 테이블 업데이트: 페이지가 메모리에 로드된 후, 페이지 테이블이 업데이트되어 새로운 매핑 정보를 반영
5) 프로세스 재개: 페이지 로딩이 완료되면, CPU는 원래의 프로세스를 재개
새로운 페이지들을 위한 공간 확보를 위해 하나 또는 그 이상의 페이지들을 먼저 페이지 아웃시킴
페이지 교체 정책: 교체 페이지를 선택하는 것
운영체제가 여유공간의 크기가 최솟값보다 작아지면 여유공간 확보를 담당하는 백그라운드 쓰레드가 실행
백그라운드 쓰레드는 일반적으로 스왑데몬, 페이지 데몬이라고 불리는데 여유공간의 크기가 최대값이 될 때 까지 페이지를 제거
운영체제 스터디