이번시간에는 운영체제 프로세스 생성과 관련된 exec(), fork() 에 대해 알아보겠습니다
먼저 프로세스와 스레드에 대해 간략하게 살펴보면
프로세스는 CPU로 부터 메모리 공간을 독립적으로 할당받은 실행중인 프로그램이라고 할 수 있습니다 각 프로세스는 독립된 메모리 공간을 가지기 때문에, 하나의 프로세스가 오류로 인해 종료되더라도 다른 프로세스에는 영향을 주지 않는 특징이 있으며 최소 하나의 스레드를 가지며 여러 스레드를 두어 병렬작업을 수행할 수 있습니다
즉, 독립적인 메모리 공간을 가지는 하나의 프로그램 인스턴스 입니다
프로세스는 메모리 영역을 가진다고 하였는데 이는 아래와 같은 구조를 가지고 있습니다
STACK - 함수 처리 영역
HEAP - 동적 메모리
DATA - 초기값이 있는 변수 값
BSS - 초기값이 없는 변수(전역 변수와 정적 변수) 값
TEXT - Code, 실행 코드 이미지
C, Java 와 같은 몇몇 프로그래밍 언어에서 전역(global), 정적(static) 변수는 명시적으로 초기화 되지 않을 경우 0, null(Java) 등 으로 기본값으로 자동 초기화 됩니다. 이렇게 명시적으로 초기화 되지 않고 기본값으로 초기화된 변수들은 프로그램에 저장하고 있을 필요가 없기 때문에 DATA 영역과 따로 분리하여 BSS 영역에서 관리하게 됩니다
위의 HEAP 영역과 STACK 영역이 만나게 되면 발생
예시로 너무 많은 재귀함수 호출을 하게 되면 STACK 영역이 계속 쌓이게 되어 HEAP 영역과 만나는 경우
스레드는 프로그램이 실행될때 가장 기본이 되는 실행 흐름 단위 입니다
프로세스 내의 여러 스레드가 존재한다면 공유 영역을 가진다는 특징이 있으며 이 공유 영역에서 생가는 공유 자원을 관리하기 위해 임계구역, 메모리 베리어 라는 개념이 나오게 됩니다
이제 프로세스와 스레드에 대해 간략히 알아봤으니 fork()와 exec()에 대해 알아보겠습니다
쉽게 얘기하면 fork()는 프로세스를 복사하는 것이며 exec()는 덮어씌우는 것입니다
fork()는 기존 프로세스를 유지하며 이와 동일한 내용을 가진 다른(자식) 프로세스를 생성하기 위해 사용합니다
즉, 새로 fork()를 통해 생성된 프로세스는 부모와 다른 PID를 가진 프로세스이며 새로운 별도의 프로세스 주소 공간(동일한 TEXT, DATA)을 만들고 부모 프로세스 공간의 데이터를 모두 복사합니다 이 과정에서 COW(Copy On Write) 형태로 동작하게 됩니다
COW 란?
fork()를 함으로써 가지는 장점은 다음과 같습니다
위와 같은 이유가 어떻게 생기는지 컴퓨터 구조 관점에서 fork()가 발생하는 과정을 살펴보겠습니다
fork() 하여 프로세스를 생성하게 되면 부모 프로세스를 복사할 때 가상 메모리 공간의 페이지 테이블을 자식 프로세스와 공유하여 부모 프로세스와 자식 프로세스가 동일한 물리 메모리를 참조하도록 합니다
페이지 테이블은 가상 주소를 물리 주소로 변환(mapping)하는데 사용되는 데이터 구조이며 실제 물리 메모리를 효율적으로 사용하기 위해 사용됩니다
부모 프로세스와 자식 프로세스는 코드, 데이터, 힙, 스택 등 메모리 영역을 공유하게 되므로 메모리를 복사하여 새로 할당한 것이 아니게 되며 새로운 프로세스를 생성하는데 필요한 초기화나 메모리 할당 비용인 자원과 시간을 줄일 수 있게 되므로 프로세스 생성 속도가 향상되게 됩니다
부모 프로세스의 상태와 자원들을 상속받아 네트워크, 메모리 영역 등을 그대로 사용할 수 있게 되므로 추가적은 작업 없이 자원을 공유할 수 있습니다
부모, 자식이 PID로 연결이 되어 있어 자식 프로세스가 종료되면 부모 프로세스가 사용하던 자원을 정리할 수 있습니다
exec() 는 새로운 프로세스를 생성하되 내용을 전부 변경하며 새로운 메모리를 할당하지 않고 동일한 exec()를 호출한 프로세스가 아닌 호출 된 프로세스만 메모리에 남기게 됩니다
exec() 를 사용하면 좋은 점은 새로 프로세스를 생성하는 과정이 생략되므로 동일한 구조를 가진 새로운 프로세스를 사용할때 쓰는 함수이다
참고