-
- 프로세스
프로세스란, 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램을 의미하며,
프로그램 자체와 프로그램의 상태가 메모리 상에서 실행되는 작업 단위를 의미하기도 한다.
(프로그램은 하드 디스크에 저장되어 있는 일련의 명령어(실행 코드)의 모음을 말한다)
프로세스는 메모리에 적재된 프로그램의 명령어를 CPU가 실행함으로써 동작되며 한 프로세스는 한 프로그램 수행에 대응된다.
즉, 프로그램 하나를 여러 번 구동하면, 여러개의 프로세스가 실행된다.
프로그램이 메모리에 적재될 때, 가상 메모리는 공간을 나누에 데이터를 효율적으로 관리한다.
Stack: 호출한 함수가 리턴될 주소를 임시로 저장하는 공간으로, 컴파일시 크기가 결정되기 때문에 크기를 무한정으로 늘릴 수 없다.
Heap: 프로그래머가 필요할 때 마다 사용하는 메모리 영역으로, 런타임에 크기가 결정된다.
Data: 전역변수 등 프로그램이 사용하는 공간으로, 읽고 쓰기가 가능하다.
Text: 프로그램 코드가 기계어로 변환되어 저장된 공간으로, 읽기만 가능하다.
- 프로세스의 상태
생성(Create): 프로세스가 생성되는 상태로 아직 메모리 공간도 할당받지 못한 상태이다.
준비(Ready): CPU할당을 제외한, 메모리 공간 할당 등 코드를 실행하기 위한 모든 준비를 끝마친 상태이다.
실행(Running): 프로세스가 CPU를 차지하여 명령어들이 실행 중인 상태이다.
대기(Waiting): 프로세스가 입출력 완료, 시그널 수신 등 어떤 사건을 대기하고 있는 상태로,보류(Block)상태라고 불리기도 한다.
완료(Terminated): 프로세스의 실행이 종료된 상태이지만, 프로세스와 관련된 자료구조를
운영체제가 아직 완전히 정리되지 못한 상태이다.
- 시분할
프로세스 하나가 CPU를 독점하면 멀티 테스킹 구현이 불가하다.
따라서 각 프로세스는 CPU의 시간 자원을 할당하고, 교대로 반복 수행되는데, 이 것을 시분할 이라고 한다.
다중 작업 시스템에서 프로세스가 선점하여 실행할 수 있는 시간을 시간 슬라이스(Quantum) 라고 부른다 (보통 50ms)
시분할을 통해서 프로세스들이 병렬 처리되는 듯한 착각을 주지만, 사실은 작은 시간 슬라이스에 의해 전환되며 처리되는 것이다.
따라서, 사용자의 성능 저하 문제, 보안 문제, 프로세스간 통신 비용의 증가 문제가 생길 수 있다.
- 컨텍스트 스위치
시분할 또는 인터럽트나 시스템 콜에 의해 기존 프로세스가다음 프로세스로CPU점유를 바꿀 때,
다음 프로세스를 수행하도록 새로운 프로세스의 상태 또는 레지스터를 교체하는 작업을 컨텍스트 스위치 라고 부른다.
컨텍스트 스위치로 인해 실행이 중단된 이후, 스케줄러에 의해 다시 실행될 때 필요한 프로세스와 그에 대한 정보를
프로세스 컨텍스트라고 부르며, 프로세스의 Process Context Block에 저장된다.
프로세스 컨텍스트에는 다음의 내용들이 포함되어 있다
- 프로세스 상태
- 프로세스 카운터
- 레지스터
- 프로세스 번호
참고로, UNIX 계열 운영체제에는 각 프로세스에 음이 아닌 정수를 부여하는데 이를 프로세스 번호 라고한다.
프로세스가 종료되면, 해당 프로세스 번호는 재 사용이 가능하다.
또한, 각 프로세스는 고유한 프로세스 번호를 갖는데, 0 과 1은 특수한 프로세스를 위하여 부여하지 않는다.
pid 0: 스와퍼 프로세스(swapper process)
pid 1: 초기화 프로세스(init process)
- 프로세스 함수
1. pid_t getpid(void);
현재 프로세스의 pid를 반환한다.
2. pid_t getppid(void);
현재 프로세스의 부모 프로세스 pid를 반환한다.
3. void exit(int status);
현재 프로세스를 정상 종료하며 status & 0377 값을 부모 프로세스에게 전달한다.
4. pid_t fork(void);
현재 프로세스의 이미지를 메모리 영역에 복사한 후 자식 프로세스를 만든다.
부모 프로세스에겐 자식 프로세스의 pid를 반환하고, 자식 프로세스에겐 0을 반환한다.
5. pid_t wait(int *status);
자식 프로세스가 종료될 때 까지 부모 프로세스를 대기시킨다. status에 자식 프로세스의 종료 상태를 저장한다.
6. pid_t waitpid(pid_t pid, int *status, int options);
특정 자식 프로세스(pid)가 종료될 때 까지 부모 프로세스를 대기시킨다.
int status = 0; pid_t pid = fork(); if (pid == -1) { exit(-1); // fork error! } else if (pid == 0) { printf("Child pid: %d\n", getpid()): printf("My Parent pid: %d\n", getppid()); } else { printf("I'm Parent, my child pid: %d\n", pid); wait(&status); printf("My child exited with: %d\n", WEXITSTATUS(status)); }
- 프로그램 실행 함수
1. int execl(const char *path, const char* arg, ...);
2. int execv(const char *path, char const *argv[]);
3. int execve(const char *path, char const *argv[], char const *envp[]);
4. int execvp(const char *name, char const *argv[]);
현재 프로세스의 이미지를 path 경로의 프로그램으로 덮어 쓴다.
1 번은 arg로 명령행 인자를 전달하며 마지막은 꼭 NULL 이어야 한다.
2 번은 argv로 명령행 인자를 저장한 배열을 전달하는데, 배열의 마지막 요소는 꼭 NULL 이어야 한다.
3 번은 2번과 동일한데, 마지막에 환경 변수를 저장한 배열도 함께 전달한다. (환경 변수를 저장한 배열의 마지막 요소도 NULL)
4 번도 2번과 동일한데, path 경로의 프로그램을 실행하는 것이 아닌, 실행 파일의 이름을 입력한다.
int status = 0; char args[] = { "ls", "-a", "-l", NULL }; pid_t pid = fork(); switch(pid) { case -1: exit(-1); // fork error! case 0: execl("/bin/ls", "ls", "-a", "-l", NULL); 또는 execvp("ls", args); break; } if (pid > 0) { wait(&status); }