Win32 API | UNIX |
---|---|
createProcess() | fork(), exec() |
ExitProcess() | exit() |
waitForSingleObject() | wait() |
가상메모리 애기를 하면 항상 따라다니는 놈이 process다. process단위로 OS가 접근제어를 하는데 파일이나 여러 자원에 대한 접근제어를 의미한다. 어떤 프로세스에 대해 OS가 접근허가를 하면 그 권한을 쓰레드들은 다 공유하게 된다. 그리고 가상메모리라는 공간은 여러 프로세스가 실행되고 있어도 이 가상 메모리 공간은 독립적인 공간으로서 보장을 받는다. 그리고 그 스레드들이 가상 메모리를 사용이 가능하다.
우리가 OS내에서 새로운 프로세스를 생성해야 한다고 보면 새로운 프로세스가 생성이 될때마다 독립적인 가상메모리 공간을 가져야 하는데 가상메모리 공간을 프로세스마다 OS가 할당해줘야 한다.
만약 어떤 프로세스가 있다고 해보자. 이 프로세스는 자기만의 고유의 PCB도 갖고 가상메모리 공간을 갖는다. 이 프로세스가 새로운 프로세스를 생성을 하면 이 2개의 프로세스의 관계가 설정이 되는데 기존 프로세스를 부모 프로세스, 새로 생긴 프로세스를 자식 프로세스라고 한다.
그런데 신기한 것이 위의 표를 보면 윈도우 경우 프로세스 생성 함수가 createProcess로 하나지만 UNIX는 fork(), exec() 2개가 존재한다. 왜 그런것일까?
프로세스가 생성이 되면 이 프로세스를 쓰기 위해서 메모리(text영역에 존재하는 실행코드)로 복사 후 이 실행코드에 대한 PCB가 생성이 되고, 가상메모리 공간이 확보가 된다. 이 가상메모리 공간 속에 heap영역, Stack영역, Data라는 정적 static 영역등이 들어가져 있다. 즉, 우리가 느끼기에 프로세스 생성 과정이 엄청 복잡하게 느껴진다. 그렇게 못 느끼신 분들을 위해 좀 더 설명해보겠다.
우리가 C/C++을 배워서 실행파일을 만들면 Unix계열은 a.out으로 Window는 a.exe로 떨어진다. 이때, 윈도우 계열 실행파일은 PE format을 하고 있고 유닉스 계열은 ELF format을 하고 있다. 이때, PE format과 ELF format은 서로 비슷하지만 PE format이 좀 더 복잡하다. 아래 그림을 보면 이해될 것이다.
즉, 프로세스가 생성이 되면 메모리에 복사가 되고 거기서 PE형식을 뜯어서 분해한 다음에 거기서 text section이라는데 가서 코드를 뜯어서 올리는데 그 전에 PCB 생성이 되고 가상 메모리 공간 할당을 한다. 이 과정이 굉장히 복잡하고 오래 걸려서 이 작업을 효율적으로 하다보니 Window에서는 createProcess() 하나지만 Linux에서는 fork(), exec() 2개로 구성이 된다. 그럼 본론으로 가서 왜 2개일까?
이 복잡한 과정을 효율적으로 처리하기 위해서이긴 한데, fork()는 프로그램이 실행되고 프로세스가 생성하고 가상메모리공간 + PCB 생성 + OS처리를 하는 이 과정을 자식 프로세스에게 그대로 복사해서 레지스터 상태정보, 실행코드만 리셋시켜주는 형태인데 이때 만일 부모 프로세스가 종료된다고 하면 너무 비효율적인 작업만 하고 종료되는게 안타까울 것이다. 이에 반해 exec()는 자식 프로세스를 만들 필요없이 종료될 프로세스에 레지스터 상태정보, 코드만 종료될 프로세스에 넣어주는 형태이다.
이렇게 보면 exec()가 좀 더 효율적이다. 그래서 되도록 exec()를 사용하는것이 좋다.
좀 더 쉽게 애기해보면 다음과 같다.
fork() 시스템 호출은 새로운 프로세스를 위한 메모리를 할당한다는 것이다. 그리고 fork()를 호출한 프로세스를 새로운 공간으로 전부 복사하게 되고, 원래 프로세스는 원래 프로세스대로 작업을 실행하고 fork()를 이용해서 생성된 프로세스도 그 나름대로 fork() 시스템 콜이 수행된 라인의 다음 라인부터 실행이 된다. (새로 생성된 프로세스는 원래의 프로세스와 똑같은 코드를 가지고 있다.)
반면, exec()는 fork()처럼 새로운 프로세스를 위한 메모리를 할당하지 않고, exec()를 호출한 프로세스가 아닌 exec()에 의해 호출된 프로세스만 메모리에 남게 된다.
간단히 정리하면, fork()의 결과는 프로세스가 하나 더 생기는 것이다.( = 프로세스 id- PID 가 완전히 다른 또 하나의 프로세스가 생기는 것). 반면 exec()실행의 결과로 생성되는 새로운 프로세스는 없고, exec()를 호출한 프로세스의 PID가 그대로 새로운 프로세스에 적용이 되며, exec()를 호출한 프로세스는 새로운 프로세스에 의해 덮어 쓰여지게 된다.
그리고 wait와 waitForSingleObject는 어떤 하나의 Object를 기다린다는 말로 가끔 프로세스가 종료될때까지 기다려야 하는 상황에 사용한다. 이 함수들을 사용하면 프로세스 상태가 대기상태로 빠지게 된다.