나를 기록하다
article thumbnail
반응형

쉽게 배우는 운영체제

프로세스의 개요

운영체제에서 프로세스란 하나의 작업 단위다.

사용자가 마우스를 더블클릭하여 프로그램을 실행하면 그 프로그램은 프로세스가 된다.

이렇게 실행된 프로세스는 복잡한 과정을 거쳐 사용자가 지시한 작업을 마무리한다.

 

프로세스의 개념

폰노이만 구조에서 프로그램이 실행된다는 것은 해당 코드가 메모리에 올라와서 작업이 진행된다는 의미다.

프로그램은 저장장치에 저장된 정적인 상태이고, 프로세스는 실행을 위해 메모리에 올라온 동적인 상태다.

프로그램과 프로세스
프로그램: 작성한다라고 표현, 어떤 데이터를 사용하여 어떤 작업을 할지 그 절차를 적어놓은 것
프로세스: 실행한다라고 표현, 프로그램으로 작성된 작업 절차를 실행에 옮기는 것
누군가 작성한 프로그램이 실행되면 프로세스가 된다.

 

일괄 작업 방식

  • 일괄 작업 시스템은 한 번에 하나의 작업만 처리하는 것
  • 큐(queue)를 사용

시분할 방식

  • CPU가 1개인 컴퓨터에서 여러 개의 프로세스를 동시에 실행하는 것
  • CPU가 시간을 쪼개어 여러 프로세스에 적당히 배분함으로써 프로세스가 동시에 실행되는 것처럼 느끼게 함
  • 시분할 방식에서 주문서(요구 사항, 현재 진행 상황 등이 포함)가 매우 중요함
  • 주문서의 모든 요구 사항이 제공되면 주문서는 주문 목록에서 삭제됨
  • 주문 목록이 삭제되었다는 것은 해당 작업이 종료되었음을 의미

현대의 운영체제는 시분할 방식을 기본으로 사용하기 때문에 프로세스가 여러 상태를 오가며 실행된다.


프로그램에서 프로세스로의 전환

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

  1. 운영체제는 프로그램을 메모리의 적당한 위치로 가져옴
  2. 그와 동시에 주문서에 해당하는 작업 지시서를 만듦
  3. 이 작업 지시서를 프로세스 제어 블록이라 함

프로세스를 처리하는 데 필요한 다양한 정보가 들어 있다. 프로세스 제어 블록이 없으면 프로그램이 프로세스로 전환되지 못한다. 즉, 어떤 프로그램이 프로세스가 되었다는 운영체제로부터 프로세스 제어 블록을 받았다는 의미다.

 

프로세스 제어 블록에 있는 대표적인 정보

  • 프로세스 구분자(PID, Process IDentification)
    • 메모리에는 여러 개의 프로세스가 존재하므로 각 프로세스를 구분하는 구분자(ID)가 필요
    • 프로세스를 구분하기 위한 프로세스 구분자가 존재
  • 메모리 관련 정보
    • CPU는 실행하려는 프로세스가 메모리의 어디에 저장되어 있는지를 알아야 작업할 수 있음
    • 이를 위해 프로세스 제어 블록에는 프로세스의 메모리 위치 정보가 담겨 있음
    • 메모리 보호를 위한 경계 레지스터와 한계 레지스터도 포함되어 있음
  • 각종 중간값
    • 프로세스가 사용했던 중간값 저장
    • 시분할 시스템에서는 여러 프로세스가 번갈아 가며 실행되기 때문에 각 프로세스는 일정 시간 작업을 한 후 다른 프로세스에 CPU를 넘겨줌

하나의 프로세스를 실행하려면 프로세스 구분자, 메모리 관련 정보, 프로그램 카운터과 각종 레지스터 같은 중간값을 관리해야 한다. 이러한 정보를 보관하는 데이터 구조가 프로세스 제어 블록이다. 프로그램이 프로세스가 되려면 메모리에 올라오는 것과 동시에 프로세스 제어 블록이 생성되어야 한다.

 

프로세스 제어 블록은 운영체제가 해당 프로세스를 실행하기 위해 관리하는 데이터 구조이므로 운영체제 영역에 만들어진다. 프로세스가 종료되면 프로세스는 메모리에서 삭제되고 프로세스 제어 블록도 폐기 된다.

 

프로그램이 메모리에 올라와 프로세스가 되는 과정

프로그램 → 프로세스

프로그램과 프로세스
프로그램이 프로세스가 된다는 것
운영체제로부터 프로세스 제어 블록을 얻는다
프로세스가 종료된다는 것
해당 프로세스 제어 블록이 폐기된다는 것
프로세스와 프로그램의 관계
프로세스 = 프로그램 + 프로세스 제어 블록
프로그램 = 프로세스 - 프로세스 제어 블록

 

컴퓨터 내에는 사용자가 실행한 프로세스만 있는 것이 아니다. 운영체제도 프로그램이기 때문에 프로세스 형태로 실행되어야 한다. 컴퓨터의 전원을 켰을 때 운영체제 프로세스를 메모리에 올리는 프로그램으로 부트스트랩(bootstrap)이 있다.

 

부트스트랩(bootstrap)

부트스트랩이 운영체제 관련 프로세스를 실행한 후 일반 프로세스가 실행되므로 컴퓨터에는 일반 사용자의 사용자 프로세스(user process)와 운영체제의 커널 프로세스(kernel process)가 섞여서 실행된다.


프로세스의 상태

운영체제에서 여러가지 이유로 프로세스 상태가 변한다.

  • 일괄 작업 시스템
    • 프로세스가 생성된 후 CPU를 얻어 실행되고 작업을 마치면 종료된다.
    • 따라서 일괄 작업 시스템 프로세스 상태는 생성(create), 실행(run), 완료(terminate) 중 하나가 된다.
  • 시분할 시스템
    • CPU를 얻어 실행 중인 프로세스가 중간에 다른 프로세스에 CPU를 넘겨주는 일이 빈번하게 발생
    • 이제 막 프로세스가 되었거나 CPU를 사용하다가 쫓겨난 프로세스는 준비 상태에서 순서를 기다림

프로세스의 네 가지 상태(생성,준비,실행,완료)

프로세스의 네 가지 상태

  • 생성 상태(create status)
    • 프로세스가 메모리에 올라와 실행 준비를 완료한 상태
    • 프로세스를 관리하는 데 필요한 프로세스 제어 블록이 생성됨
  • 준비 상태(ready status)
    • 생성된 프로세스가 CPU를 얻을 때까지 기다리는 상태
    • CPU가 하나인 컴퓨터에서는 한 번에 하나의 프로세스만 실행 가능하기에 자기 실행 순서가 될 때까지 준비 상태에서 기다려야 함
  • 실행 상태(running status)
    • 준비 상태에 있는 프로세스 중 하나가 CPU를 얻어 실제 작업을 수행하는 상태
    • execute status라고 표현하기도 함
    • 실행 상태에 들어간 프로세스는 일정 시간 동안 CPU를 사용할 권리를 갖는다.
    • 주어진 시간을 다 사용하고도 작업이 끝나지 않았다면 프로세스는 준비 상태로 돌아와 다음 차례를 기다린다.
    • 프로세스는 자신의 작업이 끝날 때까지 준비 상태와 실행 상태를 왔다 갔다 한다.
  • 완료 상태(terminate status)
    • 실행 상태의 프로세스가 주어진 시간 동안 작업을 마치면 완료 상태로 진입한다.
    • 완료 상태는 프로세스 제어 블록이 사라진 상태를 의미한다.

CPU 스케줄러(CPU scheduler)

  • 준비 상태에 있는 여러 프로세스 중 다음에 실행할 프로세스를 선정하는 역할
  • 준비 상태의 맨 앞에서 기다리고 있는 프로세스 제어 블록을 CPU에 전달하여 작업이 이루어지게 함
  • 프로세스의 전 상태(생성,준비,실행,완료)에 관여하여 모든 프로세스의 작업이 원만하게 이뤄지도록 관리
  • CPU 스케줄러에 의해 선택된 프로세스는 실행 상태에서 일정 시간 동안 작업을 함

디스패치(dispatch)

  • 준비 상태의 프로세스 중 하나를 골라 실행 상태로 바꾸는 CPU 스케줄러의 작업

타임 슬라이스 또는 타임 퀀텀

  • 프로세스에 배당된 작업 시간

타임아웃(time out)

  • 프로세스가 자신에게 주어진 하나의 타임 슬라이스 동안 작업을 끝내지 못하고 다시 준비상태로 돌아간 것

프로세스의 다섯 가지 상태(네 가지 상태 + 대기)

어떤 프로세스가 실행 상태에 들어가 입출력을 요구했다고 가정하자.

  1. 인터럽트 시스템에서 프로세스가 입출력을 요구
  2. CPU가 직접 데이터를 가져오지 않고 입출력 관리자에게 명령
  3. 프로세스는 요청한 작업이 끝날 때까지 다음 작업을 할 수 없음
  4. CPU도 아무 작업을 하지 않고 기다리게 되어 효율성 저하
  • 대기 상태(blocking status)
    • 입출력을 요구한 프로세스가 입출력이 완료될 때까지 기다리는 상태
    • 작업의 효율성을 높이기 위해 입출력을 요청한 프로세스를 실행 상태에 두지 않고 대기 상태에 옮기는 것
    • 입출력을 요청한 프로세스가 대기 상태로 옮겨지면 CPU 스케줄러는 준비 상태에 있는 프로세스 중 하나를 가져다 실행 상태로 만듦
    • 시스템 입장에서는 새로운 작업을 진행하게 되어 효율성이 높아진다.

프로세스의 다섯 가지 상태

  • 대기 상태의 프로세스는 요청한 입출력이 완료되면 입출력 관리자로부터 인터럽트를 받음
  • 대기 상태의 프로세스는 원래 실행 상태에서 옮겨 왔기 때문에 입출력이 끝나면 실행 상태로 돌아가는 것이 맞지만, 그러려면 현재 실행 상태에서 작업 중인 프로세스를 준비 상태로 돌려보내야 한다.
  • 두 프로세스의 상태를 변화시켜야 하는 복잡한 상황이므로 대기 상태에서 입출력이 끝난 프로세스는 실행 상태로 가지 않고 준비 상태로 돌아가 자기 차례를 기다린다.

생성 상태

  • 프로그램이 메모리에 올라오고 운영체제로부터 PCB를 할당 받은 상태
  • 생성된 프로세스는 바로 실행되는 것이 아닌 준비 상태에서 자기 순서를 기다린다.
  • PCB도 같이 준비 상태로 옮겨짐

준비 상태

  • 실행 대기 중인 모든 프로세스가 자기 순서를 기다리는 상태
  • PCB는 준비 큐(ready queue)에서 기다리며 CPU 스케줄러에 의해 관리
  • 실제로는 다수의 준비 큐가 운영된다.
  • CPU 스케줄러는 준비 상태에서 큐를 몇 개 운영할지, 큐에 있는 어떤 프로세스의 PCB를 실행 상태로 보낼지 결정

실행 상태

  • 프로세스가 CPU를 할당받아 실행되는 상태
  • 준비 상태에 있는 프로세스 중 실행 상태에 들어가는 프로세스의 수는 CPU의 개수만큼이다.
  • 실행 상태에 있는 프로세스는 타임 슬라이스 동안만 작업 가능, 시간 다 사용 후 PCB를 실행 상태에서 준비 상태로 옮김
  • 실행 상태 동안 작업이 완료되면 프로세스가 정상 종료
  • 실행 상태에 있는 프로세스가 입출력을 요청하면 CPU는 입출력 관리자에게 입출력을 요청하고 해당 프로세스를 대기 상태로 옮김
  • CPU 스케줄러는 새로운 프로세스를 실행 상태로 가져옴(디스패치)

대기 상태

  • 실행 상태에 있는 프로세스가 입출력을 요청하면 입출력이 완료될 때까지 기다리는 상태(wait status)
  • 작업 효율을 높이기 위해 만들어진 것으로, 프로세스는 입출력장치별로 마련된 큐에서 기다림
  • 입출력이 완료되면 인터럽트가 발생, 대기 상태에 있는 여러 프로세스 중 해당 인터럽트로 깨어날 프로세스를 찾음
  • 찾아낸 프로세스의 PCB을 준비 상태로 이동시킴
  • 어떤 프로세스가 대기 상태에서 준비 상태로 이동하는 것은 인터럽트 때문
  • 인터럽트는 입출력으로 발생하지만 어떤 이벤트에 의해 발생하기도 함

완료 상태

  • 프로세스가 종료되는 상태
  • 코드와 사용했던 데이터를 메모리에서 삭제하고 PCB를 폐기
  • 코어 덤프(core dump)
    • 오류나 다른 프로세스에 의해 비정상적으로 종료되는 강제 종료(abort)를 만나면 디버깅하기 위해 강제 종료 직전의 메모리 상태를 저장장치로 옮기는 것
  • 코어 덤프는 종료 직전의 메모리 상태를 확인함으로써 오류를 수정할 수 있게 해줌
프로세스의 상태 정리
생성 상태
저장장치에 저장된 프로그램이 메모리로 올라와 실행되어 프로세스가 되는 상태로 커널 영역에 프로세스 제어 블록이 만들어진다. 생성된 후에는 준비 상태로 돌아간다.
준비 상태
실행을 기다리는 모든 프로세스가 준비 큐에서 자기 차례를 기다리는 상태다. 실행될 프로세스를 CPU 스케줄러가 선택한다.
실행 상태
선택된 프로세스가 타임 슬라이스를 얻어 CPU를 사용하는 상태다. 작업을 마치면 완료 상태로 가고 작업을 끝내지 못하면 준비 상태로 되돌아간다.
대기 상태
실행 상태에 있는 프로세스가 입출력을 요청하면 입출력이 완료될 때까지 기다리는 상태다. 입출력이 완료되면 준비 상태로 이동한다.
완료 상태
프로세스가 종료된 상태다. 사용하던 모든 자원은 반납되고, 메모리에서 지워지며, 프로세스 제어 블록은 폐기된다.

 

휴식 상태와 보류 상태

대부분의 프로세스는 생성, 준비, 실행, 대기, 완료 상태로 운영되며 이 다섯 가지 상태를 활성 상태(active status)라고 한다.

프로세스의 상태는 활성 상태 외에 휴식 상태와 보류 상태가 있는데, 이 상태들은 조금 특별한 경우에 해당한다.

  • 휴식 상태(pause status)
    • 프로세스가 작업을 일시적으로 쉬고 있는 상태
    • 사용하던 데이터가 메모리에 그대로 있고 PCB도 유지되므로 프로세스는 멈춘 지점에서부터 재시작할 수 있음
  • 보류 상태(suspend status)
    • 프로세스가 메모리에서 잠시 쫓겨난 상태, 일시 정지 상태라고도 불림
    • 보류 상태와 비교하여 일반적인 프로세스 상태를 활성 상태라고 함
    • 보류 상태가 되는 경우
      1. 메모리가 꽉 차서 일부 프로세스를 메모리 밖으로 내보낼 때
      2. 프로그램에 오류가 있어서 실행을 미루어야 할 때
      3. 바이러스와 같이 악의적인 공격을 하는 프로세스라고 판단될 때
      4. 매우 긴 주기로 반복되는 프로세스라 메모리 밖으로 쫓아내도 큰 문제가 없을 때
      5. 입출력을 기다리는 프로세스의 입출력이 계속 지연될 때
    • 대게 컴퓨터의 성능을 떨어뜨리거나 실행을 미루어도 큰 지장이 없는 프로세스가 해당

프로세스의 상태 변화

  • 스왑 영역(swap area)
    • 보류 상태에 들어간 프로세스는 메모리 밖으로 쫓겨나 스왑 영역에 보관
    • 메모리에서 쫓겨난 데이터가 임시로 보관되는 곳
보류상태 vs 휴식 상태
보류 상태: 스왑 영역에 있는 상태
휴식 상태: 프로세스가 메모리에 있으나 멈춘 상태

 

보류 상태는 다시 대기 상태에서 옮겨진 보류 대기 상태(block suspend status)와 준비 상태에서 옮겨진 보류 준비 상태(ready suspend status)로 구분되며, 각 상태에서 재시작하면 원래의 활성 상태로 들억나다. 또한 보류 대기 상태에서 입출력이 완료되면 활성 상태가 아닌 보류 준비 상태로 옮겨 간다.


프로세스 제어 블록과 문맥 교환

프로세스 제어 블록(PCB)

PCB는 프로세스를 실행하는 데 필요한 중요한 정보를 보관하는 자료구조로 TCB(Task Control Block)라고도 한다.

모든 프로세스에는 고유의 PCB가 있으며, 프로세스가 생성될 때 만들어져 프로세스 실행이 완료되면 폐기된다.

프로세스 제어 블록의 구성

  • 포인터
    • PCB의 첫 번째 블록에 저장
    • PCB를 연결하여 준비 상태나 대기 상태의 큐를 구현할 때 사용
  • 프로세스 상태
    • 프로세스가 현재 어떤 상태에 있는지를 나타냄(생성, 준비, 실행, 대기, 보류 준비, 보류 대기 등)
  • 프로세스 구분자
    • 운영체제 내에 있는 여러 프로세스를 구별하기 위한 구분자
  • 프로그램 카운터
    • 다음에 실행될 명령어의 위치를 가리키는 프로그램 카운터의 값 저장
  • 프로세스 우선순위
    • 사용자 프로세스보다 중요도가 큰 커널 프로세스는 우선순위가 높고, 사용자 프로세스끼리도 우선순위가 다름
    • 우선순위에 따라 PCB들이 여러 줄로 서 있음
    • CPU 스케줄러가 준비 상태에 있는 프로세스 중 실행 상태로 옮겨야 할 프로세스를 선택할 때 기준으로 삼음
    • 높은 우선순위의 프로세스가 더 먼저 실행되고 더 자주 실행됨
  • 각종 레지스터 정보
    • 프로세스가 실행될 때 사용하던 레지스터의 값 저장
    • 2장에서 다룬 레지스터, 누산기(accumulator), 색인 레지스터(index register), 스택 포인터(stack pointer)등의 레지스터
    • 이전 실행 시 사용한 레지스터의 값을 보관해야 다음에 실행할 수 있기에 사용하던 레지스터의 중간값 보관
  • 메모리 관리 정보
    • 메모리 위치 정보, 메모리 보호를 위한 경계 레지스터 값, 한계 레지스터 값 등이 저장
    • 세그먼테이션 테이블(segmentation table), 페이지 테이블(page table) 등의 정보 보관
  • 할당된 자원 정보
    • 프로세스를 실행하기 위해 사용하는 입출력 자원이나 오픈 파일 등에 대한 정보
  • 계정 정보
    • 계정 정보, CPU 할당 시간, CPU 사용 시간 등에 대한 정보
  • 부모 프로세스 구분자와 자식 프로세스 구분자
    • PPID(Parent PID)와 CPID(Child PID) 정보 저장

문맥 교환

문맥 교환의 의미

  • 문맥 교환(context switching): CPU를 차지하던 프로세스가 나가고 새로운 프로세스를 받아들이는 작업
  • 이때 두 PCB의 내용이 변경됨
  • 실행 상태에서 나가는 PCB에는 지금까지의 작업 내용 저장
  • 실행 상태로 들어오는 PCB의 내용으로 CPU가 다시 세팅됨

문맥 교환의 절차

  1. 실행 상태에 있는 프로세스 P1이 타임아웃이 되면 P1의 PCB에 현재까지의 작업 결과 저장
  2. P1은 준비 상태로 쫓겨남
  3. 준비 상태에 있던 프로세스 P2가 실행 상태로 가면 CPU의 레지스터가 P2의 PCB 값으로 채워져 다음 작업을 하게 됨

문맥 교환 과정

  • 문맥 교환이 일어나는 상황
    • 한 프로세스가 타임아웃 될 때
    • 인터럽트가 걸렸을 때
    • [예시] 어떤 프로세스가 자신에게 주어진 메모리 공간을 넘어가려 하는 상황의 문맥 교환
      1. 경계 레지스터의 범위를 벗어나는 것
      2. 인터럽트 발생
      3. 현재 실행 중인 PCB의 제어 블록을 저장
      4. 인터럽트 관리 프로세스를 실행 상태로 만듦
      5. 인터럽트 관리 프로세스는 메모리 범위를 넘어선 프로세스 강제 종료 후 인터럽트 마침

문맥 교환과 타임 슬라이스의 크기

  • 타임 슬라이스가 너무 크면, 한 프로세스의 작업이 끝난 뒤 다른 프로세스의 작업이 이루어질 때까지 긴 시간이 걸려 작업이 끊겨 보임

타임 슬라이스 크기와 문맥 교환

  • 타임 슬라이스를 1ms로와 같이 매우 작은 값으로 설정하면 사용자는 여러 프로그램이 동시에 실행되는 것처럼 느낄 수 있음
  • 하지만 문맥 교환에도 시간이 걸리기에 문맥 교환에 많은 시간을 낭비해 타임 슬라이스를 너무 작게 설정하면 시스템의 성능이 떨어짐
결론
타임 슬라이스는 되도록 작게 설정하되 문맥 교환에 걸리는 시간을 고려하여 적당한 크기로 설정하는 것이 중요
ex) 유닉스의 타임 슬라이스는 대략 100ms(10ms ~ 200ms 사이에서 조정)

프로세스의 연산

프로세스의 구조

프로세스는 코드 영역, 데이터 영역, 스택 영역으로 구성되어 있다.

  • 코드 영역(요리책)
    • 프로세스의 본문이 기술된 곳, 텍스트 영역(text area)이라고도 함
    • 프로그래머가 작성한 프로그램은 코드 영역에 탑재되며 탑재된 코드는 읽기 전용으로 처리됨
  • 데이터 영역(재료)
    • 요리 재료의 역할을 하는 영역. 요리를 완성하려면 재료가 있어야 함
    • 코드가 실행되면서 사용하는 변수(variable)나 파일 등의 각종 데이터를 모아놓은 곳
    • 데이터는 변하는 값이기 때문에 이곳의 내용은 기본적으로 읽기와 쓰기가 가능
    • 상수로 선언된 변수는 읽기 전용
  • 스택 영역(조리 도구)
    • 운영체제가 프로세스를 실행하기 위해 부수적으로 필요한 데이터를 모아놓은 곳
    • [예시] 프로세스 내에서 함수를 호출(function call)
      1. 함수 수행
      2. 원래 프로그램으로 되돌아올 위치를 이 영역에 저장
    • 운영체제가 사용자의 프로세스를 작동하기 위해 유지하는 영역 → 사용자에게 보이지 않음

[예시] 워드프로세서 프로그램 실행

  1. 프로그램 → 코드 영역에 탑재
  2. 워드프로세서로 편집 중인 문서 → 데이터 영역에 탑재
  3. 운영체제가 워드프로세서를 작동하기 위해 사용하는 각종 부가 데이터 → 스택 영역에서 관리

프로세스의 생성과 복사

  1. 프로세스는 프로그램을 실행할 때 새로 생성
  2. 사용자가 프로그램을 실행하면 운영체제는 프로그램을 메모리로 가져와 코드 영역에 넣고 프로세스 제어 블록을 생성
  3. 메모리에 데이터 영역과 스택 영역을 확보한 후 프로세스를 실행

 

  • 프로세스 생성 방법
    • 프로세스 새로 생성하는 방법
    • 실행 중인 프로세스에서 새로운 프로세스를 복사하는 방법

 

fork() 시스템 호출의 개념

  • fork() 시스템 호출
    • 실행 중인 프로세스로부터 새로운 프로세스를 복사하는 함수
    • 커널에서 제공
    • 사용 시 실행 중인 프로세스와 똑같은 프로세스가 하나 더 만들어짐
    • [예시] 워드프로세서로 문서 작업 중 새로운 워드프로세서 프로그램을 하나 더 실행
      1. 운영체제에서 fork() 시스템 호출을 사용하여 기존 워드프로세서 복사
      2. 복사 시 처음 워드프로세서 프로그램을 실행하는 속도보다 훨씬 빠름
    • [예시] 크롬에서 어떤 페이지를 보다가 Ctrl + N키를 누르면 크롬이 하나 더 실행
      → 현재의 크롬 프로세스를 복사한 것
fork() 시스템 호출
fork() 시스템 호출은 실행 중인 프로세스를 복사하는 함수다. 이때 실행하던 프로세스는 부모 프로세스, 새로 생긴 프로세스는 자식 프로세스로서 둘은 부모 - 자식 관계가 된다.

 

fork() 시스템 호출의 동작 과정

부모 프로세스와 자식 프로세스

  • fork() 시스템 호출을 하면 PCB을 포함한 부모 프로세스 영역의 대부분이 자식 프로세스에 복사되어 똑같은 프로세스가 만들어짐
  • 단 PID의 내용 중 일부가 변경됨
    1. PID(프로세스 구분자)
    2. 메모리 관련 정보 - 부모 프로세스와 자식 프로세스가 차지하는 메모리의 위치가 다르기 때문
    3. PPID(부모 프로세스 구분자), CPID(자식 프로세스 구분자)

 

fork() 시스템 호출의 장점

  1. 프로세스의 생성 속도가 빠름
    • 하드디스크로부터 프로그램을 새로 가져오지 않고 기존 메모리에서 복사하기 때문에 자식 프로세스의 생성 속도가 빠름
  2. 추가 작업 없이 자원 상속 가능
    • [예시] 부모 프로세스가 파일 A를 사용하기 위해 초기화했다면 자식 프로세스는 파일 A를 바로 사용 가능
  3. 효율적인 시스템 관리 가능
    • 부모 프로세스와 자식 프로세스가 CPID와 PPID로 연결되어 있음
      • 자식 프로세스 종료 시 자식이 사용하던 자원을 부모 프로세스가 정리 가능
      • 프로세스 종료 시 프로세스가 사용하던 메모리 영역, 파일, 하드웨어를 잘 정리해야 함
      • 정리를 부모 프로세스에 맡김으로써 시스템이 효율적으로 관리됨

프로세스의 전환

  • 운영체제는 주문하는 요리가 달라도 간단하게 처리하는 기능을 제공
    1. fork() 시스템 호출로 요리를 복사
    2. 복사된 요리를 새로운 요리로 바꾸는 함수인 exec() 시스템 호출 사용

 

exec() 시스템 호출의 개념

  • 기존 프로세스를 새로운 프로세스로 전환하는 함수
  • fork() 시스템 호출이 스파게티를 하나 더 만드는 것이라면, exec() 시스템 호출은 스파게티를 우동으로 바꾸는 것
fork()와 exec()
fork(): 새로운 프로세스를 복사하는 시스템 호출
exec(): 프로세스는 그대로 둔 채 내용만 바꾸는 시스템 호출
exec() 시스템 호출을 하면 현재의 프로세스가 완전히 다른 프로세스로 전환
  • exec() 시스템 호출을 사용하는 목적: 프로세스의 구조를 재활용하기 위함
  • 새로운 프로세스를 만들려면 PCB를 만들고 메모리의 자리를 확보하는 과정 필요
  • 프로세스를 종료한 후 사용한 메모리 청소(garbage collection)하기 위해 상위 프로세스와 부모-자식 관계 만들어야 함
  • exec() 시스템 호출을 사용하면 이미 만들어진 PCB, 메모리 영역, 부모-자식 관계 그대로 사용 가능 → 편리
exec() 시스템 호출
exec() 시스템 호출은 이미 만들어진 프로세스의 구조를 재활용하는 것

 

exec() 시스템 호출의 동작 과정

  • exec() 호출 시 코드 영역에 있는 기존 내용이 지워지고 새로운 코드로 바뀜
  • 데이터 영역이 새로운 변수로 채워지고 스택 영역이 리셋됨
  • PCB의 일부 내용 변경(프로세스를 처음 시작하는 것처럼 내용 정리)
    • PID, PCID, CPID, 메모리 관련 사항 등은 변하지 않음
    • 프로그램 카운터 레지스터 값을 비롯한 각종 레지스터와 사용한 파일 정보가 모두 리셋

exec() 시스템 호출 후 프로세스의 변화

  • fork() 시스템 호출: 새로운 프로세스 하나를 복사
  • exec() 시스템 호출: 기존 프로세스 구조를 그대로 둔 채 내용만 바꾸어 새로 실행하는 것

프로세스의 계층 구조

유닉스의 프로세스 계층 구조

유닉스의 프로세스 계층 구조

  • 커널이 처음 메모리에 올라와 부팅되면 커널 관련 프로세스를 여러 개 만듦
  • init 프로세스는 전체 프로세스의 출발점이 됨
  • 효율적인 프로세스 관리를 위해 init 프로세스를 만든 다음 나머지 프로세스를 init 프로세스의 자식으로 만듦 → 트리 구조
  • init 프로세스는 일반 사용자 프로세스의 맨 위에 위치, fork()와 exec() 시스템 호출을 이용하여 자식 프로세스를 만듦

 

프로세스 계층 구조의 장점

동시에 여러 작업을 처리하고 종료된 프로세스의 자원을 회수하는데 유용함

  • login 프로세스 - 여러 작업의 동시 처리
    • login 프로세스는 인증을 거쳐 컴퓨터에 접속하는 과정 처리
    • [예시] 사용자 3명이 동시에 컴퓨터 접속
      1. login 프로세스는 한 번에 1명만 처리 가능
      2. 유닉스 운영체제는 여러 사용자를 동시에 처리하기 위해 fork() 시스템 호출로 login 프로세스 여러 개 생성
      3. 새로운 사용자가 들어올 때마다 이러한 작업 반복하여 여러 사용자 동시 처리
  • shell 프로세스
    • login 프로세스 통과 후 shell 프로세스가 필요
    • shell 프로세스가 있어야 사용자가 운영체제에 명령을 내리고 결과를 받을 수 있음
    • login 프로세스가 마치면 해당 프로세스가 종료되고 새로운 shell 프로세스가 생성됨
      1. 이때 login 프로세스가 차지하고 있던 메모리 공간이 비워짐
      2. PCD 제거
      3. shell 프로세스를 메모리에 올리기 위한 공간이 확보됨
      4. PCD 새로 생성
      5. 이미 자리를 차지한 login 프로세스를 없애고 새로운 shell 프로세스를 만드는 것은 비효율적
      6. exec() 시스템 호출을 사용하여 login 프로세스의 구조를 shell 프로세스로 다시 활용하면 효율적인 자원 관리 가능
      7. 기존의 부모-자식 관계 유지 가능
    • 명령어로 응용프로그램을 실행 시에도 fork()와 exec() 시스템 호출 이용

프로세스의 재사용

  • 용이한 자원 회수
    • 프로세스를 계층 구조로 만들면 프로세스 간의 책임 관계가 분명해져 시스템 관리하기 수월함
    • 특히 프로세스가 작업 종료 후 그 프로세스가 사용하던 자원을 회수할 때 편리함
    • 모든 프로세스가 독립적이라면? → 운영체제가 직접 자원 회수 → 복잡
    • 모든 프로세스를 부모-자식 관계로 만들면? 자식 프로세스가 작업을 마쳤을 때 사용하던 자원을 부모 프로세스가 회수하면 됨

 

고아 프로세스와 좀비 프로세스

  • 고아 프로세스(orphan process)
    • 자식 프로세스가 종료되기 전에 부모 프로세스가 먼저 종료되면 자식 프로세스는 고아 프로세스가 됨
    • 부모 프로세스가 먼저 종료되어 돌아갈 곳이 없는 프로세스
  • 좀비 프로세스(zombie process)
    • 자식 프로세스가 종료되었는데도 부모 프로세스가 뒤처리를 하지 않을 때 발생

고아 프로세스나 좀비 프로세스가 많아지면 자원이 낭비되어 효율적인 운영에 방해가 된다.

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

컴퓨터가 느려지는 현상
컴퓨터를 오래 켜두면 부팅할 때는 없었던 각종 프로세스가 메모리에 상주하는 데다 좀비 프로세스가 메모리를 차지하기 때문에 느려진다. 따라서 프로세스가 종료되면 그 프로세스가 사용한 메모리 공간을 깨끗이 청소해야 한다. 제대로 청소하지 않으면 메모리에 찌꺼기가 남아 다른 작업에 영향을 미친다. 다시 부팅하면 컴퓨터가 빨라지는데, 이는 메모리에서 찌꺼기가 사라졌기 때문이다.

스레드

운영체제는 프로세스의 낭비 요소를 제거하고 프로세스 작업의 유연성을 얻기 위해 멀티스레드를 사용한다.

 

스레드의 개념

스레드의 정의

  • [요리사 모델 예시] 안심 스테이크를 만드는데 안심 스테이크를 만들기 위해 준비하는 과정을 프로세스 생성 과정으로, 레시피의 정해진 절차에 따라 요리하는 것을 스레드로 볼 수 있다. 프로세스는 요리 작업 전체와 같고, 스레드는 요리를 완성하기 위해 수행하는 각각의 조리에 해당하는 것이다.
  • 프로세스의 작업 과정
    • 운영체제
      1. 코드와 데이터를 메모리에 올려서 PCB를 생성
      2. 작업에 필요한 메모리 영역 확보
      3. 준비된 프로세스를 준비 큐에 삽입
    • 프로세스가 생성되면 CPU 스케줄러는 프로세스가 해야 할 일을 CPU에 전달하고 실제 작업은 CPU가 수행
    • 이때 CPU 스케줄러가 CPU에 전달하는 일 하나가 스레드
      → CPU가 처리하는 작업의 단위는 프로세스로부터 전달받은 스레드가 됨
    • 프로세스: 운영체제의 입장의 작업 단위
    • 스레드: CPU 입장의 작업 단위
스레드(thread)
프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위

 

작업의 크기
작업을 상대적인 크기순으로 나열하면 job > task > operation
프로세스와 스레드의 관계에 대입하면 처리(job) > 프로세스(task) > 스레드(operation)
여러 개의 스레드가 모여 프로세스를 이루고 여러 개의 프로세스가 모여 처리가 된다.
여러 개의 프로세스를 모아서 한꺼번에 처리하는 방법을 일괄 작업(batch job)이라고 한다.

 

프로세스와 스레드의 차이

프로세스끼리는 약하게 연결되어 있는 반면 스레드끼리는 강하게 연결되어 있다.

스레드 관련 개념의 차이

  • 멀티스레드
    • 프로세스 내 작업을 여러 개 스레드로 분할함으로써 작업의 부담을 줄이는 프로세스 운영 기법
  • 멀티태스킹
    • 운영체제가 CPU에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법
    • 여러 스레드에 시간을 잘게 나누어 주는 시스템을 시분할 시스템이라고 함
    • 시분할 시스템에서 운영체제가 CPU에 전달하는 작업은 프로세스가 아니라 스레드
  • 멀티 프로세싱
    • CPU를 여러 개 사용하여 여러 개의 스레드를 동시에 처리하는 작업 환경을 말함
    • 컴퓨터 하나에 CPU가 여러 개 설치되어 동시에 작동하는 것이 멀티 프로세싱
    • 하나의 CPU 내 여러 개의 코어에 스레드를 배정하여 동시에 작동하는 것도 멀티 프로세싱
  • CPU 멀티스레드
    • 한 번에 하나씩 처리해야 하는 스레드를 잘게 쪼개어 동시에 처리하는 명령어 병렬 처리 기법
    • 인텔 CPU의 경우 하이퍼 스레드라 명명함
    • 프로세스의 멀티스레드와 구분하기 위해 CPU 멀티스레드라고 부름
      1. 멀티스레드: 운영체제가 소프트웨어적으로 프로세스를 작은 단위의 스레드로 분할하여 운영하는 기법
      2. CPU 멀티스레드: 하나의 CPU에서 여러 스레드를 동시에 처리하는 것

멀티스레드의 구조와 예

멀티스레드의 구조

  • 멀티태스킹의 경우 fork() 시스템 호출 사용 → 코드 일부, PCB, 공유 변수가 메모리의 여러 곳에 중복 → 메모리 낭비
  • 스레드는 이러한 멀티태스킹의 낭비 요소를 제거하기 위해 사용
  • 비슷한 일을 하는 2개의 프로세스를 만드는 대신 코드, 데이터 등을 공유하면서 여러 개의 일을 하나의 프로세스 내에서 하는 것

멀티태스킹 vs 멀티스레드

  • 정적인 영역: 프로세스가 실행되는 동안 바뀌지 않는 영역
  • 동적인 영역: 레지스터 값, 스택, 힙 등
  • 스레드 - 가벼운 프로세스(LWP; Light Weight Process)
  • 스레드가 1개인 프로세스 - 무거운 프로세스(HWP; Heavy Weight Process)

멀티스레드의 장단점

멀티스레드의 장점

  1. 응답성 향상
    • 한 스레드가 입출력으로 인해 작업이 진행되지 않더라도 다른 스레드가 작업을 계속하여 사용자의 작업 요구에 빨리 응답 가능
  2. 자원 공유
    • 한 프로세스 내 독립적인 스레드를 생성하면 프로세스가 가진 자원을 모든 스레드가 공유하게 되어 작업을 원활하게 진행 가능
  3. 효율성 향상
    • 여러 개의 프로세스를 생성할 필요가 없어 불필요한 자원의 중복을 막음으로써 시스템의 효율이 향상
  4. 다중 CPU 지원
    • 2개 이상의 CPU를 가진 컴퓨터에서 멀티스레드를 사용하면 다중 CPU가 멀티스레드를 동시에 처리하여 CPU 사용량이 증가하고 프로세스의 처리 시간이 단축됨

멀티스레드의 단점

  • 모든 스레드가 자원을 공유하기 때문에 한 스레드에 문제가 생기면 전체 프로세스에 영향을 미침
  • [예시] 인터넷 익스플로러 - 멀티스레드
    • 프로세스는 하나이고 그 안에서 여러 개의 스레드 사용
    • 문제 발생 시 스레드만 종료되는 것이 아닌 프로세스 전체가 종료됨
  • [예시] 크롬 - 독립적인 프로세스
    • 문제 발생 시 다른 화면에 미치는 영향이 작음
  • 현재 메모리가 넉넉하고 멀티코어 CPU가 대중화되어 여러 개의 프로세스를 여러 개의 CPU에서 동시에 실행할 수 있게 됨
  • 크롬은 다른 스레드가 영향받는 것을 최소화하기 위해 나입 요소가 있더라도 멀티스레드 대신 멀티태스킹을 이용

멀티스레드 모델

프로세스가 커널 프로세스와 사용자 프로세스로 나뉘듯이 스레드도 커널 스레드와 사용자 스레드로 나뉜다.

  • 커널 스레드: 커널이 직접 생성하고 관리하는 스레드
  • 사용자 스레드: 라이브러리에 의해 구현된 일반적인 스레드

사용자 스레드가 커널 스레드를 사용하려면 시스템 호출을 통해야 한다.

 

사용자 스레드(1 to N 모델)

  • 커널이 멀티스레드를 지원하지 않을 때 사용하는 방법, 초기의 스레드 시스템에 사용됨
  • 사용자 레벨에서 라이브러리를 사용하여 스레드를 구현
  • 사용자 스레드 라이브러리는 커널이 지원하는 스케줄링이나 동기화 같은 기능을 대신 구현해줌
  • 커널 입장에서 사용자 스레드는 하나의 프로세스처럼 보임
  • [예시] 하나의 넷플릭스 아이디를 여러 사람이 돌아가면서 사용하는 경우
  • 장점: 사용자 스레드에서는 문맥 교환가 같은 부가적인 작업이 줄어들어 속도가 빠름
  • 단점
    • 여러 개의 스레드가 하나의 커널 스레드와 연결
      → 커널 스레드가 입출력 작업을 위해 대기 상태에 들어가면 모든 사용자 스레드가 같이 대기
    • 한 프로세스의 타임 슬라이스를 여러 스레드가 공유
      → 여러 개의 CPU를 동시에 사용할 수 없음
    • 보안에 취약
      → 커널 레벨에서는 공유 변수를 보호하는 장치가 있으나 이런 서비스를 커널이 아닌 라이브러리에서 구현해야 하기 때문

 

커널 스레드(1 to 1 모델)

  • 커널이 멀티스레드를 지원하는 방식
  • 하나의 사용자 스레드가 하나의 커널 스레드와 연결되기 때문에 1 to 1 모델이라 부름
  • 독립적으로 스케줄링됨 → 특정 스레드가 대기 상태에 들어가도 다른 스레드는 작업을 계속할 수 있음
  • 커널이 제공하는 보호 기능과 같은 모든 기능 사용 가능
  • 장점
    1. 커널 레벨에서 모든 작업 지원 → 멀티 CPU 사용 가능
    2. 하나의 스레드가 대기 상태에 있어도 다른 스레드가 작업 가능
    3. 보안에 강하고 안정적으로 작동
  • 단점: 문맥 교환을 할 때 오버헤드 때문에 느리게 작동

 

멀티레벨 스레드(M to N 모델)

  • 멀티레벨 스레드 또는 하이브레드 스레드는 사용자 스레드와 커널 스레드를 혼합한 방식
  • 사용자 스레드에서는 커널 스레드의 개수가 사용자 스레드보다 적음
    → 멀티레벨 스레드에서는 커널 스레드의 개수가 사용자 스레드보다 같거나 적음
  • 장점: 하나의 커널 스레드가 대기 상태에 들어가면 다른 커널 스레드가 대신 작업 → 사용자 스레드보다 유연하게 작업 처리
  • 단점: 커널 스레드를 같이 사용 → 문맥 교환 시 오버헤드가 존재
  • 빠르게 움직여야 하는 스레드는 사용자 스레드로, 안정적으로 움직여야 하는 스레드는 커널 스레드로 동작

사용자 스레드 / 커널 스레드 / 멀티레벨 스레드


동적 할당 영역과 시스템 호출

프로세스의 동적 할당 영역

프로세스의 상세 구조

  • 코드 영역(프로그램의 본체 존재)과 데이터 영역(프로그램이 사용하려고 정의한 변수와 데이터 존재)은 프로세스가 실행되기 직전에 위치와 크기가 결정되고 실행되는 동안 변하지 않으므로 정적 할당 영역이라고 부름
  • 스택 영역과 힙 영역은 프로세스가 실행되는 동안 만들어지는 영역, 크기가 늘기도, 줄어들기도 하는 동적 할당 영역

 

스택 영역

  1. 호출한 함수가 종료되면 함수를 호출하기 전 코드로 되돌아와야 하는데 되돌아올 메모리의 주소를 스택에 저장
  2. 스택은 변수 사용 범위에 영향을 미치는 영역(scope)를 구현할 때 사용.
    • 특정 함수에서만 사용하는 지역 변수는 함수가 호출 될 때만 사용되다가 함수가 종료되면 사용한 공간을 반환해야 함
    • 지역 변수를 저장할 때 스택 사용
  3. 스택은 프로세스를 작동하기 위해 커널이 유지하는 자료구조
  4. 스택을 더 정확하게 표현하면 스레드가 작동하는 동안 추가되거나 삭제되는 동적 할당 영역으로 정의 가능

 

힙 영역

  1. 동적으로 할당되는 변수 영역
  2. 대부분의 데이터는 데이터 영역에 할당되고 그 크기가 정해짐
  3. 일부 데이터는 프로그램이 실행되는 동안 할당됨
  • 프로세스 제어 블록 큐
    1. 새로운 프로세스 생성 시 malloc() 함수를 사용하여 PCB 구조체를 만들고 해당 큐에 삽입
    2. 프로세스 종료 시 free() 함수를 사용하여 해당 PCB가 차지하던 메모리 공간 반환

PCB 큐


exit()와 wait() 시스템 호출

exit() 시스템 호출

main() {
    printf("Hello\n");
    exit(0);
}
  • main() 함수의 맨 마지막 줄에 exit() 혹은 return() 문을 사용하는 이유
    • 자식 프로세스가 끝났음을 부모 프로세스에 알려주기 위함
    • exit() 함수는 작업의 종료를 알려주는 시스템 호출
  • 모든 프로세스는 부모-자식 관계를 가짐
  • exit() 함수를 선언함으로써 부모 프로세스는 자식 프로세스가 사용하던 자원을 빨리 회수 가능
  • exit() 함수는 전달하는 인자를 확인하여 자식 프로세스가 어떤 상태로 종료되었는지를 알려줌
    • 0: 정상 종료
    • -1: 비정상 종료

 

wait() 시스템 호출

  • 운영체제는 부모 프로세스가 먼저 종료됨으로써 고아 프로세스가 생기는 것을 방지하기 위해 wait() 시스템 호출을 사용
  • [예시] 윈도우 운영체제에서 창을 여러개 띄웠을 때
    • 맨 앞의 창에서만 키보드와 모니터 사용 가능
    • 맨 앞에서 작동하는 프로세스를 전면 프로세스(foreground process)
    • 뒤에서 작동하는 프로세스를 후면 프로세스(background process)
    • 전면 프로세스에서는 작업이 끝날 때까지 셸이 다음 명령어의 입력을 받아들이지 않는 반면, 후면 프로세스에서는 프로세스를 실행한 후 셸이 바로 다음 명령어를 실행할 준비를 함
    • 전면 프로세스에서는 셸이 wait() 함수를 사용하기 때문에 자식 프로세스가 끝날 때까지 다음 명령어를 입력받을 수 없지만, 후면 프로세스에서는 셸이 wait() 함수를 사용하지 않기 때문에 다음 명령어를 받아들일 수 있음
반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...