Posted:      Updated:

운영체제 스터디를 하며 ‘운영체제 공룡책’ 교재를 정리한 글입니다.

프로세스

프로그램 자체는 프로세스가 아니며, 실행 파일이 메모리에 적재될 때 프로그램이 프로세스가 된다.
프로그램(실행 파일)은 디스크에 저장된 수동적인 존재다.
프로세스는 다음에 실행할 명령어를 지정하는 프로그램 카운터, 관련 자원의 집합을 가진 능동적인 존재다.

프로세스의 메모리 배치

  • 텍스트 섹션: 실행 코드
  • 데이터 섹션: 전역 변수
  • 힙 섹션: 프로그램 실행 중 동적으로 할당되는 메모리
  • 스택 섹션: 함수 호출 시 임시 데이터 저장(함수 매개변수, 복귀 주소, 지역 변수 등)

텍스트, 데이터 섹션은 고정되어 프로그램 실행 동안 크기가 변하지 않는다.
스택, 힙 섹션은 실행 중에 동적으로 줄어들거나 커진다.

프로세스 상태

  • 새로운(new): 프로세스 생성 중
  • 실행(running): 명령어들을 실행 중
  • 대기(waiting): 프로세스가 어떤 이벤트(입출력 완료, 신호 수신 등)를 기다림
  • 준비(ready): 프로세스가 처리기에 할당되는 것을 기다림
  • 종료(terminated): 프로세스 실행 종료

프로세스 제어 블록(Process Control Block, PCB)

특정 프로세스와 관련된 정보를 수록한다.

  • 프로세스 상태
  • 프로그램 카운터: 이 프로세스가 다음에 실행할 명령어 주소
  • CPU 레지스터들: 누산기(accumulator), 인덱스 레지스터, 스택 레지스터, 범용(general-purpose) 레지스터, 상태 코드(condition code) 정보 등
  • CPU-스케줄링 정보: 프로세스 우선순위, 스케줄 큐에 대한 포인터, 다른 스케줄 매개변수
  • 메모리 관리 정보: 메모리 시스템에 따라 기준(base) 레지스터, 한계(limit) 레지스터의 값, 페이지 테이블 또는 세그먼트 테이블 정보 등
  • 회계(accounting) 정보: CPU 사용 시간, 경과된 실시간, 시간 제한, 계정 번호, 잡 또는 프로세스 번호 등
  • 입출력 상태 정보: 이 프로세스에 할당된 입출력 장치들, 열린 파일 목록 등

스레드(Threads)

현대 운영체제는 대부분 한 프로세스가 다수의 실행 스레드를 가질 수 있다.
특히 다중 처리기 시스템에서 여러 스레드가 병렬로 실행될 수 있다.
PCB는 각 스레드의 관한 정보를 포함하도록 확장된다.

프로세스 스케줄링

다중 프로그래밍에서는 CPU 이용을 최대화하기 위해 CPU 코어를 빈번하게 교체하여 항상 어떤 프로세스가 실행되도록 하는 것이 목표이다.
각 CPU 코어는 한 번에 하나의 프로세스를 실행할 수 있으며, 다중 코어 시스템은 한 번에 여러 프로세스를 실행할 수 있다.
현재 메모리에 있는 프로세스 수는 다중 프로그래밍 정도라고 한다.

프로세스 스케줄러가 코어에서 실행 가능한 프로세스들 중 하나를 선택한다.
코어보다 많은 프로세스가 있다면, 초과 프로세스는 코어가 사용 가능해져서 다시 스케줄 될 때까지 기다린다.

I/O 바운드 프로세스: 계산에 소비하는 것보다 I/O에 더 많은 시간을 소비
CPU 바운드 프로세스: 계산에 더 많은 시간을 사용하고 I/O 요청이 자주 생성되지 않음

스케줄링 큐

프로세스가 시스템에 들어가면 준비 큐 에 들어가 준비 상태가 되어 CPU 코어에서 실행되기를 기다린다.
일반적으로 연결리스트이며, 헤더는 첫 번째 PCB에 대한 포인터와 준비 큐의 다음 PCB를 포함한다.

I/O 작업은 보통 느리게 실행되므로, I/O 작업 완료를 기다려야 하는데, 이러한 특정 이벤트를 기다리는 프로세스는 대기 큐 에 삽입된다.

새 프로세스가 처음에 준비 큐에 놓이면, 실행을 위해 선택되거나 디스패치 될 때까지 기다린다.
CPU 코어가 할당되고 실행 상태가 되면 입출력 요청, 타임슬라이스 종료, 자식 프로세스 생성, 인터럽트 대기와 같은 이벤트가 발생한다.
이벤트가 발생하면 준비 상태로 전환되며, 프로세스가 종료될 때까지 이를 반복한다.
종료 시점에 모든 큐에서 제거되고 PCB 및 자원을 반환한다.

CPU 스케줄링

CPU 스케줄러는 준비 큐에 있는 프로세스 중에서 선택된 하나의 프로세스에 CPU 코어를 할당한다.

일부 운영체제에서는 메모리가 초과 사용되면 가용공간 확보를 위해 스와핑 기법을 활용한다.
메모리에서 프로세스를 제거해 디스크로 스왑아웃하여 현재 상태를 저장하고, 나중에 디스크에서 메모리에 다시 스왑인하여 중단된 위치에서 실행을 계속한다.

문맥 교환(Context Switch)

인터럽트로 운영체제가 CPU 코어를 현재 작업에서 뺏어 커널 루틴을 실행할 수 있게 한다.
인터럽트 처리가 끝난 후 문맥(Context)을 복구해야 하므로 현재 문맥을 저장해야 한다.
따라서 CPU의 현재 상태를 저장하는 작업, 연산을 재개하기 위한 상태 복구 작업이 이루어진다.
이전 프로세스 상태를 보관하고 새로운 프로세스의 보관 상태를 복구하는 작업을 문맥 교환(Context Switch)이라고 한다.

문맥 교환이 이루어지는 동안 시스템은 유용한 일을 하지 못하므로 순수한 오버헤드가 발생한다.
이 문맥 교환 시간은 하드웨어의 지원과 관련이 있다.
운영체제가 복잡할수록, 복잡한 고급 메모리 관리 기법을 사용할 수록 더 많은 자료들을 교환해야 한다.

프로세스에 대한 연산

프로세스 생성

프로세스는 실행되는 동안 여러 개의 새로운 프로세스들을 생성할 수 있다.
생성하는 프로세스는 부모 프로세스, 새로운 프로세스는 자식 프로세스라고 부르며, 트리 구조가 된다.
프로세스마다 고유한 값인 프로세스 식별자(pid)로 프로세스를 구분할 수 있으며 정수 값을 사용한다.

Linux 운영체제에서는 pid가 1인 systemd 프로세스가 모든 사용자 프로세스의 루트 부모 프로세스 역할을 수행한다.
시스템이 부팅되면 systemd 프로세스가 다른 다양한 사용자 프로세스를 생성한다.

자식 프로세스의 자원은 운영체제로부터 직접 얻거나, 부모 프로세스가 가진 자원의 부분만을 사용하도록 제한되거나, 여러 자식 프로세스들이 같이 사용하도록 할 수 있다.
부모 프로세스가 자식 프로세스에 초기화 데이터(입력)을 전달할 수도 있다.

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

int main()

{
pid_t pid;

  pid = fork();

  if (pid < 0) {
    fprintf(stderr, "Fork Failed");
    return 1;
  }
  else if (pid == 0) {
    execlp("/bin/ls","ls",NULL) ;
  }
  else {
    wait(NULL);
    printf("Child Complete") ;
  }

  return 0;

위 코드는 fork() 시스템 콜을 사용해 별도의 프로세스를 생성하는 코드이다.
새 프로세스인 자식 프로세스는 부모 프로세스와 같은 소스코드로 복사된다.
pid 가 0이면 자식 프로세스, pid가 0 초과면 부모 프로세스로 구분하여 부모 프로세스는 자식 프로세스의 실행이 끝나는 것을 기다린다.

프로세스 종료

프로세스가 마지막 문장 실행을 끝내고 exit 시스템 콜을 사용해 자신의 삭제를 요청하면 종료한다.
자신을 기다리고 있는 부모 프로세스에 wait 시스템 콜로 상태 값(보통 정수값)을 반환할 수 있다.
부모는 자식 프로세스를 종료시킬 수 있는데, 자식의 pid를 알아야 하므로, 새로운 프로세스를 만들 때 identity가 부모에 전달된다.
부모 프로세스가 종료되면 자식 프로세스도 종료되어야 하는데, 이것을 연쇄식 종료라고 한다.

프로세스가 종료되었지만 부모 프로세스가 wait() 호출을 하지 않으면 좀비(zombie) 프로세스가 된다.
부모가 wait() 호출을 하지 않고 종료하면 자식 프로세스는 고아(orphan) 프로세스가 된다.
UNIX 에서는 고아 프로세스의 새로운 부모 프로세스로 init 프로세스를 지정하여 해결한다.
init 프로세스는 주기적으로 wait() 을 호출해 고아 프로세스의 종료 상태를 수집하고 자원을 반환한다.

Android 프로세스 계층

Android는 제한된 메모리와 같은 제약 때문에 프로세스의 중요도 계층을 식별하여 중요도가 낮은 프로세스부터 종료한다.

  • 전경 프로세스(forground process): 사용자가 현재 상호 작용하고 있는 응용 프로그램, 화면에 보이는 현재 프로세스
  • 가시적 프로세스(visible process): 전경에서 직접 볼 수 없지만 전경 프로세스가 참조하는 활동(즉, 현재 상태가 전경 프로세스에 표시되는 활동을 수행하는 프로세스)을 수행하는 프로세스
  • 서비스 프로세스(service process): 백그라운드 프로세스와 유사하지만 사용자가 인지할 수 있는 활동(예: 음악 스트리밍)을 수행하는 프로세스
  • 백그라운드 프로세스(background process): 활동을 수행하고 있지만 사용자가 인식하지 못하는 프로세스
  • 빈 프로세스(empty process): 응용 프로그램과 관련된 활성 구성요소가 없는 프로세스

댓글남기기