프로세스의 이해

Koo·2023년 9월 17일
post-thumbnail

프로세스

  • 실행중인 프로그램
  • 운영체제의 작업의 단위는 프로세스가 된다
  • 프로세스가 실행이 되기 위해서는 자원이 필요
    • CPU
    • memory
    • 파일, I/O 장치 등
  • 보조 기억장치에 저장된 프로그램(명령어의 집합)을 메모리에 올리면 CPU에 fetch하여 실행할 수 있음
  • CPU를 점유할 수 있어야 프로그램을 실행할 수 있음
  • 운영체제는 프로세스를 관리하는 작업을 해주어야 함

프로세스 구조

  • 텍스트 영역(Text section) - 명령어 집합이 있는 영역
  • 데이터 영역(Data section) - 전역변수를 저장하는 영역
  • 힙 영역(Heap section) - 메모리 할당을 할 때 사용하는 영역
  • 스택 영역(Stack section) - 함수를 호출할 때 사용하는 영역

텍스트 섹션, 데이터 섹션 위에 힙 섹션이 올라가고, 스택 섹션은 위에서부터 내려온다. 스택 섹션과 힙 섹션 사이의 공간이 부족해지면 다른 메모리 공간을 다시 할당받는다.

#include <stdio.h>
#include <stdlib.h>

int x;
int y = 15;

int main(int argc, char* argv[]){
	int* values;
    int i;
    
    values = (int*)malloc(sizeof(int) * 5);
    
    for(i=0;i<5;i++)
    	valeus[i] = i;
    
    return 0;
}
  • data는 다시 초기화된 데이터와 초기화되지 않은 데이터로 구분되어 저장됨
  • main함수, values, i는 stack 영역에 저장
  • 동적할당된 값은 heap 영역에 저장되고 values는 동적할당된 값의 주소를 가짐
  • 실행 코드들은 text 영역에 저장됨

프로세스의 생명주기(Life cycle)

  • New - 프로세스가 생성된 상태
  • Running - 프로세스가 CPU를 점유하여, 프로세스의 명령어를 CPU에 올려 실행하는 상태
  • Waiting - 프로세스가 다른 이벤트를 기다리는 상태
    • time sharing에서 다른 프로세스가 CPU를 점유할 때도 Waiting 상태가 됨
    • I/O 이벤트가 발생하면 I/O가 끝날 때까지 대기하는 상태
  • Ready - Waiting이 끝난 후 CPU 점유를 기다리고 있는 상태
    • I/O 이벤트가 끝난 후도 Ready 상태가 됨
  • Terminated - 프로세스가 실행이 끝난 후 종료된 상태

  • new - 프로세스가 만들어진 상태
  • ready - 프로세스가 만들어진 후 CPU 점유를 위해 ready queue에서 대기하는 상태
  • running - 운영체제가 이 프로세스에게 CPU를 할당한 상태
  • interrupt - 프로세스가 점유 시간이 너무 긴 경우 운영체제가 프로세스를 ready 상태로 만듬
  • I/O or event wait - I/O를 처리해야 하는 경우 ready queue로 가기 전에 waiting queue로 가서 대기
  • waiting - I/O 처리를 하고 있는 상태, 프로세스는 ready 상태로 갈 수 없음
  • I/O or event completion - I/O 처리가 끝난 후 프로세스가 ready queue로 가서 CPU 할당을 기다림
  • scheduler dispatch - 스케쥴러가 CPU를 dispatch해줌 → running 상태가 됨
  • exit - 명시적으로 프로그램을 끝내거나 return을 하여 프로세스를 종료함

프로세스 관리 방법

  • PCB(Process Control Block) - 블록에 프로세스가 가져야 하는 정보를 모두 저장
  • TCB(Task Control Block) - 리눅스에서 task를 용어를 많이 사용하여 쓰는 용어, PCB와 동일
    • Process state - new / run / wait / ready / exit
    • Program counter - IR로 메모리를 fetch해야 하는데 Program counter는 어디에 있는 메모리를 fetch할지를 알려줌
      • 1을 증가시키거나 명령어 길이만큼 증가시켜 다음 명령어를 가져옴
        - CPU registers - IR, PC, DR
    • CPU 스케쥴링 정보
    • memory 관리 정보
    • 계정 정보 - 어떤 계정이 사용하는 프로세스인지
    • I/O 상태 정보 - 어떤 파일을 사용하고 있는지

운영체제는 PCB들을 관리함

쓰레드

실행중인 프로그램은 기본적으로 하나의 쓰레드를 가짐

  • 메모리에는 쓰레드를 갖는 여러 개의 프로그램이 있음
  • 쓰레드를 PCB로 바꿔주고 CPU에서 실행함
  • time-sharing을 할 때 하나의 프로그램 쓰레드를 실행하다 다른 프로그램 쓰레드를 실행하고 다시 돌아오면 이어서 쓰레드를 실행함
  • 싱글 쓰레드는 한 번에 하나의 task만 처리할 수 있음
  • 하나의 프로그램 안에서도 여러 개의 쓰레드를 가져야할만큼 프로그램이 복잡해짐

쓰레드는 프로세스보다 작은 프로세스

  • 멀티 프로세싱보다 멀티 쓰레드를 하도록 바뀜
  • 여러 개의 쓰레드를 사용하는 것이 여러 개의 프로세스를 사용하는 것보다 효율적

멀티프로그래밍

  • 동시에 여러 개의 프로세스를 하는 것이 목적
  • CPU 사용 효율을 최대화하기 위해
  • time-sharing을 하는 이유는 CPU core를 프로세스간의 자주 스위치하여 사용자가 느끼기에 동시에 사용된다고 느끼게 하기 위해

스케쥴링 큐

  • time-sharing을 하기 위해서는 CPU를 스케쥴링 해주어야 함
  • 여러 개의 프로세스가 큐에서 대기하고 있다가 CPU가 사용가능하면 CPU를 점유하게 됨 → ready queue
  • I/O 처리가 있다면 프로세스가 waiting queue에 있다가 다시 ready queue로 가게 됨
  • 장치가 여러 개이기 때문에 waiting queue는 여러 개 존재함
  • ready queue와 waiting queue를 사용하면 CPU 스케쥴링을 할 수 있음

Queueing Diagram

  • ready queue에서 CPU를 획득한 후 다시 ready queue로 들어가는 경우
  • I/O request가 끝난 후 ready queue로 들어가는 경우
  • time slice가 만료된 후 기다릴 게 없어 ready queue로 들어가는 경우
  • 새로 fork한 후 new상태가 된후 ready queue로 들어가는 경우
  • interrupt가 발생한 후 ready queue로 들어가는 경우

Context Switch

  • 문맥을 교환하는 일을 운영체제가 해주어야 함
  • 문맥은 PCB를 의미함
  • 인터럽트가 발생하면 현재의 context를 저장함 (어디까지 실행되었는지)
  • context switch는 CPU core를 다른 프로세스에게 넘겨줄 때 발생
  • 현재의 프로세스의 상태(PCB, context)를 저장
  • 새로운 프로세스의 상태(PCB, context)를 복원

P0P_0P1P_1가 문맥교환을 하는 경우
1. P0P_0에서 interrupt를 발생
2. P0P_0의 상태를 저장
3. P1P_1의 상태를 불러옴
4. P1P_1에 interrupt가 발생해도 동일하게 수행

프로세스의 실행

운영체제는 프로세스를 생성하고 종료할 수 있어야 함

  • 프로세스는 새로운 프로세스들을 생성할 수 있음
  • 부모 프로세스 - 프로세스를 만드는 프로세스
  • 자식 프로세스 - 새롭게 생성되는 프로세스
  • fork()를 이용해 프로세스를 만들 수 있음

프로세스 실행의 2가지 상황

  1. 부모 프로세스가 자식 프로세스를 만든 후, 부모 프로세스와 자식 프로세스가 동시에 실행되는 경우
  2. 부모 프로세스가 자식 프로세스의 동작을 하는 동안 기다리는 경우

주소 공간의 2가지 상황

  1. 자식 프로세스의 동작이 부모 프로세스와 동일한 경우, 부모 프로세스의 주소 공간을 복사
  2. 자식 프로세스가 새로운 주소 공간을 할당받는 경우
#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main(){
        pid_t pid;
        pid = fork(); // 새로운 프로세스를 할당
        if(pid < 0){ // 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;
  • 부모 프로세스가 pid>0pid > 0pid=0pid = 0으로 분기됨
  • pid>0pid > 0pid=0pid = 0이 실행 완료될 때까지 대기
  • 실행 완료된 후 pid>0pid > 0도 실행이 종료됨

프로세스의 종료

  1. 마지막 문장(ex. return)을 실행
  2. exit() system call을 사용
  • 운영체제는 메모리, 열려있는 파일, I/O 버퍼 등을 회수하고 종료함

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

좀비 프로세스

  • 부모 프로세스도 실행하고 있지만 wait를 호출하지 않은 경우
  • 부모 프로세스가 계속 실행되는 상태에서 자식 프로세스도 계속 실행되고 있는 상태가 됨
  • 백그라운드 프로세스(데몬)를 만들 때 활용됨

고아 프로세스

  • 부모 프로세스가 wait하지 않고 종료되는 경우
  • 자식 프로세스는 종료되지 않고 부모 프로세스가 없는 상태로 남아있게 됨
profile
스터디를 해보자

0개의 댓글