강의 링크 : https://core.ewha.ac.kr/publicview/C0101020140325134428879622?vmode=f
부모와 자식 관계로 트리를 형성한다.
프로세스는 자원을 필요하여 운영체제
에게로부터 자원을 받고 부모
와 공유한다.
자원의 공유 형태에 따른 모델
1. 부모와 자식이 모든 자원을 공유하는 모델
2. 일부를 공유하는 모델
3. 전혀 공유하지 않는 모델
원칙적으로는 공유하지 않고 부모 자식 관계라고 하더라도 생성된 순간부턴 서로 독립적인 프로세스가 된다.
강의에서는 자원을 얻기 위해 경쟁한다라고 까지 표현하였다.
수행
1. 부모와 자식이 공존하며 수행되는 모델
자식은 부모의 공간을 복사한다.
공간을 복사한다는 것은 주소 공간 (Address space
) 을 복사한다.
공간만 복사하는 것이 아닌 그 안에 존재하는 데이터와 자원도 복사한다. (PCB와 같은)
그리고 복사된 그 공간에 새로운 프로그램을 올린다.
부모의 주소 공간을 복사하는 시스템 콜을 fork()
라고 하고
복사 된 주소 공간에 새로운 프로그램을 올리는 시스템 콜을 exec()
라고 한다.
두 행위는 독립적이기에 복사만 하고 덮어씌우지 않는 경우도 존재한다. 꼭 같이 일어나야 할 필요는 없다.
물론 복사 하지않고 실행만 하여 덮어씌우기만 하기도 한다.
좌측에서 프로그램 카운터가 fork()
를 만나는 순간 우측에 자식 프로세스가 생성된다.
좌측의 코드를 실행하던 프로그램 카운터는 fork()
이후의 코드를 따라 실행한다.
우측에 프로세스가 새로 생성되면 좌측과 독립적으로 우측의 코드를 실행시키는 프로그램 카운터
가 생성된다.
이후 프로그램 카운터는 컨텍스트에 따라 fork()
가 실행된 이후의 코드를 시행한다.
이렇게 fork()
를 통해 부모 프로세스를 복제한다.
의문이 든다. 부모 프로세스를 그대로 복제하면 결국 복제해도 동일한 활동을 하는거 아닌가 ?
아닙니다
부모프로세스의 경우 fork
함수의 반환값이 양수이다. 그럼으로 부모 프로세스는 fork
이후 조건문에 따라 부모 프로세스의 코드를 실행해 나가면 된다.
자식프로세스의 경우 fork
함수의 반환값이 0이거나 음수이다. 그럼으로 자식 프로세스는 fork
이후 조건문에 따라 자식 프로세스의 코드를 실행해 나가면 된다.
exec()
시스템콜fork
를 통해 새로운 자식 프로세스를 생성하였을 때
부모 프로세스와 독립적인 다른 기술을 가진 프로세스를 기대할 것이다.
그렇기 때문에 부모 프로세스의 코드와 다른 로직인 코드를 필요로 하는데 그런 코드를 불러오는
코드가 exec()
이다.
부모로부터 받은 소스코드를 내가 원하는 코드로 덮어 씌우는 것이다.
그로인해 exec()
시스템콜 이후엔 다시 부모 프로세스의 로직으로 돌아올 수 없다.
그로 인해 exec()
이후에 존재하는 부모 프로세스의 코드들은 실행되지 않는다.
프로세스에 관해 이야기 할 때
자식 프로세스는 부모 프로세스의 주소 공간을 복사하고, 주소 공간 뿐이 아니라 안에 존재하는 자원들을 복사한다고 하였다.
주소 공간을 복사한다는 것의 의미
부모의 주소 공간에는 부모 프로세스의
code , stack , data
가 존재하고 있다. 이 때 자식 프로세스가 생성 될 때 자식 프로세스도 부모 프로세스의code , stack , data
가 존쟇는 주소를 가리키고 있다.
이것이 주소 공간을 복사한다는 것의 의미이다.
부모 프로세스와 자식 프로세스는 독립적인 개체로서 같은 주소 공간을 가리키고만 있을 뿐 CPU
자원을 독립적으로 할당 받아 독립적인 포인터
들로 프로그램을 실행 할 수 있다.
이 때 자식 프로세스가 부모 주소 공간 내에 존재하는 code , stack , data
등을 조회만 할 때에는 같은 공간을 가리키고 있다.
하지만 자식 프로세스에서 write
하는 순간 부모 프로세스의 주소 공간에 정보를 덮어 씌울 수 없기 때문에
자원을 복사하여 새로운 메모리 공간에 할당한다.
정리하면 다음과 같다.
공유 자원
여러 프로세스나 스레드가 동일하 데이터를 공유 하고 있을 때, 이 데이터는 메모리 상에서 하나의 원본이 담긴 주소를 가리키게 된다.
복사하지 않고 참조
여러 프로세스나 스레드가 동일한 원본을 참조하고 있다. 여러 프로세스 별 복사된 동일한 데이터가 메모리에 할당되면 비효율적이나, 같은 주소 공간을 가리키고 있음으로 메모리를 절약 할 수 있다.
변경 시 복사
만약 이 중 어떤 프로세스나 스레드가 데이터를 변경하고자 한다면, 가리키고 있던 원본 공간의 데이터들을 복사하여 새로운 메모리에 할당 한 후 새로 할당한 영역을 가리킨 후 변경한다 .이로서 데이터를 공유하고 있는 다른 프로세스나 스레드에는 영향을 미치지 않는다.
추후 메모리 관리 파트에서 설명하겠지만 모든 곳을 복사하는 것이 아니라 수정된 부분만 복사하여 새로 할당한다.
경쟁 조건 회피
변경 시 복사를 통해 변경 작업이 원본과 독립적으로 이뤄지게 하여 경쟁 조건을 회피 할 수 있다.
프로세스가 마지막 명령을 수행한 후 OS 에게 종료 되었음을 알리는 시스템 콜 exit
프로세스가 종료 된 후 일어나는 일
- 자식이 부모에게 output data 를 보낸다. (via
wait
)- 프로세스의 각종 자원들이 운영체제에 반납된다.
wait()
시스템콜프로세스 A가 wait
시스템콜을 사용하면 프로세스 A의 자식 프로세스의 실행이 종료 될 때 까지 커널
은 A 프로세스
를 block
상태로 만든다.
이후 자식 프로세스가 종료되면 A 프로세스
를 ready
상태로 만든다.
exit()
시스템콜주로 자발적으로 종료되며 exit()
함수를 호출 할 수도 있고 자동으로 컨텍스트 상 마지막 문맥 이후에 종료된다.
혹은 비자발적으로 부모 프로세스가 자식 프로세스를 강제로 종료 시킬 수도 있다.
부모 프로세스가 자식의 수행을 종료 시키는 시스템 콜 (abort
)
비자발적으로 종료시키는 경우
현실 세계에선 자식이 부모보다 오래 살지만 프로세스는 부모 프로세스가 자식 프로세스보다 항상 오래 산다.
정리
System Call Description Example fork() Create a new process by duplicating the calling process. pid_t child_pid = fork();
exec() Replace the current process image with a new one. execl("/bin/ls", "ls", "-l", NULL);
wait() Suspend execution of the calling process until one of its child processes exits. pid_t terminated_child = wait(NULL);
exit() Terminate the calling process and return an exit status to the parent process. exit(0);
프로세스는 매우 독립적이다
프로세스 별로 개별적인 프로그램 카운터를 얻으니까 그렇다.
경우에 따라서 프로세스는 서로 협력을 해야 효율적일 때가 있다.
프로세스 간 협력하는 메커니즘을 IPC (Interprocess Communication)
이라고 한다.
message passing
: 커널을 통해 메시지를 전달하는 방법
프로세스 사이에서 공유 변수 shared variable
을 일체 사용하지 않고 통신하는 시스템을 말한다.
이 때 프로세스 간 직접적 통신은 불가능하기 때문에 메시지를 전달하는 주체는 kernel
이다.
Direct Communication
통신하려는 프로세스의 이름을 프로세스에서 명시적으로 표시한다.
이후 커널이 해당 메시지를 이름 적힌 프로세스에게 전달한다.
메시지를 전달하는 방법
send(목적지 프로세스 , 메시지)
,receiver (소스 프로세스 , 메시지)
와 같은 형태로 메시지를 직접적으로 주고 받는다.
물론 메시지를 주고 받게 하는 주체는 커널이다.
Indirect Communication
mailbox
또는 port
를 통해 메시지를 간접 전달한다.
중간 매개체를 메시지 전달 지점으로 사용하며 해당 매개체는 일종의 버퍼 역할을 한다. 메시지를 받을 준비가 된 프로세스는 메시지를 수신 할 수 있다.
메시지를 전달하는 방법
A프로세스에서 B프로세스로 메시지를 전달하는 과정이라고 해보자
1. A프로세스는 시스템콜send()
를 호출하여 메시지를 B 프로세스에게 전달하고자 한다.
2. 시스템 콜은 사용자 공간의 데이터를 커널로 복사한다. (보내고자 하는 메시지를 커널에 복사)
3. 커널에서 메시지 큐 또는 버퍼에 저장한다.
4. 프로세스 B 는 시스템콜receive()
를 호출하여 커널 메시지 큐 또는 버퍼에 저장되어 있는 메시지를 꺼내 확인한다.
shared memory
공유 변수를 이용한 협력 과정
- 프로세스들은 공유 변수로 사용할 동일한 메모리 영역을 할당 받는다.
- 프로세스들은 공유 변수에 접근하여 데이터를 읽거나 쓸 수 있다.
- 프로세스가 동시에 공유 변수에 접근 할 때 동기화가 필요하다. (공유 변수는 한 번에 한 프로세스만 접근 가능하다)
- 하나의 프로세스가 공유 변수에 데이터를 쓰면 , 다른 프로세스가 해당 데이터를 읽을 수 있다.
thread
는 주소공간 자체를 서로 공유하고 있기 때문에 협력이 쉽게 가능하다.