4. Process Management 1, 2

운영체제 – 이화여대 KOWC 공개강의

 

Process Management Part 1

프로세스 생성.
원칙적으로는 부모 프로세스와 자식 프로세스가 자원을 공유하지는 않는다. 부모가 자식을 하나 낳으면 별도의 프로세스가 되기 때문에 경쟁하는 사이가 된다. 각자 CPU를 얻으려고 경쟁하고 메모리를 더 많이 얻으려고 경쟁하게 되지 보통은 공유하는 모델보다는 공유하지 않는 모델이 일반적이다.

자식은 부모의 공간(자원들)을 그대로 복제(fork())한 다음에 그곳에 새로운 프로그램을 덮어씌운다.(exec()) 이것이 프로세스 생성의 2단계이다. 여기서 복사는 프로세스의 문맥(code, data, stack. cpu 문맥(pc, register)을 그대로 복사하는 것이다. 이것은 독립적이기 때문에 복제만 하고 덮어씌우지 않을 수도 있고, 복제하지 않고 exec()을 하면 새로운 프로세스로 바뀔 수도 있다. 프로세스 생성은 사용자 프로그램이 하는 것이 아니라 운영체제를 통해서만 생성이 가능하다.(fork, exec 시스템콜) 운영체제에게 자식을 낳아달라고 부탁을해서 처리를 한다.

프로그램의 세계에서는 항상 자식 프로그램이 먼저 죽고(exit) 부모가 종료된다. 프로세스가 종료될 때는 자식이 부모에게 data를 전달한다.(wait 시스템 콜을 통해)

 

Process Management Part 2

부모 프로세스를 카피해서 프로세스의 문맥이 만들어지는 것이 원칙이지만 리눅스나 좀 더 효율적인 운영체제에서는 일단 카피하지 않고 자식이 부모의 주소공간을 공유하고 있다. Program Counter만 하나 복사해서 똑같은 위치를 가리키고 있게 된다. 이런 상태로 실행을 하다가 부모와 내용이 달라지면 그때서야 부모의 메모리 공간 일부를 카피해서 자식이 갖게 된다. 이러한 기법을 Copy on write(COW. write가 발생했을 때 그때 copy하겠다.) 기법이라고 한다. write(원래 있던 내용이 바뀌는것) 할 때 그것을 카피해서 새로운 것을 만들고 그 이전까지는 내용이 똑같기 때문에 부모의 것을 그대로 공유한다.

Write가 발생해서 부모의 것을 카피할 때 그것을 통째로 카피하는 것이 아니다. code, data, stack이 물리 메모리에 통째로 올라가는 것이 아니라 잘게 쪼개져서 필요한 부분만 물리적 메모리에 올라가기 때문에 그 잘게 쪼개진 부분에 대해 write가 발생하면 그 부분만 카피해서 부모의 것과 공유하지 않는 식으로 관리가 이루어진다.

 

fork()시스템 콜. 위 사진의 코드는 fork()와 exec()의 실제 내부 코드(운영체제의 코드)가 아니라 포크를 사용해서 사용자 프로그램이 프로세스를 만드는 코드이다.

위 그림을 보면 부모 프로세스(왼쪽)의 fork() 함수 호출을 통해 새로운 자식 프로세스가 만들어졌다(오른쪽) 그러면 자식 프로세스는 새로운 프로세스인데 프로세스가 시작되고 main() 함수부터 시작하는 것이 아니라 for()를 수행한 이후 시점부터 자식 프로세스는 실행을 하게 된다. 그 이유는 부모의 프로세스 문맥을 그대로 복제하기 때문에 문맥 안의 Program counter가 fork()함수 코드 이후의 인스트럭션을 가르키고 있기 때문이다.

문제는 복제된 프로세스가 자기는 복제된 것이 아니라 원본 프로세스라고 주장을 할 수 있다. 부모 프로세스를 복제본 취급을 하는 혼란스러운 상황이 발생할 수 있다. 그리고 fork를 하게 되면 세상의 모든 프로그램은 모두 똑같은 프로그램이 될 것 같은 문제가 있다. 이러한 문제를 막기 위해 운영체제는 자식과 부모를 구분지어 준다. fork() 함수의 실행결과 리턴값을 다르게 한다. 부모의 프로세스는 리턴값이 양수값이 되고 자식 프로세스는 0을 받는다. 이렇게 해서 부모와 자식에게 다른 일을 시킬 수 있게 된다.

exec() 시스템 콜은 프로세스를 완전히 새로운 프로세스로 태어나게 해주는 역할을 한다.

꼭 fork()를 하고 exec()을 해야 하는 것은 아니다. 위 사진의 코드에서는 execlp() 아래 부분 print(“\n Hello, I am parent\n’); 은 영원히 실행되지 않는 부분이다. 한번 exec()을 하고 나면 다시 원래의 프로세스로 절대 되돌아올수 없기 때문이다.

wait() 시스템 콜은 자식 프로세스를 만든 다음에 콜 하게 된다. 그러면 부모 프로세스는 CPU를 얻지 못하고 block상태가 된다. 그러다 자식프로세스가 종료가 되면 그때 wait() 코드 다음 부분을 실행할 수 있게 된다.

 

프로세스간의 정보를 주고 받을 수 있는 방법(IPC). 프로세스끼리 직접 정보를 주고 받을 수 있는 방법은 없다. 커널을 통해서만 가능하다.

원칙적으로 프로세스는 자기의 주소공간만 볼 수 있는데 Shared memory는 일부 주소 공간을 두 프로세스에서 공유가 가능하다. Shared memory도 프로세스끼리 당장 주소 공간을 공유할 수 있는 것이 아니라 커널에게 Shared memory를 사용한다는 시스템 콜을 통해 매핑을 해놓고 사용하게 된다.

댓글 남기기

Close Menu