둘셋 개발!

[운영체제] 프로세스 (뜻, 구조, 상태) 본문

운영체제

[운영체제] 프로세스 (뜻, 구조, 상태)

23 2023. 8. 7. 14:34

프로세스란?

메모리에 올라와 컴퓨터에서 실행되고 있는 프로그램이다.

 

오늘날의 컴퓨터 구조는 폰 노이만 구조를 따른다.

폰노이만 구조는 모든 프로그램은 메모리에 올라와야 실행할 수 있다는 것이다.

따라서 저장장치에 있는 프로그램을 동작하게 하고 싶다면 메모리에 올라와야 하고 메모리에 올렸다면 실행할 수 있는 프로그램(프로세스)가 되는 것이다.

그렇다면 메모리에 어떤 형태로 프로그램이 올라와져 있는 것일까??

 

 


프로세스 메모리 구조

우선 메모리의 구조부터 알아보자.

메모리 구조

(이미지 출처: yeahg_dev 블로그 https://velog.io/@yeahg_dev/TIL11.-C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%95%A0%EB%8B%B9-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0)

 

CODE (코드 영역)

프로그램의 본문코드가 있는 공간이다. 이 코드는 읽기 전용이다.

 

예를 들어

package hello.servlet;

public class TestApplication {

	public static void main(String[] args) {
		printf("hello java");
	}

}

이런 코드를 실행한다면 이 코드가 그대로 코드영역에 읽기 전용으로 올라가는 것이다.

(물론 이 코드를 OS가 이해할 수 있는 컴파일된 코드가 올라간다)

 

DATA (데이터 영역)

이 영역에는 전역 변수, 정적 변수, 전역 상수 등 각종 데이터가 저장되어 있다.

코드 영역과 달리 읽기와 쓰기 둘 다 가능한다.

물론 상수의 경우 쓰기가 불가능 하겠지만!

 

Stack (스택 영역)

이 영역에는 함수호출과 관련된 지역 변수, 매개변수, 리턴 값 등이 저장되어 있다.

 

예를 들어

int main() {
    int a=1, b=2;
    f1(a, b);  // f1 호출
    exit();
}

f1 (int x, y) {
    f2 (x, y);	// f2 호출
}

f2 (int b, int t) {
    print("%d %d\n", b, t);
}

이런 수도코드가 있다고 하자.

1. main()함수가 맨 먼저 실행되고 main()함수 내부에 있는 f1()을 호출한다.

2. 스택 영역에서는 매개변수인 x, y와 함수 f1가 종료되고 다시 되돌아올 정보(위치)를 저장한다.

3. f1()이 실행 되면서 다시 내부에서 f2()을 호출한다.

4. 스택 영역에는 매개변수인 b,t와 함수 f2가 종료되고 다시 되돌아올 정보(위치)를 저장한다.

5. f2()의 함수를 끝까지 마치면 스택을 pop() 시킨다 (스택의 가장 위에 저장되어 있던 b,t, 되돌아올 정보가 빠져나옴)

6. 되돌아올 정보를 가지고 되돌아 간다.

7. f1()의 함수를 끝까지 마치면 마찬가지로 스택을 pop() 시킨다. (스택의 가장 위에 저장되어 있던 x,y, 되돌아올 정보가 빠져나옴)

8. 되돌아올 정보를 가지고 되돌아 간다.

9. main()함수에서 exit()을 하면서 해당 프로세스를 종료한다.

 

HEAP (힙 영역)

이 영역에는 동적으로 할당되는 변수 영역이다. 

c언어의 경우 malloc(), calloc()함수를 사용해서 동적으로 할당한다.

자바의 경우 직접 동적으로 할당되어 힙 영역에 저장되는 것은 인스턴스 변수이다.

 

잠깐 의문이 드는 것은 왜 동적으로 할당해야할까? 그냥 정적으로 할당하면 안되는 것일까?

그건 바로 메모리를 효율적으로 사용하기 위함이다.

예를 들어 배열 같은 경우 크기가 큰 배열을 선언하면 프로세스 실행 시작부터 끝까지 메모리를 차지하게 되지만, 동적인 크키로 배열을 선언하면 필요할 때만 메모리를 사용하고, 필요없을 때에는 메모리 영역을 반환함으로써 메모리 낭비를 해결할 수 있다.

 


 

지금까지 프로세스의 무엇인지 알아보았고, 프로세스가 어떻게 저장되는지 살펴보았다.

컴퓨터는 하나의 프로세스만 실행하면 끝일까? 아니다.

여러 프로세스가 동작하도록 해야한다.

 

여러 프로세스가 동작하기 위한 방법은 여러가지가 있다.

그 중 가장 간단한 방법으로 일괄 작업 방식이 있다.

일괄 작업 방식으로 한 프로세스를 실행하면 해당 프로세스가 끝나기 전까지 다른 프로세스를 실행하지 않는 것이다.

그렇게 순차적으로 프로세스를 하나하나 실행하는 방법이다.

이 동작방식은 멀티가 불가능하기 때문에 불편함이 많다. (노래를 들으면서 쇼핑을 할 수 없다🙀)

 

따라서 시분할 방식을 사용하면 된다.

cpu가 시간을 쪼개서 여러 프로세스에 적당히 배분함으로써 동시에 실행하는 것처럼 하면 된다.

 

이렇게 여러 프로세스를 실행하게 되면 cpu를 할당받아 실행중인 프로세스가 있을 것이고, cpu를 뺐겨서 기다리는 프로세스도 있을 것이고, 모든 실행을 끝마쳐 종료된 프로세스 등등 다양한 상태가 존재한다.

 


 

프로세스 상태

프로세스 구조

Create 상태

프로그램이 메모리에 올라오고 운영체제로 부터 PCB을 할당받은 상태

(PCB: Process control block의 약자로 운영체제 커널영역에 저장된 자료구조로, 프로세스에 대한 정보가 있음)

 

 

Ready 상태

프로세스들이 cpu를 할당받으려고 기다리는 상태이다.

프로세스는 Ready Queue에서 기다리며 cpu 스케줄러에 의해 관리된다.

PCB 안에 포인터가 있는데, 이 포인터를 연결해서 큐를 구현한다.

 

그림을 살펴보면 Ready가 되는 프로세스 상태는 총 3가지가 있다.

create상태에서 ready가 될 수도 있고, running 상태에서도 ready가 될 수도 있고 wait 상태에서도 ready가 될 수 있다.

실행중인 프로세스(running 상태인 프로세스)가 타임아웃으로 인터럽트를 발생하거나,

입출력을 위해 대기상태에 있던 프로세스가 입출력을 완료해서 다시 cpu을 할당받기 위해 ready 상태 큐로 들어간다.

 

Running 상태

cpu를 할당받아 실행중인 상태이다.

cpu의 갯수만큼 혹은 cpu의 코어 갯수 만큼 running 상태인 프로세스가 존재한다.

 

Wait 상태

실행 중이였다가 프로세스가 입출력을 요청하면,  입출력을 완료할 때까지 기다리는 상태이다.

이 상태는 작업의 효율성을 높여주는 중요한 상태이다.

왜냐하면 입출력을 할 때는 cpu가 직접 하지 않고 입출력 관리자에게 명령을 내려 입출력 관리자가 입출력을 한다.

따라서 입출력을 할 때까지 cpu는 쉬는 시간을 갖게 된다.

cpu는 쉬는 시간이 길어지면 될까? 아니다....대기 중인 프로세스들도 있기 때문에 쉴 틈이 없다!

따라서 입출력 요청이 들어오면 wait 상태로 보내는 것이다.

 

Terminate 상태

메모리에서 해당 프로세스를 삭제하고 PCB도 폐기된 상태이다.

 


ref

https://zangzangs.tistory.com/107

https://velog.io/@yeahg_dev/TIL11.-C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%95%A0%EB%8B%B9-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0