프로세스의 연산

GwanMtCat·2023년 9월 14일
0

프로세스가 어떻게 생성되고 복사되는지 알아 보자.

프로세스의 구조 (메모리 구조)

프로세스는 구조는 다음의 영역으로 나뉜다.

  • 코드 영역
    • 프로그램의 본문이 기술된 곳으로, 텍스트 영역이라고도 한다. 탑재된 코드는 읽기 전용으로 처리된다.
    • 프로그램을 실행시키는 실행 파일 내의 명령어들이 위치하는 공간이다.
    • 사용자가 작성한 함수가 저장되어 있다.
  • 데이터 영역

    • 코드가 실행되면서 사용하는 변수나 파일 등의 각종 데이터를 모아 놓은 곳이다. 읽기와 쓰기가 가능하다. 상수로 선언된 변수는 읽기 전용이지만, 대부분의 변수는 읽기, 쓰기가 가능하다.
    • 전역 변수나, static 변수가 선언되어 있다.
  • 힙 영역

    • 동적할당을 위한 메모리 영역을 말한다. malloc(), new로 생성된 인스턴스 객체등이 그 예이다.
  • 스택 영역

    • 운영체제가 프로세스를 실행하기 위해, 부수적으로 필요한 데이터를 모아 놓은 곳이다.
    • 지역변수나 파라미터가 여기에 있다.

프로세스의 생성과 복사

프로세스는 프로그램을 실행할 때 새로 생성된다. 프로그램을 실행하면 프로그램을 메모리로 가져와 코드 영역에 넣고 PCB를 생성한다. 그리고 메모리에 데이터 영역과 스택 영역을 확보한 후, 프로세스를 실행한다.

실행 중인 프로세스로부터 새로운 프로세스를 복사하는 방법도 있다. fork() 함수가 그 역할을 하는데 커널에서 제공하는 이 함수는 프로세스를 복사하는 일종의 시스템 호출이다.

예로 새로운 워드프로세서 프로그램을 하나 더 실행하면, 운영체제는 새로운 워드프로세서 프로그램을 실행하지 않고, fork() 시스템 호출을 사용하여 기존의 워드 프로세서를 복사한다.

프로세스를 복사할 때, 기존의 프로세스는 부모 프로세스가되고 새로 생긴 프로세스는 자식 프로세스가 되어, 두 프로세스는 부모-자식관계로 연결된다.

프로세스 제어 블록을 포함한 부모 프로세스 영역의 대부분이 자식 프로세스에 복사되어 똑같은 프로세스가 만들어지는데 프로세서 제어 블록의 내용 중 일부가 변경된다.

  • 프로세스 구분자(PID)가 바뀐다.
  • 메모리의 위치가 다르므로 메모리 관련 정보가 바뀐다
  • 부모 프로세스 구분자(PPID)와 자식 프로세스 구분(CPID)가 바뀐다.

프로세스를 복사하면 다음과 같은 장점이 있다.

  • 프로세스의 생성 속도가 빠르다
  • 추가 작업 없이 자원을 상속할 수 있다
  • 시스템 관리를 효율적으로 할 수 있다.

기억해야 할 점은 fork() 문 이전에 파일을 여거나 변수를 선언하면 이것이 모두 자식 프로세스에 상속된다는 것이다.


프로세스의 전환

기존의 프로세스를 새로운 프로세스로 전환하려면 어떻게 해야할까? exec() 함수의 시스템 호출을 하면된다.

fork() 가 새로운 프로세스를 복사하는 것이라면, exec() 는 프로세스는 그대로 둔 채 내용만 바꾸는 시스템 호출이다.

exec() 시스템 호출을 하면 현재의 프로세스가 완전히 다른 프로세스로 전환된다.
사용 목적은 프로세스의 구조체를 재활용하기 위함으로 이미 만들어진 프로세스 제어 블록, 메모리 영역, 부모-자식 관계를 그대로 사용할 수 있어 편리하다. 새로운 코드 영역만 가져오면 되기 떄문에 운영체제의 작업이 수월하다.

exec() 호출을 하면

  • 코드 영역에 있는 기존의 내용을 지우고 새로운 코드로 바꾼다.

  • 데이터 영역이 새로운 변수로 채워지고 스택 영역이 리셋된다.

  • 프로세스 제어 블록의 내용 중 프로세스 구분자, 부모 프로세스 구분자, 자식 프로세스 구분자, 메모리 관련 사항 등은 변하지 않지만 프로그램 카운터 레지스터 값을 비롯한 각종 레지스터와 사용한 파일 정보가 모두 리셋된다.

즉 기존의 프로세스 구조를 그대로 둔 채 내용만 바꾸어 실행하는 것이다.


위의 프로세스의 복사와 전환은 프로세스의 계층구조를 이해하는데 큰 도움이 된다.

가령 유닉스의 프로세스 계층 구조를 보자.

유닉스에서 커널이 처음 메모리에 올라와 부팅이 되면 커널 관련 프로세스를 여러 개 만드는데 그중 init 프로세스가 전체 프로세스의 출발점이 된다.

init 프로세스는 일반 사용자 프로세스의 맨 위에 위치하여, fork()와 exec() 시스템 호출을 이용하여 자식 프로세스를 만든다.

이러한 구조는 동시에 여러 작업을 처리하고, 종료된 프로세스의 자원을 회수하는데 유용하다.

  • 동시에 여러 작업을 처리하는 경우
    • 다수의 사용자가 login 프로세스를 사용한다고 가정하면, fork() 시스템 호출로 프로세스를 여러 개 만들어 나누어준다. login 이후 shell 프로세스가 생성되어야 하는데
    • 이때, login을 종료 시키는게 아니라 exec() 시스템 호출로 구조를 바꾸는 것이다.

  • 종료된 프로세스 자원의 회수
    • 프로세스 간의 책임 관계가 분명해져서 시스템을 관리하기가 수월하다. 모든 프로세스를 부모-자식 관계로 만들면 자식 프로세스가 작업을 마쳤을 때 사용하던 자원을 부모 프로세스가 회수하면 된다.

고아 프로세스

  • 부모 프로세스는 자원을 회수하기 위해 자식 프로세스의 종료를 기다려야된다.

  • exit() 혹은 return() 문은 자식 프로세스가 작업이 끝났음을 부모 프로세스에 알리는 것이다.

  • 부모 프로세스가 먼저 종료되거나 자식 프로세스가 비정상적으로 종료되면 부모 프로세스에 연락이 안 되는 경우도 있다. 이런 문제가 발생하면 자식 프로세스가 종료되지 않거나, 종료 되었는데도 사용하던 자원이 그대로 남게 된다.

  • 이러한 프로세스를 고아 프로세스(orphan process) 라고 한다.

  • 따라서 운영체제는 반환되지 못한 자원을 회수하는 자원 회수를 주기적으로 해야 한다.

  • 자바의 경우는 모든 객체는 Object 객체의 자식이 되므로 객체를 다 사용하고 난 뒤에 자원이 쉽게 회수되게 한다.

0개의 댓글