티스토리 뷰

1. 프로세스 개념 

: 일반적으로 프로세스는 실행 중인 프로그램으로 정의한다.

 

프로그램은 디스크 상에 존재하며 실행을 위한 명령어와 정적 데이터의 묶음이다.

이 명령어와 데이터 묶음을 읽고 실행하여 프로그램에 생명을 불어넣는 것이 운영체제이다. 

 

여기서 질문!!

위에서 말하는 정적 데이터란 "이미 만들어진 데이터"를 의미한다. 

반면에 동적 데이터는 요청이 발생할 때 새로 생성되서 전달되는 데이터이다. 

정적데이터는 static 키워드를 통해 지역변수의 소멸시기를 '프로그램 종료 시'로 바꿀 수 있기 때문에

 static 변수를 지역변수와 전역변수의 영역으로 끌고 오는 것은 다소 오해의 소지가 있다. 

 

주소 메모리 영역

 

사용자는 하나 이상의 프로그램을 동시에 실행시키기를 원한다. 

여러 프로그램을 동시에 실행할 수 있으면, 시스템을 쉽게 사용할 수 있다. (easy to use - 운영체제의 목적이기도 함.)


2. CPU 가상화 

여기서 질문!!

Q) 적은 개수의 CPU 밖에 없더라도, 운영체제는 어떻게 거의 무한개에 가까운 CPU가 있는 듯한 환상을 만들 수 있을까?

 

A)  그것은 바로 운영체제가 CPU를 가상화하기 때문이다. 즉 하나의 프로세스를 실행하고, 얼마 후 중단시키고 다른 프로세스를 실행하는 작업을 반복하면서 실제 하나 또는 소수의 CPU로 여러 개의 가상 CPU가 존재하는 듯한 환상을 만들어 낸다. 

cf> 시분할(time sharing) : 원하는 수만큼의 프로세스를 동시에 실행할 수 있게 한다. 시분할 기법은 CPU를 공유하기 때문에, 각 프로세스의 성능은 낮아진다. 

 

- CPU 가상화의 효율적인 구현을 위해서는 메커니즘(mechanism) - how과 정책(policy) - what 가 필요하다.

1. 메커니즘은 필요한 기능을 구현하는 방법이다. ex) 문맥 교환 방법  (CPU에서 프로그램 실행을 잠시 중단하고 다른 프로그램을 실행)

2. 정책은 운영체제에서 어떤 결정을 내리는 데 사용되는 알고리즘이다. ex) 스케줄링 정책 (어느 프로그램을 먼저 선택해서 실행할지)


3. 프로세스 API - 운영체제가 반드시 API로 제공해야 하는 몇몇 기본 기능 

  • 생성(Create) : 운영체제는 새로운 프로세스를 생성할 수 있는 방법을 제공해야 함. 쉘에 명령어를 입력하거나, 응용 프로그램의 아이콘을 더블-클릭하여 프로그램을 실행시키면, 운영체제는 새로운 프로세스를 생성함.
  • 제거(Destroy) : 프로세스 생성 인터페이스를 제공하는 것처럼 운영체제는 프로세스를 강제로 제거할 수 있는 인터페이스를 제공해야 한다. 물론, 많은 프로세스는 실행되고 할 일을 다하면 스스로 종료한다. 그러나 프로세스가 스스로 종료하지 않으면 사용자가 필요 없는 프로세스를 중단시켜야 한다. 
  • 대기(Wait) : 때론 어떤 프로세스의 실행 중지를 기다릴 필요가 있다. 
  • 각종 제어 (Miscellaneous Control) : 프로세스의 제거, 대기 이외에 일시 정지하거나 재개하는 기능을 제공한다. 
  • 상태 (Status) : 프로세스 상태 정보를 얻어내는 인터페이스도 제공한다. 상태 정보에는 얼마 동안 실행되었는지 또는 프로세스가 어떤 상태에 있는지 등이 포함된다. 

4. 운영체제는 어떻게 프로세스를 생성하는가? 프로그램이 어떻게 프로세스로 변형되는가?

 

1. 디스크 상의 프로그램 코드와 정적 데이터를 찾아 메모리, 프로세스의 주소 공간에 탑재(load)한다.

   초기 운영체제는 프로그램 실행 전에 코드와 데이터를 모두 메모리에 탑재함. 현재의 운영체제들은 이 작업을 늦춤. 즉, 프로그램을 실행하면서 필요한 부분만 메모리에 탑재함. 이 부분을 정확하게 이해하기 위해서는 페이징(paging)과 스와핑(swapping) 동작의 이해가 필요함. 

 

2. 스택과 힙을 생성하고 초기화한다.

   특정 크기의 메모리 공간이 프로그램에 스택 용도로 할당되어야 한다. 특히, main() 함수의 인자인 argc와 argv 벡터를 사용하여 스택을 초기화한다. 

또한, 프로그램의 힙을 위한 메모리 영역을 할당한다. 힙은 동적으로 할당된 데이터를 저장하기 위해 사용된다. 

 

3. 입출력 셋업과 관계된 다른 작업을 마치면, 운영체제는 프로그램 실행을 위한 준비를 마치게 된다.

프로그램의 시작 지점(entry point), 즉 main() 루틴으로 분기함으로써 운영체제는 CPU를 새로 생성된 프로세스에게 넘기게 되고 프로그램이 실행된다.  

 UNIX 시스템에서 각 프로세스는 기본적으로 표준 입력(STDIN), 표준 출력(STDOUT), 표준 에러(STDERR) 장치에 해당하는 세 개의 파일 디스크럽터 (file descripter)를 갖는다. 이 디스크럽터를 사용하여 프로그램은 터미널로부터 입력을 읽고 화면에 출력을 프린트하는 작업을 쉽게 할 수 있다. 


5. 프로세스 상태 

1. 실행(Running) : 실행 상태에서 프로세스는 프로세서에서 실행 중이다. 즉, 프로세스는 명령어를 실행하고 있다. 

2. 준비(Ready) : 준비 상태에서 프로세스는 실행할 준비가 되어 있지만 운영체제가 다른 프로세스를 실행하고 있는 등의 이유로 대기 중이다. 

3. 대기(Blocked) : 프로세스가 다른 사건을 기다리는 동안 프로세스의 수행을 중단시키는 연산이다. ex) 입출력이 완료될 때까지 대기 상태

4. 초기(inital) : 프로세스가 완전히 생성되기 전까지의 상태

5. 최종(final) : 프로세스는 종료되었지만 해당 프로세스가 사용하던 각종 자원들이 아직 완전히 반납되지 않은 상태(부모, 자식) = 좀비 상태 

프로세스 : 상태 전이


6. CPU 시뮬레이션 실습해보기 (실행 코드 : github.com/angelatto/OS_Study/tree/main/pr_self/homework/cpu-intro )

아래의 명령은 4개의 명령어를 실행하고 모두 CPU만 사용하는 하나의 프로세스와 오직 입출력을 요청하고 완료되기를 기다리는 하나의 프로세스를 의미한다. 두 프로세스가 모두 종료되는데 걸리는 시간은 11이다. 

- 옵션으로 지정된 프로세스의 순서를 바꿔봄. 실행 순서를 교환하는 것은 의미가 있나?   YES!!!!

  시간이 11에서 7로 줄었다. 이유는 입출력 요청을 먼저 실행시킨 후 PID가 0인 프로세스가 대기 상태일 때

PID가 1인 프로세스를 실행시켰기 때문이다. 

- 아래의 명령은 요청 프로세스가 입출력을 하는 동안 다른 프로세스로 전환하지 않고 대신 요청 프로세스가 종료되길 기다린다. 

이번에는 프로세스가 입출력을 기다릴 때마다 다른 프로세스로 전환시킨다. 

결과는 아래와 같다. 

또 다른 중요한 행동은 입출력이 완료되었을 때 해당 프로세스를 바로 실행할 것인지 여부를 결정하는 것이다. 

먼저, 입출력이 완료된 시점에 이미 실행 중이던 프로세스가 계속 실행되도록 해본다. 

다음으로는 입출력이 완료되었을 때 요청 프로세스가 곧바로 실행되도록 해본다. 

과연 방금 입출력을 완료한 프로세스를 다시 실행시키는 것이 좋은 생각일 수 있는 이유는 무엇인가? 

-> PID가 0인 프로세스에게 우선순위를 높게 부여하면 입출력 대기(blocked) 시간에 다른 프로세스를 실행시킴으로써 

자원을 효율적으로 사용할 수 있기 때문이다. 


< 시뮬레이션 결과 >

1. SWITCH_ON_IO 가 SWITCH_ON_END 보다 시간 절약.

   ->프로세스가 입출력을 기다릴 때마다 다른 프로세스로 전환하는 게 효율적임.

 

2. RUN_IMMEDIATE 가 RUN_LATER 보다 시간 절약.!? -> 입출력이 완료되었을 때 해당 프로세스를 바로 실행하는 것이 효율적.!?

   이것은 반드시 그렇지는 않음.