본문 바로가기

프로그래밍/운영체제(OS)

프로세스

1. 프로세스

운영체제는 실행중인 프로그램의 개념을 제공하는데, 이를 프로세스(process)라 한다. CSAPP에서는 프로세스를 실행중인 프로그램에 대한 운영체제의 추상화 라고 표현한다. 프로세스가 뭔지 알려면 프로세스가 무엇으로 구성되었는지 이해하면 된다. 아래 그림에서 Processor, Main memory, I/O devices 들로 Process를 추상화 했다고 말한다. 즉 이 세 가지가 프로세스의 구성요소인 것이다. 특정 순간의 프로세스를 생각해보자. 실행되는 동안 접근했거나 영향을 받은 자원의 목록이 이 세가지에 포함되어 있을 것이다.
프로세스의 구성요소를 이해하기 위해 하드웨어 상태(machine state)를 먼저 이해해야 한다. 프로그램이 실행되는 동안 하드웨어 상태를 읽거나 갱신할 수 있다. 프로세스의 하드웨어 상태 중 가장 중요한 구성요소는 메모리이다. 명령어는 메모리에 저장된다. 실행 프로그램이 읽고 쓰는 데이터 역시 메모리에 저장된다. 프로세스가 접근할 수 있는 메모리(주소 공간(address space)라 불림)은 프로세스를 구성하는 요소이다.
레지스터도 프로세스의 하드웨어 상태를 구성하는 요소 중 하나이다. 많은 명령어들이 레지스터를 직접 읽거나 갱신한다. PC(Program Counter, Instruction Counter 라고도 불림), SP(Stack Pointer), FP(Frame Pointer) 같은 레지스터는 알아두면 좋다. PC는 프로그램의 어느 명령어가 실행중인지를 저장하고, SP와 FP는 함수의 변수와 리턴주소를 저장하는 스택을 관리할 때 사용하는 레지스터이다.
프로그램은 영구 저장장치(persistent storage)에 접근하기도 한다. 이 입출력 정보는 프로세스가 현재 열어 놓은 파일 목록을 가지고 있다. 
이렇게 세 가지 레지스터(프로세서), 메모리(메인메모리), 영구저장장치(I/O devices) 라는 프로세스 하드웨어상태의 구성요소들을 알아보았다.

1.1 프로세스 상태

프로세스 상태는 세 가지가 있다. (책에는 이렇게 요약되어 있지만 new, terminated 상태까지 합치면 다섯가지임)

(1) 실행(Running): 실행 상태에서 프로세스는 프로세서에서 실행 중이다. 즉, 프로세스는 명령어를 실행하고 있다.
(2) 준비(Ready): 준비 상태에서 프로세스는 실행할 준비가 되어 있지만 운영체제가 다른 프로세스를 실행하고 있는 등의 이유로 대기 중이다.
(3) 대기(Blocked): 프로세스가 다른 사건을 기다리는 동안 프로세스의 수행을 중단시키는 연산이다. 흔한 예로 프로세스가 디스크에 대한 입출력 요청을 하였을 때 프로세스는 입출력이 완료될 때까지 대기 상태가 되고, 다른 프로세스가 실행 상태로 될 수 있다. 이 개념이 CPU burst 와 I/O burst 와 관련있다. 입출력 요청시 프로세스가 I/O 요청이 완료될때까지 기다리는데 소비하는 시간이 I/O burst 이다. 나중에 프로세스 스케줄링에서 멀티레벨피드백큐를 공부할 때 필요한 개념이다.

 

2. 프로세스 API

프로세스를 생성하고 제어하는데에 필요한 인터페이스를 알아본다. 개념공부하는데에 중요도가 높은 파트는 아닌 것 같다.

2.1 fork() 시스템 콜

fork 는 호출한 프로세스가 자신과 같은 자식 프로세스를 생성하는 시스템콜이다. 중요한점은 자식 프로세스와 부모 프로세스가 완전히 동일하지 않다는 점이다. 자식프로세스는 자신의 주소 공간, 자신의 레지스터, 자신의 PC 값을 갖는다. 또 한가지 차이점은 fork() 시스템 콜의 반환 값이 서로 다르다. fork()로 부터 부모 프로세스는 생성된 자식 프로세스의 PID를 반환받고, 자식 프로세스는 0을 반환받는다.


2.2 wait() 시스템 콜

부모 프로세스가 자식 프로세스의 종료를 대기해야 하는 경우가 발생할 수 있다. 이러한 작업을 위해 wait 시스템 콜이 있다. 


2.3 exec() 시스템 콜

자기 자신이 아닌 다른 프로그램을 실행해야 할 때 사용된다. fork()와 exec() 명령을 왜 분리하였을까? Unix 쉘은 보통 fork(), wait() 그리고 exec()을 사용하여 사용자의 명령을 시작한다. fork()와 exec() 명령을 분리하였기 때문에 실행 중인 프로그램을 조작하지 않고도 입력/출력 재지정, 파이프, 그리고 다른 기능들을 처리하는 것이 가능하다. (정확하게 파악 못하고 넘어간 부분)

예를 들어 아래 코드를 보자. 자식프로세스가 실행되면 fork() 반환값이 '0'이기 때문 두 번째 분기문으로 들어간다. 부모프로세스가 실행되면 반환값이 자식프로세스 ID 이므로 세 번째 분기문으로 들어간다. 만약 부모프로세스가 wait()없이 fork()만을 호출하면 자식프로세스가 먼저 실행될 수도 있고 부모프로세스가 먼저 실행될 수 있다. 결과는 비결정적이다. 하지만 아래 코드처럼 wait()을 호출한다면 항상 자식프로세스 먼저 실행->종료 되고 부모는 그 다음에 실행->종료 되는 동일한 결과를 볼 수 있다. 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
	printf("hello world (pid:%d)\n", (int) getpid());
	int rc = fork();
	if (rc < 0) { // fork failed; exit
		fprintf(stderr, "fork failed\n");
		exit(1);
	} else if (rc == 0) { // child (new process)
		printf("hello, I am child (pid:%d)\n", (int) getpid());
	} else { // parent goes down this path (main)
		int rc_wait = wait(NULL);
		printf("hello, I am parent of %d (rc_wait:%d) (pid:%d)\n",
		rc, rc_wait, (int) getpid());
	}
	return 0;
}

 

여담: 슈퍼사용자(ROOT)

시스템을 관리하기 위한 사용자가 일반적으로 필요하다. AWS EC2 우분투 서버에서 터진 프로세스를 kill 한 경험이 있는데 kill한 주체가 바로 루트였다. 모든 프로세스에 대한 접근 권한이 있는 것이다. 프로세스에 대한 잘못된 종료같은 실수를 피하기 위해 일반 사용자로 시스템에 접속하는 것이 좋다. 그리고 sudo를 쓴다..?


면접 대비 요약

 

1. 프로세스란 무엇인가요?

프로세스는 실행중인 프로그램 혹은 실행중인 프로그램에 대한 운영체제의 추상화 입니다. 프로그램이 실행되면 세 가지 자원에 접근하거나 영향을 받습니다. 바로 메모리, CPU, I/O device 입니다. 이 세가지 구성요소들로 프로세스를 추상화 했다고 표현합니다. 

 

꼬리질문

- 추상화가 뭔가요? 가상화랑 차이점은 무엇인가요?

추상화는 중요하지않은 복잡한 백그라운드 디테일은 숨기고, 이해하기 쉽도록 단순화 시키는 과정을 뜻합니다. CPU는 virtualizing cpu, 메모리는 가상메모리, 디스크 입출력 장치는 파일로 추상화를 해서 프로세스에 이 단순화된 API를 제공합니다.(= 시스템콜)
가상화는 사용자에게 마치 물리적 한계를 뛰어넘는 듯한 착각을 제공하는 기술입니다. 앞서 설명드린 추상화를 통해 각각의 프로세스가 마치 싱글 머신을 독차지하여 동작하는 것처럼 환상을 제공합니다. 이것이 바로 가상화입니다. (or 예를들어 운영체제의 프로세스를 통한 CPU 가상화기법으로 여러개의 프로세스 만큼의 CPU를 가지고 있는듯한 환상을 제공합니다. 메모리 가상화를 통해서는 프로세스가 마치 메모리를 점유하고 있고 프로세스 마다 메모리가 있는 것 같은 환상을 제공합니다.)

 

- 구체적으로 세 가지가 프로세스랑 어떻게 엮여 있는지 예시?

세 가지 구성요소를 더 구체적으로 예를 들면 프로그램이 실행되면 프로그램의 명령어, 데이터 등등이 메모리에 저장됩니다. 그리고 프로그램 카운터, 스택포인터 같은 레지스터들이 사용됩니다. 프로그램은 영구저장장치에 접근하기도 합니다. 입출력 정보는 프로세스가 현재 열어놓은 파일목록으로 가지고 있기도 합니다.

 

- 프로세스의 메모리 영역에는 어떤것들이 있나요?

프로세스 메모리는 코드영역, 데이터영역, 힙영역, 스택영역으로 구성되어 있습니다. 코드영역에는 실행해야할 프로그램의 명령어가 있고 데이터 영역에는 전역변수, static 변수가 있습니다. 힙영역에는 프로그램 실행중에 동적으로 할당되는 메모리가 있으며, 스택은 함수의 호출이나 지역변수, 매개변수가 저장되는 메모리 영역입니다. (+a, 데이터영역은 .date 영역과 .bss 영역으로 구분하기도한다. bss에는 초기화 되지않은 데이터가 들어갑니다. 버퍼같은 크기는 큰데 초기화되지 않은 데이터는 변수명, 버퍼크기같은 정보들과 함께 bss에 넣어서 사용하면 메모리 공간 절약할 수 있음)

 

'프로그래밍 > 운영체제(OS)' 카테고리의 다른 글

프로세스 스케줄링  (0) 2022.08.28
운영체제 개요  (0) 2022.08.24
운영체제 정리  (0) 2022.08.24