프로세스 생성
- 프로세스는 실행 중인 프로그램으로, 프로그램을 실행하기 위해서는 디스크 상에 존재하는 프로그램을 메모리에 탑재해야 한다.
과정
1. 운영체제는코드와 정적 데이터를 메모리로 탑재한다.
- 프로그램은 디스크에 특정 실행 파일 형식으로 존재하는데, 코드와 정적 데이터를 메모리에 탑재하기 위해서 운영체제는 디스크의 해당 바이트를 읽어서 메모리의 어딘가에 저장한다.
- 초기 운영체제들은 프로그램 실행 전에 코드와 데이터를 모두 메모리에 탑재하였으나, 현대의 운영체제는 프로그램을 실행하면서 코드나 데이터가 필요할 때 필요한 부분만 메모리에 탑재한다.
2. 스택과 힙을 생성하고 초기화한다.
- 코드와 정적 데이터가 메모리로 탑재된 후, 프로세스를 실행시키기 전에 운영체제는 일정량의 메모리를 프로그램의 의 스택 용도로 할당하고 main() 함수의 argc와 argv 벡터 인자로 초기화한다.
- 또한 운영체제는 프로그램의 힙을 위한 메모리 영역을 할당하는데, 힙에는 동적으로 할당된 데이터를 저장한다.
- 프로그램은 malloc()을 호출하여 필요한 공간을 요청하고 free()를 호출하여 사용했던 공간을 반환하여 다른 프로그램이 사용할 수 있도록 한다.
- 프로그램이 실행되면 malloc() 라이브러리 API를 호출하여 메모리를 요청하고, 운영체제가 이를 충족하도록 메모리를 할당한다.
3. 입출력과 관계된 초기화 작업을 수행한다.
- 입출력과 셋업과 관계된 작업까지 마치게 되면 프로그램 실행을 위한 준비가 끝나게 된다.
- 이후 프로그램의 시작 지점인 main() 루틴으로 분기함으로써, 운영체제는 CPU를 새로 생성된 프로세스에게 넘기게 되고 프로그램이 실행된다.
프로세스 API
- UNIX는 프로세스를 생성하기 위하여 fork()와 exec() 시스템 호출을 사용한다.
- wait()은 프로세스가 자신이 생성한 프로세스가 종료되기를 기다리기 원할 때 사용한다.
fork()
- fork() 시스템 호출은 실행 중인 프로세스를 복사하는 함수로, 이때 실행하던 프로세스는 부모 프로세스, 새로 생긴 프로세스는 자식 프로세스로서 부모-자식 관계가 된다.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid = fork();
if (pid < 0) {
printf("Error");
return 1;
} else if (pid == 0) {
printf("Child");
} else {
printf("Parent");
}
return 0;
}
- PID는 프로세스 식별자로, UNIX 시스템에서 PID는 프로세스의 실행이나 중단과 같이 특정 프로세스를 대상으로 작업을 해야 할 경우 프로세스를 지칭하기 위해 사용된다.
- fork()로부터 부모 프로세스는 생성된 자식 프로세스의 PID를 반환받고, 자식 프로세스는 0을 반환받는다.
- 이러한 반환 값의 차이로 인해 부모와 자식 프로세스가 서로 다른 코드를 실행하는 프로그램을 작성할 수 있다.
주의할 점
- fork() 이전에 파일을 열거나 변수를 선언하면 이것이 모두 자식 프로세스에 상속된다.
- 새로 생성된 자식 프로세스는 별도의 주소 공간, 레지스터, PC 값을 갖는다.
- 부모 프로세스와 자식 프로세스는 서로 독립적이기 때문에, 둘 중 어느 것이 먼저 실행될지는 알 수 없다.
wait()
- 부모 프로세스가 자식 프로세스의 종료를 기다려야 하는 경우, wait() 시스템 호출을 사용한다.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid t pid;
/* fork a child process */
pid = fork();
if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}
else if (pid == 0) { /* child process */
execlp("/bin/ls","ls",NULL);
}
else { /* parent process */
/* parent will wait for the child to complete */
wait(NULL);
printf("Child Complete");
}
return 0;
}
- 부모 프로세스는 wait() 시스템 콜을 호출하여 자식 프로세스 종료 시점까지 자신의 실행을 잠시 중지시키고, 자식 프로세스가 종료되면 wait()이 리턴한다.
exec()
- exec() 시스템 호출은 이미 만들어진 프로세스의 구조를 재활용하는 것으로, 자기 자신이 아닌 다른 프로그램을 실행해야 할 때 사용한다.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid t pid;
/* fork a child process */
pid = fork();
if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
return 1;
}
else if (pid == 0) { /* child process */
execlp("/bin/ls","ls",NULL);
}
else { /* parent process */
/* parent will wait for the child to complete */
wait(NULL);
printf("Child Complete");
}
return 0;
}
- exec()를 호출하면 코드 영역에 있는 기존의 내용을 지우고 새로운 코드로 덮어 씌워지며, 데이터 영역이 새로운 변수로 채워지고 힙과 스택 영역이 리셋된다.
- 프로세스 제어 블록의 내용 중 프로세스 구분자, 부모 프로세스 구분자, 자식 프로세스 구분자, 메모리 관련 사항 등은 변하지 않지만 PC 값을 비롯한 각종 레지스터와 사용한 파일 정보가 모두 리셋된다.
'Computer Science > Operating System' 카테고리의 다른 글
CPU 스케줄링 (0) | 2022.03.15 |
---|---|
프로세스 제어 블록과 문맥교환 (0) | 2022.03.15 |
프로세스와 스레드 (0) | 2022.03.15 |
병렬 처리 (0) | 2022.03.15 |
컴퓨터 성능 향상 기술 (0) | 2022.03.14 |