| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 갭체크
- 오블완
- 1일1코테
- 99클럽
- 코딩테스트
- 코테공부
- 코딩테스트준비
- 운영체제
- 항해99
- 티스토리챌린지
- 자바스크립트
- 개발자취업
- 파이썬
- #코드트리 #코딩테스트 #코테공부 #백트래킹 #알고리즘 기초
- 프로그래밍 언어
- 플로이드-워셜
- 프로그래머스
- 자료구조
- 개발자루틴
- C
- 웹
- til
- 알고리즘
- 코딩테스트사이트추천
- 코드트리
- 인접 행렬
- 자바
- 연결 리스트
- Java
- 데이터베이스
- Today
- Total
와드의 블로그
Chapter 3. 프로세스와 스레드 본문
1. 프로세스의 개요
1-1. 프로세스의 개념
프로그램은 저장장치에 저장되어 있는 정적인 상태이고, 프로세스는 실행을 위해 메모리에 올라온 동적인 상태이다.
프로그램이 프로세스가 될 때 프로그램이 실행되는데 필요한 정보들을 포함한 프로세스 제어 플록이 만들어진다.
프로세스는 프로그램의 코드와 프로세스 제어 블록을 모두 포함한 자료구조이다.
1-2. 프로세스의 처리
① 일괄 처리 방식
가장 간단한 프로세스 처리 방법은 일괄 처리 방식이다.
일괄 처리 방식은 한 번에 하나의 프로세스만 처리하는 것으로, 하나의 프로세스가 끝날 때까지 다른 프로세스는 실행할 수 없다.
② 시분할 방식
일괄 처리 방식으로 프로세스를 처리하는 것은 비효율적이다.
시분할 방식은 CPU 자원을 적절히 배분하여 여러 프로세스를 동시에 처리할 수 있기 때문에 효율적이다.
시분할 방식에서는 CPU가 시간을 쪼개어 여러 프로세스에 적당히 배분함으로써 동시에 실행하는 것처럼 느껴진다.
③ 시분할 방식에서의 예상치 못한 상황 처리
프로세스를 처리할 때 IO 처리나 예외 처리 등의 이유로 처리가 지연될 수 있다.
이때 시분할 방식에서는 인터럽트를 기다려야 하는 프로세스 대신 다른 프로세스를 처리해서 효율성을 높인다.
시분할 방식을 이용한 프로세스의 처리는 여러 프로세스를 옮겨 가면서 진행되는데 현대의 운영체제는 이러한 시분할 방식을 기본으로 사용한다.
1-3. 프로세스의 상태
프로세스는 처리되는 과정에서 여러 가지 상태를 가진다.
① 프로세스의 다섯 가지 상태
- 생성 상태: 프로세스가 메모리에 올라와 실행 준비를 완료한 상태로 프로세스 제어 블록이 생성된다.
- 준비 상태: 생성된 프로세스가 CPU를 할당받을 때까지 기다리는 상태이다.
- 실행 상태: 프로세스가 CPU를 할당받아 실제 작업을 수행하는 상태이다.
- 대기 상태: 입출력을 요구한 프로세스가 입출력이 완료될 때까지 기다리는 상태이다.
- 완료 상태: 실행 상태의 프로세스가 작업을 마치면 완료 상태가 되고 프로세스 제어 블록이 삭제된다.
준비 상태에 있는 여러 프로세스 중 다음에 실행할 프로세스를 선정하는 일은 CPU 스케줄러가 담당한다.
CPU 스케줄러가 준비 상태에 있는 프로세스 중 하나를 골라 실행 상태로 바꾸는 작업을 디스패치라고 한다.
실행 상태의 프로세스는 일정 시간 동안 작업을 하고 일정 시간이 지나면 타임 아웃이 발생해서 다시 준비 상태로 돌아간다.

② 휴식 상태와 보류 상태
대부분의 프로세스는 위에 나온 다섯 가지 상태로 운영되며 이 다섯 상태를 활성 상태라고 한다.
프로세스의 상태는 활성 상태 외에 특별한 상태가 존재한다.
- 휴식 상태: 휴식 상태는 프로세스가 작업을 일시적으로 쉬고 있는 상태이다.
- 보류 상태: 보류 상태는 프로세스가 메모리에서 잠시 쫓겨난 상태이다. 다음 경우 보류 상태가 된다.
- 메모리가 꽉 차서 일부 프로세스를 메모리 밖으로 내보낼 때
- 프로그램에 오류가 있어서 실행을 미뤄야 할 때
- 바이러스와 같이 악의적인 프로세스라고 판단될 때
- 매우 긴 주기로 반복되는 프로세스라 메모리 밖으로 쫓아내도 문제가 없을 때
- 입출력을 기다리는 프로세스의 입출력이 계속 지연될 때
2. 프로세스 제어 블록과 문맥 교환
2-1. 프로세스 제어 블록
프로세스 제어 블록은 프로세스를 실행하는 데 필요한 중요한 자료구조다.
모든 프로세스는 고유의 프로세스 제어 블록을 가지며, 프로세스 제어 블록은 프로세스가 생성 시 만들어져서 프로세스의 실행이 완료되면 삭제된다.
① 프로세스 제어 블록의 구성
- 포인터: 프로세스 제어 블록을 연결하여 준비 상태나 대기 상태의 큐를 표현할 때 포인터를 사용한다.
- 프로세스 상태: 프로세스가 어떤 상태에 있는지를 나타낸다.
- 프로세스 구분자: 운영체제 내에 있는 여러 프로세스를 구별하기 위한 구분자를 저장한다.
- 프로그램 카운터: 다음에 실행될 명령어의 위치를 가리키는 PC값을 저장한다.
- 프로세스 우선순위: 프로세스가 실행될 때 사용되는 프로세스 우선순위를 저장한다.
- 각종 레지스터 정보: 프로세스가 실행되는 중에 사용되었던 레지스터의 정보가 저장된다.
- 메모리 관리 정보: 프로세스가 메모리 어디에 있는지 나타내는 메모리 위치 정보와 메모리 보호를 위한 한계 레지스터, 경계 레지스터 값 등이 저장된다.
- 할당된 자원 정보: 프로세스를 실행하기 위해 사용하는 입출력 자원이나 오픈 파일 등에 대한 정보를 저장한다.
- 계정 정보: 계정 번호, CPU 할당 시간, CPU 사용 시간 등의 정보를 저장한다.
- 부모 프로세스, 자식 프로세스 구분자: 부모 프로세스와 자식 프로세스의 구분자를 저장한다.
② 포인터의 역할
입출력이 완료되기를 기다리는 프로세스는 대기 상태로 모여서 입출력 장치 별로 큐를 형성한다.
준비 상태의 프로세스도 역시 준비 큐를 형성하면서 CPU 할당을 기다린다.
이러한 큐의 생성 과정에서 포인터를 사용한다.
2-2. 문맥 교환
① 문맥 교환의 의미
문맥 교환은 CPU를 차지하던 프로세스가 나가고 새로운 프로세스를 받아들이는 작업을 말한다.
이때 두 프로세스 제어 블록의 내용이 변경된다.
실행 상태에서 나가는 프로세스 제어 블록에는 지금까지의 작업 내용이 저장되고, 반대로 실행 상태로 들어오는 프로세스 제어 블록의 내용은 CPU에서 사용한다.
② 문맥 교환의 과정

3. 프로세스의 연산
3-1. 프로세스의 구조
프로세스는 코드 영역, 데이터 영역, 스택 영역으로 구성된다.
- 코드 영역: 프로그램의 코드가 저장된 영역이다.
- 데이터 영역: 코드가 실행되면서 사용하는 변수나 파일 등의 각종 데이터가 저장된 영역이다.
- 스택 영역: 운영체제가 프로세스를 실행하기 위해 부수적으로 필요한 데이터가 저장된 영역이다.
3-2. 프로세스의 생성과 복사
프로세스는 프로그램을 실행할 때 새로 생성된다.
사용자가 프로그램을 실행하면 운영체제는 프로그램을 메모리로 가져와 코드 영역에 넣고 프로세스 제어 블록을 생성한다.
프로세스는 새로 생성하는 방법뿐만 아니라 실행 중인 프로세스로부터 새로운 프로세스를 복사하는 방법도 있다.
① fork 시스템 호출의 개념
fork 시스템 호출은 실행 중인 프로세스로부터 새로운 프로세스를 복사하는 함수이다.
프로세스를 복사할 때 기존의 프로세스는 부모 프로세스가 되고 새로 생긴 프로세스는 자식 프로세스가 된다.
② fork 시스템 호출의 동작 과정
fork 시스템 호출을 하면 프로세스 제어 블록을 포함한 부모 프로세스 영역의 대부분이 자식 프로세스에 복사되어 똑같은 프로세스가 만들어진다.
단, 프로세스 제어 블록의 내용 중 일부가 변경된다.
- 프로세스 구분자가 바뀐다.
- 부모 프로세스와 자식 프로세스가 차지하고 있는 메모리의 위치가 다르므로 메모리 관련 정보가 바뀐다.
- 부모 프로세스 구분자와 자식 프로세스 구분자가 바뀐다.
③ fork 시스템 호출의 장점
- 프로세스의 생성 속도가 빠르다.
- 추가 작업 없이 자원을 상속할 수 있다.
- 시스템 관리를 효율적으로 할 수 있다.
④ fork 시스템 호출의 예
다음은 유닉스의 fork 시스템 호출 c 코드이다.
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid = fork();
if (pid < 0) {
printf("Error\n");
exit(-1);
}
else if (pid == 0)
printf("Child\n");
else
printf("Parent\n");
return 0;
}
fork는 자식 프로세스를 생성하고 자식 프로세스에는 0을 반환하고 부모 프로세스에는 0보다 큰 값을 반환한다.
결국 위의 예에서 부모 프로세스와 자식 프로세스가 모두 실행되어 Child와 Parent가 모두 출력된다.
3-3. 프로세스의 전환
fork 시스템 호출로 프로세스를 복사한 후 exec 시스템 호출로 프로세스를 전환할 수 있다.
① exec 시스템 호출의 개념
exec 시스템 호출은 기존의 프로세스를 새로운 프로세스로 전환시키는 함수이다.
exec 시스템 호출을 사용하는 목적은 프로세스의 구조체를 재활용하기 위함이다.
exec 시스템 호출을 사용하면 이미 만들어진 프로세스 제어 블록, 메모리 영역, 부모-자식 관계를 그대로 사용할 수 있어서 편리하다.
② exec 시스템 호출의 동작 과정
exec 시스템 호출을 하면 코드 영역에 있는 기존의 내용을 지우고 새로운 코드로 바꾼다.
또한 데이터 영역이 새로운 변수로 채워지고 스택 영역은 리셋된다.
③ exec 시스템 호출의 예
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid = fork();
if (pid < 0) {
printf("Error\n");
exit(-1);
}
else if (pid == 0)
execlp("mplayer", "mplayer", NULL);
else {
wait(NULL);
printf("mplayer terminated\n");
}
return 0;
}
execlp 함수를 사용해서 자식 프로세스의 코드를 바꾼다.
wait 함수를 통해 부모 프로세스는 자식 프로세스가 끝날 때까지 기다렸다가 실행이 된다.
3-4. 프로세스의 계층 구조
① 유닉스의 프로세스 계층 구조
유닉스는 커널이 처음 메모리에 올라와 부팅이 되면 커널 관련 프로세스를 여러 개 만드는 그중 init 프로세스는 전체 프로세스의 출발점이 된다.
유닉스의 프로세스는 init 프로세스를 부모로 하는 계층 구조를 형성한다.

② 프로세스 계층 구조의 장점
- 여러 작업의 동시 처리: 여러 작업의 동시 처리를 지원하면서 자원을 효율적으로 사용할 수 있다.
- 용이한 자원회수: 프로세스를 계층 구조로 만들면 프로세스 간의 책임 관계가 분명해져서 시스템을 관리하기가 수월하다.
③ 고아 프로세스
부모 프로세스는 자원을 회수하기 위해 자식 프로세스가 끝날 때까지 기다려야 한다.
그런데 부모 프로세스가 먼저 종료되거나 자식 프로세스가 비정상적으로 종료되어 부모 프로세스에 연락이 안 되는 경우가 있다.
프로세스가 종료된 후에도 비정상적으로 남아있는 프로세스를 고아 프로세스라고 한다.
운영체제는 이러한 고아 프로세스를 종료해서 자원을 회수해야 한다.
4. 스레드
4-1. 스레드의 개념
① 스레드의 정의
운영체제는 코드와 데이터를 메모리에 가져오고, 프로세스 제어 블록을 생성하고, 작업에 필요한 메모리 영역을 확보한 후, 준비 상태의 프로세스를 준비 큐에 삽입한다.
CPU 스케줄러는 프로세스가 해야 할 일을 CPU에 전달하고 실제 작업은 CPU가 수행한다.
이때 CPU가 프로세스로부터 전달받은 작업의 단위가 스레드이다.
② 프로세스와 스레드의 차이
개개의 프로세스와 스레드는 서로서로 미치는 영향이 다르다.
프로세스끼리는 약하게 연결되어 있는 반면 스레드끼리는 강하게 연결되어 있다.
서로 독립적인 프로세스는 데이터를 주고받을 때 프로세스 간 통신을 이용한다.
서로 강하게 연결되어 있는 스레드는 변수나 파일 등을 공유하고 전역 변수나 함수 호출 등의 방법으로 스레드 간 통신을 한다.
③ 레드 관련 용어
- 멀티스레드: 프로세스 내 작업을 여러 개의 스레드로 분할함으로써 작업의 부담을 줄이는 프로세스 운영 기법이다.
- 멀티태스킹: 운영체제가 CPU에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법이다.
- 멀티프로세싱: CPU 여러 개를 사용하여 여러 개의 스레드를 동시에 처리하는 작업 환경이다.
- CPU 멀티스레드: 한 번에 하나씩 처리해야 하는 스레드를 파이프라인 기법을 이용하여 동시에 여러 레드를 처리하도록 만든 병렬 처리 기법이다.
4-2. 멀티 레드 구조
① 멀티 레드 구조
멀티태스킹 환경에서는 여러 개의 작업을 동시에 처리하기 위해 fork 시스템 호출로 프로세스를 생성한다.
이 과정에서 코드 영역과 데이터 영역의 일부가 메모리에 중복되어 존재하여 메모리에 낭비가 생긴다.
스레드는 이러한 멀티태스킹의 낭비 요소를 제거하기 위해 사용한다.
하나의 프로세스 내에 여러 개의 스레드를 생성하는 멀티스레드는 코드, 파일 등의 자원을 공유함으로써 자원의 낭비를 막고 효율성을 향상한다.

② 멀티 스레드의 예
다음은 스레드를 이용한 간단한 자바 코드이다.
class TH_test extends Thread {
public void run() {
for(int i = 0; i < 100; i++)
System.out.println("Th_Test");
}
}
public class Test_main {
public static void main(String[] args) {
TH_test TH_print = new TH_test();
TH_print.start();
for(int i = 0; i < 100; i++)
System.out.println("Test_Main");
}
}
스레드를 사용하지 않는 일반 코드라면 "Test_Main"이 100번 출력된 후 "Th_Test"가 100번 출력되지만 위의 코드는 레드를 사용해서 "Test_Main"과 "Th_Test"가 섞여서 출력된다.
4-3. 멀티 스레드의 장단점
① 멀티 스레드의 장점
- 응답성 향상: 한 스레드가 입출력으로 인해 작업이 진행되지 않더라도 다른 스레드가 작업을 계속하여 사용자의 작업 요구에 빨리 응답할 수 있다.
- 자원 공유: 한 프로세스 내에서 독립적인 스레드를 생성하면 프로세스가 가진 자원을 모든 스레드가 공유하게 되어 작업을 원활하게 진행할 수 있다.
- 효율성 향상: 여러 개의 프로세스를 생성하는 것과 달리 멀티 스레드는 불필요한 자원의 중복을 막음으로써 시스템의 효율이 향상된다.
- 다중 CPU 지원: 2개 이상의 CPU를 가진 컴퓨터에서 멀티스레드를 사용하면 다중 CPU가 멀티스레드를 동시에 처리하여 CPU 사용량이 증가하고 프로세스의 처리 시간이 단축된다.
② 멀티 스레드의 단점
멀티 스레드는 모든 스레드가 자원을 공유하기 때문에 한 스레드에 문제가 생기면 전체 프로세스에 영향을 미친다.
4-4. 멀티 스레드 모델
① 사용자 스레드
사용자 스레드는 운영체제가 멀티스레드를 지원하지 않을 때 사용하는 방법이다.
이 스레드는 사용자 레벨에서 스레드를 구현하기 때문에 관련 라이브러리를 사용하여 구현한다.
사용자 스레드는 커널 입장에서는 일반 프로세스이지만 커널이 하는 일을 라이브러리가 대신 처리하여 여러 개의 스레드를 작동한다.
사용자 스레드는 문맥 교환의 오버헤드가 없어 속도가 빠르다.
하지만 한 프로세스의 타임 슬라이스를 여러 스레드가 공유하기 때문에 여러 개의 CPU에서 동시에 사용할 수 없고 보안에 취약하다.
② 커널 스레드
커널 스레드는 커널이 멀티스레드를 지원하는 방식이다.
커널 스레드는 커널 레벨에서 모든 작업을 지원하기 때문에 멀티 CPU를 사용할 수 있고 보안에 강하고 안정적으로 동작한다.
하지만 문맥 교환의 오버헤드 때문에 느리게 작동한다.
③ 멀티레벨 스레드
멀티레벨 스레드는 사용자 스레드와 커널 스레드를 혼합한 방식이다.
멀티레벨 스레드는 사용자 스레드와 커널 스레드의 방식을 혼용하기 때문에 사용자 스레드와 커널 스레드의 장단점을 모두 가지고 있다.
따라서 빠르게 움직여야 하는 스레드는 사용자 스레드로 작동하고, 안정적으로 움직여야 하는 스레드는 커널 스레드로 작동한다.
「쉽게 배우는 운영체제」 조성호
'CS > 운영체제' 카테고리의 다른 글
| Chapter 4. CPU 스케줄링 (1) | 2024.03.30 |
|---|---|
| Chapter 2. 컴퓨터의 구조와 성능 향상 (0) | 2021.10.21 |
| Chapter 1. 운영체제의 개요 (0) | 2021.10.20 |