반응형

이번 시간에는 리눅스 환경에서 프로세스들이 어떻게 작동하는지 알아봅니다.

 

프로세스는 구조는 아래와 같습니다. 아래의 구조를 PCB라고 부르며, 프로세스가 생성되면 고유의 pid(Process ID) 및 PCB가 주어집니다. 이 공간에 프로세스 이미지가 업로드되고, 실행하면 프로세스의 작업이 시작됩니다.

이 때 생성되는 pid는 파일에서의 inode와 같이, 프로세스와 1:1로 관리가 됩니다.(이 때 커널 메모리는 모든 프로세스가 공유합니다.)

 

1) STACK : 함수 실행 간 생성되는 데이터를 정적으로 할당

2) HEAP : 함수 실행 간 생성되는 데이터를 동적으로 할당

3) BSS : 초기화되지 않은 변수 값들을 저장

4) DATA : 초기화된 변수 값들을 저장

5) TEXT : 실행프로그램의 코드를 저장

운영체제가 Load되면 최초의 프로세스인 init이 생성되고, 이 프로세스에 pid = 1이 주어집니다. 리눅스에서는 일반적으로 init을 제외한 다른 프로세스를 생성할 때 fork() 시스템 콜을 사용하여 생성하게 됩니다. 

프로세스의 생성 과정은 아래와 같습니다.

 

[프로세스 생성]

1) 부모 프로세스에서 fork()를 수행한다.

2) fork()를 하면 자식 프로세스의 경우 성공적으로 수행되면 새로운 가상메모리(4GB) 공간을 생성한 뒤 return 값으로 0을 주게 되는데(실패하면 -1), return 값이 0으로 확인되면 exec(실행파일)을 수행한다.

3) exec(실행파일)에 의해서 새 프로세스 공간에 실행파일을 덮어씌우고, 이를 처음부터 실행한다.

4) 자식 프로세스 마지막에 exit(종료 상태값)을 사용하여 자식 프로세스를 종료함과 동시에 종료 상태값을 전달한다.

5) 부모 프로세스에서 wait(종료 상태값)을 사용하여 자식 프로세스의 종료 상태값을 확인하고, 남은 부모 프로세스를 수행한다.

 

* copy-on-write : 부모 프로세스에서 자식 프로세스를 fork()할 때 새로운 가상메모리공간 4GB를 복사하여 만들게 되는데, 4GB를 복사하는데 시간이 매우 오래 걸린다. 이를 개선하기 위해 자식 프로세스에서는 처음에 생성된 뒤 부모의 메모리 공간(물리적)을 참조한다. 읽기 과정에서는 해당 참조값이 없지만, 자식 프로세스의 수행 과정에서 메모리에 write할 일이 생기면, 부모 프로세스의 메모리 공간(물리적)에 그대로 쓰는 것이 아니라 변화가 필요한 내용의 페이지만 새로운 공간에 복사하여 write를 진행한다.

 

[ 프로세스 관련 시스템 콜 ]

 

* fork() : 시스템 콜. 해당 시스템 콜이 실행되면, 운영체제는 새로운 프로세스 공간(PCB)를 만들고, fork() 시스템콜을 호출한 프로세스(부모 프로세스) 공간을 모두 복사해서 새로운 프로세스 공간에 붙여넣습니다. 이렇게 새로 만들어진 프로세스에는 새로운 pid가 부여됩니다. 이 때 부포 프로세스의 id를 ppid라고 하고, 해당 변수도 새롭게 만들어진 프로세스에 저장됩니다.

 


ex)

#include <sys/types.h>
#include 
#include 
int main()
{
        pid_t pid;
        printf("Before fork() call\n");
        pid = fork();

        if(pid == 0)
                printf("This is Child process. PID is %d\n", pid);
        else if (pid > 0)
                printf("This is Parent process. PID is %d\n", pid);
        else
                printf("fork() is failed\n");
        return 0;
}


* exec() : 시스템 콜. 해당 시스템 콜을 호출한 프로세스 공간의 [TEXT, DATA, BSS] 영역을 새로운 프로세스(인자로 전달)의 이미지로 덮어씌웁니다. 따라서 인자로 실행파일을 전달해줘야 합니다.

 

exec()는 fork()와 다르게 여러 버전의 함수가 있습니다. 해당 함수들은 인자를 전달하는 방식이 다릅니다.

 

1) execl() 

2) execlp()

3) execle()

4) execv()

5) execvp()

6) execve()

 

* getpid() : pid(Process ID)를 가져옵니다.

* getppid() :ppid(부모의 Process ID)를 가져옵니다.

 

* wait() : 시스템 콜, 부모 프로세스가 자식 프로세스보다 먼저 종료되는 일이 없도록, 자식 프로세스가 종료될 때까지 기다리게 하는 함수입니다. 자식 프로세스가 종료할 때 종료 상태값(정상 종료 : 0)을 저장하면, 해당 상태값을 참조하여 나머지 부모 프로세스를 수행합니다.

 

* exit() : 시스템 콜, 프로세스를 강제로 즉시 종료시킵니다. 인자로 프로세스 종료 상태 번호를 전달합니다.

# 동작 과정

1) atexit()이라는 함수에 등록된 함수를 역순으로 순차적으로 실행한다.

2) 열려있는 모든 입출력 스트림 버퍼를 삭제한다.

3) 프로세스가 오픈한 파일을 모두 닫는다.

4) tmpfile() 함수를 통해 생성한 임시 파일을 삭제한다. 

 

[ Process 관련 명령어 ]

 

* ps -ef : 현재 실행중인 프로세스의 모든 정보를 출력합니다.

반응형

+ Recent posts