[OS] 4) Process Management

Gon Kim·2022년 9월 26일
0

OS - 반효경 교수님

목록 보기
5/7

자료 출처 : KOCW 운영체제 - 반효경 교수님

1. Process의 생성과 종료

process는 어떻게 만들어질까? 이 부분은 그냥 진리라고 생각하고 받아들이자. 이렇게 작동하도록 설계했다.

  • Process는 부모 process로부터 만들어진다. 이미 어떤 process가 있으면, 거기서 다른 process를 호출하는 형식인 것이다.
    • 이러한 구조는 트리 형태를 빌려 표현할 수 있다.
    • 족보만 생각해도 쉽게 이해할 수 있다.
  • process가 생기면, code, stack, data가 필요했다. 이는 운영체제로부터 할당된다.
    • 원칙적으로 자원은 부모와 공유하지 않는다. 하는 경우도 있다.
  • 부모와 자식이 공존하며 수행되는 경우가 있다.
    • 자식이 종료될 때까지 부모가 기다리는 경우도 있다.

💡 Copy-on-write(COW)
사실 부모와 자원을 공유하지 않는게 원칙인데, 리눅스와 같은 운영체제에서는 이를 효율적으로 수행하기 위해 일단 공유부터 한다. PC만 따로 두고, 일단 쭉 진행하다가 수정해야할 부분이 생기면 그 부분들을 그제서야 따로 copy한다. 말 그대로 write해야할 때 copy하는 것이다.

각각의 과정을 조금 더 자세히 알아보도록 하자.

1) 생성 과정

앞에서도 배웠듯이 process 하나 다루는게 어지간한 일이 아니다. 따라서 process가 바로 자식을 만들지는 않고, 운영체제에게 부탁한다.

두 단계를 거친다.

  • 먼저 pid(process id)를 제외한 부모 process의 주소 공간, pcb, 자원 등을 그대로 복사한다.
  • 그 후, 그 복제된 곳에 새로운 process를 덮어씌운다.

복제 생성은 fork()라는 system call로 이루어지며, 그 후 덮어 씌우는 과정은 exec()라는 system call로 이루어진다.

fork()

fork()를 통해 새 process를 생성한다. 정확히는 자신을 복사해 새로운 process를 생성하는 꼴이다.

int main(){
	int pid;
	pid = fork();
	if(pid == 0)
		printf("hi, i am a child\n");
	else if(pid > 0)
		printf("hi, i am a parent\n");
}
  • 부모 process에서 위와 같은 함수를 실행한다고 하자.
  • fork()가 호출됨에 따라 os는 자식 process를 만들어줄 것
  • 이 때, 부모의 context를 그대로 복사할 것이다. 경우에 따라 다르지만 pc만 복사할 수도 있다.
    • 위 코드, 데이터, 스택이 모두 복사 혹은 공유되는 것이다.
  • 그 후, 자식은 부모와 똑같이 fork() 이 후 시점부터 실행을 시작한다. 복사되는 시점에 pc는 fork() 다음 부분을 가리켰을 것이기 때문이다.
  • 이 때, 부모와 자식을 구별하기 위해 fork()함수는 경우에 따라 다른 return value를 뱉는다. return value로는 자식의 pid를 뱉는다.
    • 부모의 경우 return value가 양수이다. 자식의 pid는 양수이기 때문이다.
    • 자식의 경우 return value가 0이다. 자식이 없기 때문이다.

그런데 이렇게 fork만쓰면 제어 흐름(코드)이 완전히 같은(물론 조건문으로 분기는 되지만) 프로그램만 존재할 수 밖에 없다. 이를 위해 exec를 쓰는 것이다.

exec()

exec()을 통해 전혀 다른 프로그램을 실행할 수 있다. 실행된 process의 공간에 다른 process를 덮어씌운다.

int main(){
	int pid;
	pid = fork();
	if(pid == 0){
		printf("hi, i am a child. Now i'll run date\n");
		execlp("/bin/data", "/bin/data", (char*)0);
	}	
	else if(pid > 0)
		printf("hi, i am a parent\n");
}
  • fork 후 child는 "hi, i am a child. Now i'll run date\n"를 출력하고 execlp에 의해 /bin/data process로 바뀌게 된다.
  • exec()은 fork() 달리 process를 처음부터 실행시킨다. PC를 유지하지 않는다.
  • exec()을 한번 호출하면, 당연히 본래의 process로 돌아올 수 없다.
  • 꼭 자식을 만들고 자식에서 exec()할 필요는 전혀 없다. fork()없이 사용 가능하다.

2) 종료

  • 마지막 명령을 수행한 후, 프로그램은 exit()을 통해 운영체제에게 이를 알려준다. 자발적인 종료이다.
    • 명시적으로 넣지 않았으면 컴파일러가 main함수 끝에 알아서 넣어주게 된다.
    • 물론 중간에 명시적으로 넣어서 종료도 가능
  • 종료가 될 때는 자식이 부모에게 output data를 보낸다. wait()을 통해 수행한다.
    • → 분명 교수님께서는 이렇게 말씀하셨지만 wait을 아래서 다시 설명할 때는 다르게 설명하셨다. 아마 부모가 자식이 종료될 때 까지 sleep 상태로 감으로써, 자식이 만들어낸 결과를 기다릴 수 있다는 뜻이 아닐까 싶다. 내 추론일 뿐, 확실한 것은 아니다.
  • 부모가 자식을 강제 종료하는 경우도 있다. abort()로 가능
    • 자식이 할당 자원의 한계치를 넘어서는 경우
    • 자식에게 시킬 일이 더 이상 없을 경우
    • 부모 종료(exit)하는 경우
      • 운영체제는 부모가 종료돼야하면 가장 밑 자식부터 종료시키며 그 위의 부모들을 종료시켜나간다.

wait()

부모가 wait()을 호출했을 때 kernel은 자식이 종료될 때 까지 해당 부모를 blocked 상태로 만들어 놓는다. 그 후 자식이 모두 종료되면 ready 상태로 바꾼다.

int main(){
	int pid;
	pid = fork();
	if(pid == 0){
		// some child logic
	}	
	else if(pid > 0){
		// some parent logic
		wait();
	}
	// logic
}
  • wait()가 실행된 순간, 부모는 sleep(blocked)상태가 되고, 자식이 종료된 후에야 ready로 바뀐다.
  • 우리가 쓰는 쉘이 wait()을 쓰는 대표적인 예이다.
    • 쉘에서 특정 프로그램을 실행시킬 수 있는데, 이 경우 쉘에 wait이 걸리고 다른 프로그램을 자식으로 생성하는 것이라고 한다.
    • 그래서 자식이 실행되는 동안은 쉘이 얼어있는 것이다.

exit()

process를 종료시킬 때 호출하는 system call

  • process가 자발적으로 종료될 때 호출된다.
  • 명시적으로 적어주지 않아도 main함수가 return되는 위치에 컴파일러가 알아서 넣어준다.

2. Process간 협력

기본적으로 process는 각자의 주소 공간을 가지고 수행되므로, 다른 process의 수행에 영향을 미치지 않는다.

그러나 필요한 경우 process들끼리 필요한 정보는 나누면서 일을 하게 만들고 싶을 수도 있다.(구체적인 예를 들어보고 싶은데 지식이 없다 ㅠㅠ)

이런 협력 메커니즘을 interprocess communication, IPC라고 한다.

IPC

process간 협력에는 크게 2가지 방법이 있다.

Message Passing

서로 다른 process간에 메세지를 전달

  • 커널이 중간에서 전달해준다.
    • 자기 주소 공간에만 간섭 가능하고 공유 변수가 있는 것도 아니기 때문이다.

여기도 크게 두가지 방법이 있다. 하지만 둘다 커널이 중간에서 전달해준다는 것은 변함없는 사실

Direct Communication

  • 통신하려는 프로세스의 이름을 명시적으로 표기한다

Indirect Communication

  • mailbox(또는 port)를 통해 메시지를 간접 전달한다
  • 메세지를 꺼내볼 수 있는 대상이 여러개가 될 수 있는 것이다

Shared Memory

일부 주소 공간을 두 process가 공유하게 만드는 방법

그림의 우측을 보면 빨간 상자와 노란 상자가 겹치는 부분이 있다. 저 부분이 shared memory

조금 더 정확히 말해보자. process들은 생성될 때 자신만의 메모리 공간을 갖게 된다. virtual memory를 떠올려라! 이 virtual memory를 physical memory에 mapping할 때, 저렇게 겹치게 mapping함으로써 shared memory를 확보한다.

이런걸 하려면 kernel에게 shared memory를 한다는 system call을 해야한다고 한다. 그러나 kernel이 한번 이어주면 그 후로는 알아서 신중하게 해야한다.

profile
응애

0개의 댓글