독학사- 운영체제 편을 공부하고 정리한 내용입니다.
프로세스 개념
커널에 등록되어 실행중인 프로그램을 의미
- 디스크에 있던 프로그램을 메모리에 적재하여 운영체제의 제어를 받는 상태이다.
- 프로세서 점유 시간, 메모리, 파일 같은 자원을 할당받는다.
- 프로세스는 활동상태를 나타내는 프로그램 카운터와 프로세서 레지스터를 포함한다.
- 별도의 주소공간을 가지며 코드, 데이터, 힙, 스택으로 분리된 메모리 공간을 가진다.
- 프로세스 메모리 구조
- 코드(text segment): 실행 가능한 명령어들이 포함된 코드가 정의되어있다. 수정(write)이 불가능하다.
- 초기화된 전역 변수(data segment): 초기값이 있는 전역 변수가 정의되어있다.
- 초기화되지 않은 전역변수(BSS): 초기값이 없는 전역 변수가 정의되어있다.
- 힙(heap): 실행 시간에 동적으로 할당되는 메모리 공간으로 낮은 주소부터 차례대로 할당받는다.
- 스택(stack): 함수 내 사용되는 지역변수가 정의되는 공간으로 높은 주소부터 차례대로 할당받는다.
메모리 초과로 스택 영역이 힙 영역을 침범하게되면 스택 오버 플러우,
힙 영역이 스택 영역을 침범하게 되면 힙 오버플로우 오류로 인한 인터럽트가 발생한다.
- 윈도우 운영체제
운영체제는 실제 물리 메모리 주소가 아니라 가상 주소를 사용하고 운영체제 마다 메모리 할당 방식이 다르다. "알려진" 메모리 구조는 높은 주소에서 부터 스택, 낮은 주소부터 힙 영역이 할당된다고 생각되는데, 실제 Visual Studio IDE에서 주소를 출력하면 조금 다른 양상이 나타난다.
힙 메모리는 스택보다 높은 가상 주소를 가지며 위로 증가한다. 또한 스택은 낮은 주소부터 할당되어 아래로 증가하게 된다. 이로 써 윈도우 운영체제에서는 힙과 스택이 충돌하는 일을 막고 있는 듯 하다.
초기화되지 않은 전역 변수 a, 초기화된 b 전역 변수가 낮은 주소부터 할당되어 있다.
"지역변수" c는 100개짜리 int 형 배열의 첫 번째 원소 주소를 가리키는 포인터 지역 변수이다.
따라서 실제 배열은 heap 영역에 동적으로 할당되어 있기 때문에 c [0]는 heap영역에 c는 지역변수이므로 stack영역에 정의되어 있다.
d는 100개짜리 int 형 배열이다. 그 의미는 d [0] 주소와 d주소는 같고 배열은 stack영역에 할당되어 있다.
--- 관련 글
stackoverflow.com/questions/12687274/size-of-stack-and-heap-memory
또다른 예시
#include <stdlib.h>
#include <stdio.h>
void check(int depth) {
char c;
char* ptr = (char*) malloc(1);
printf("stack at %p, heap at %p\n", &c, ptr);
if (depth <= 0) return;
check(depth - 1);
}
int main() {
check(10);
return 0;
}
프로세스 상태
- 프로세스 상태와 전이
상태 | 설명 | 상태변화 |
생성(New) | 프로세스 생성에 대한 허락을 받고 메모리와 프로세스 제어 블록을 할당 받는 중인 상태 준비 상태가 되기 위한 임시 상태이다. |
New -> Ready: 운영체제로 부터 실행 허락을 받아 프로세스를 생성한다. |
준비(Ready) | 메모리 할당이 완료되고 CPU 스케줄로부터 선택되어 실행 차례를 기다리는 상태 | Ready -> Running / dispatch(PID): CPU 스케줄러가 해당 프로세스를 실행한다. |
실행(Running) | CPU를 할당받아 커널에 등록되어 실행되는 상태 | Running -> Ready / timeout interrupt: 프로세스 마다 할당 받은 실행 시간을 초과하여 timeout 인터럽트로 인한 준비 상태로 전이된다. Running -> Wait / block(PID): 입출력 요청으로 해당 프로세스를 잠시 대기한다. |
대기(Wait) | 입출력 인터럽트로 잠시 대기하는 상태 입출력 작업이 완료되면 다시 준비 상태가 된다. |
Wait -> Ready / wakeup(PID): 입출력 완료로 인터럽트가 발생하고 대기 상태에 있던 프로세스를 준비 상태로 전이한다. |
완료(Terminated) | 프로세스가 종료된 상태 할당받은 메모리를 해제하고 프로세스 제어 블록을 삭제한다. |
Running -> Terminated exit(status): 실행을 완료하고 정상적으로 종료한다. abort(status): 오류나 다른 프로세스에 의해 비정상적으로 종료한다. |
- Wait -> Suspended Wait / Ready -> Suspended Wait
Suspended는 중지되었음을 의미하고 프로세스가 할당받은 메모리가 해제된 상태이다.
이와 같은 상태 전이는 다른 우선순위가 높은 프로세스가 스케줄링되어 현재 실행 중인 프로세스를 중단함으로써 발생한다.
다시 메모리를 할당받아 실행될 수 있으므로 종료 상태와 구별된다.
프로세스 제어 블록 (Process Control Block)
프로세스를 실행하는데 필요한 중요한 정보를 보관한다.
- 포인터: 프로세스를 가리키는 포인터, 부모/자식 프로세스 포인터, 할당받은 메모리 위치에 대한 포인터, 기타 할당받은 자원에 대한 포인터
- 프로세스 상태: 생성, 준비, 실행, 대기 등의 상태
- 프로세스 구분자: 프로세스 고유 식별 번호
- 프로세스 카운터: 다음 실행될 명령어의 위치
- 프로세스 우선순위: 스케줄링 정보와 해당 프로세스 우선순위
- 레지스터 정보: 실행 중 사용되는 누산기, 색인 레지스터, 스택 포인터
- 메모리 관리 정보: 메모리 위치 정보, 보호를 위한 경계 레지스터, 한계 레지스터 값, 세그먼테이션 테이블 및 페이지 테이블
- 할당된 자원 정보: 입출력 자원이나 열려있는 파일 등
- 계정 정보: 계정 정보, CPU 할당 시간, 사용 시간 등
- 부모/자식 프로세스 구분자: 부모 프로세스의 구분자 PPID와 자식 프로세스의 구분자 CPID
문맥 교환 (Context Switching)
프로세서(CPU)가 현재 프로세스에서 사용되지 않을 때 다른 프로세스로 교체하여 프로세서를 쉬지 않게한다.
- 실행 중이던 프로세스를 교체하는 것을 의미한다.
- 대기 상태에 있는 프로세스를 교체함으로써 프로세서 유휴 시간을 줄여 작업 처리량과 CPU 사용률을 높인다.
- 지금까지의 작업 내용(PCB)을 저장하고 들어오는 프로세스 제어 블록 내용으로 CPU가 세팅된다.
- 실행 중인 프로세스 P0가 타임아웃/입출력 인터럽트나 다른 시스템 호출로 대기 상태가 되는데, 이때 작업 내용을 PCB0에 저장한다.
- 프로세스 P1를 실행하기 위해 PCB1 내용을 CPU 레지스터로 불러오고 P1을 실행한다.
- 프로세스 P1이 똑같이 대기 상태로 돌입하고 작업내용을 PCB1에 저장한다.
- PCB0에 있던 내용을 CPU 레지스터에 불러오고 P0를 실행한다.
스레드
- 프로세스의 실행 단위
- CPU에 작업을 요청하는 실행 단위
- 스레드 ID, 프로그램 카운터, 레지스터 정보, 스택으로 구성된다.
- 프로세스 내에서 코드, 데이터, 힙 공간을 공유하기 때문에 불필요하게 자원을 생성하거나 복제하는 것을 방지한다.
- 스레드마다 독립적인 실행 흐름을 가지기 때문에 함수 호출과 지역변수를 위한 별도의 스택 공간이 필요하고 실행 흐름을 기억하기 위해 PC와 레지스터 또한 필요하다.
멀티 스레드
- 멀티 스레드의 특징
- 멀티 스레드: 프로세스의 모든 작업을 순서대로 실행하지 않고 작은 단위의 스레드로 작업을 분할하여 부담을 줄이는 운영 기법이다. 멀티스레드 간 프로세스에 할당된 공유 자원은 공유하고 독립적인 실행 흐름으로 별도로 할당된 자원을 사용할 수 있다.
- 멀티 태스킹: 운영체제가 CPU에 작업을 할당할 때 시간을 잘게 나누어 배분하는 기법이다. 여러 스레드에 시간을 잘게 나누어 주는 시스템을 시분할 시스템이라고하고 운영체제는 CPU에게 프로세스 대신 스레드를 할당한다.
- 멀티 프로세싱: 스레드 간 공유 자원 문제 가능성이 적은 경우, 즉 병렬로 처리해도 되는 작업일 경우 다중 CPU를 사용하여 여러 스레드를 병렬적으로 처리할 수 있다.
- CPU 멀티 스레드: 하드웨어적인 방법으로 하나의 CPU에서 여러 스레드를 동시에 처리하는 병렬 처리 기법이다.
※ 멀티 프로그래밍(다중 프로그래밍): 초기 CPU에는 하나의 프로그램만 실행할 수 있는데, 그 프로그램이 입출력 처리로 CPU를 사용하지 않는 유휴 시간이 발생한다. 따라서 여러 프로그램을 메모리에 적재해 하나의 프로그램을 실행하다 CPU 유휴 시간 발생 시 다른 프로세스를 실행하고 프로세서의 사용률을 높일 수 있다.
- 용어
- Single point of failure: 하나의 스레드에서 실패로 오류 종료 시 프로세스에 포함된 전체 스레드가 강제 종료된다.
- Shared data: 스레드의 공유 자원에 대한 병렬적 접근에 따라 공유 자원에 대한 동기화 문제가 있다.
- 멀티 프로세스를 사용하지 않는 이유
- 시분할 시스템, 프로세스나 스레드에 사용 시간이 할당되는 만큼 실행하고 교체되는 시스템에서 프로세스 교체 비용이 스레드 교체 비용보다 비싸기 때문이다.
- 스레드는 한 프로세스 내에서 코드, 데이터, 힙 공간을 공유하고 운영체제의 제어 신호 및 열린 파일 자원 등을 공유할 수 있으므로 자원을 효과적으로 사용할 수 있다.
- 프로세스 간 통신(IPC)을 위해 운영체제 자원(PIPE)을 이용해야 하고 과정이 복잡하다.
예를 들면, 웹 서버-클라이언트의 관계에서 클라이언트가 웹 페이지를 요구할 때마다 서버 프로그램에서 새로운 프로세스를 할당하거나 복제한다면 열려있는 소켓이나 웹 페이지 자원 등이 새로운 프로세스에 생성된다.
이 객체들에 대한 자원은 클라이언트마다 공유 가능하므로 클라이언트 요구마다 새로운 스레드를 생성하여 자원을 공유하고 멀티 태스킹을 통한 빠른 응답 시간, 즉 실시간성을 제공할 수 있다.
스레드 분류
- 사용자 레벨 스레드
- 초기 운영체제가 멀티 스레드를 지원하지 않을 때 사용되었다.
- 사용자가 직접 멀티 스레드 관련 스케줄링, 동기화 함수를 구현해서 멀티 스레드를 사용한다.
- 스레드 문맥 교환만 일어나므로 비용이 싸고 빠르다.
- 스레드 하나가 대기되면 프로세스 내 모든 스레드가 대기한다.
- 다른 CPU의 스케줄러를 사용할 수 없으므로 여러 CPU를 사용할 수 없다.
- 커널 레벨 스레드
- 스레드마다 커널이 제공하는 독립적인 운영체제 기능을 사용할 수 있다.
- 다중 CPU를 이용한 멀티 프로세싱이 가능하다.
- 프로세스 문맥 교환이 필요하다면 비용이 비싸고 느리다.
'Computer Science 기본 지식 > 운영체제' 카테고리의 다른 글
[운영체제] 프로세스 동기화 (0) | 2021.01.15 |
---|---|
[운영체제] 프로세스 스케줄링 (0) | 2021.01.14 |
[운영체제] 명령어 구조와 사이클 / 인터럽트 / 커널 (0) | 2021.01.12 |
[운영체제] 컴퓨터 시스템의 구성 (0) | 2021.01.11 |
[운영체제] 운영체제 개념 (0) | 2021.01.11 |