[운영체제] 프로세스 관리

ryun·2023년 3월 22일
0

운영체제

목록 보기
5/17

📍 프로그램의 실행

  • 프로그램 실행하면 메모리에 올라가서 프로세스가 된다
  • 운영체제 커널은 기본적으로 메모리에 올라가있다 (핵심)

버추얼 메모리
중간에 한 단계 더 있는 것

  • 독자적인 주소 공간이 만들어진다 (각 프로그램의 머릿속에 존재)
  • 각 프로세스만의 주소

버츄얼 메모리 공간 3개

  • 코드
    실행 파일에 있던 코드가 올라옴 (실행할 기계어)
  • 데이터
    전역 변수 혹은 프로그램 시작해서 종료할 때까지 남아있는 데이터
  • 스택
    모든 데이터가 아닌 함수 안에 있는 지역변수같은 데이터
    모든 프로그램은 함수구조로 되어 있는데 함수가 끝나면 리턴되어 되돌아오는 정보

스왑 메모리

  • 당장 필요한 부분은 물리적 메모리에 올라가고,
    올라가있지 않은 부분은 스왑 메모리에 내려가있게 된다

📍 커널 주소 공간

커널도 함수구조
커널도 코드 / 데이터 / 스택으로 되어 있다

  • 커널 코드
    • 커널에 함수형태로 들어가 있는 코드
    • 운영체제에 인터럽트 들어왔을때 뭘 처리해야하는지
  • 데이터
    • 모든 하드웨어와 프로세스들을 관리하기 위한 자료구조
    • PCB 프로세스 컨트롤 블럭 (이후에 나온다)
  • 스택
    • 스택은 각 프로세스마다 별도로 존재
    • 운영체제 함수구조로 되어 있기 때문에 함수가 다른 함수를 호출하면 스택 사용

📍 사용자 프로그램 함수 3가지

어떤 프로그램이든간에 함수로 구성되어 있다

사용자 정의 함수

  • 내 프로그램에서 정의한 함수

라이브러리 함수

  • 가져다 쓰기만 하는 함수

커널 함수

  • 운영체제 안에 있는 함수 (커널에 있음)
    ex) 디스크 파일읽기 > 커널에 CPU 제어권이 넘어가서 실행된다

커널함수 특징

사용자 정의 함수 / 라이브러리 함수 수행은 내 프로그램 안에서 프로그램 카운터만 바꾸어서 다른 위치 기계어 실행
운영체제의 커널 함수 호출하는 것은 가상메모리 주소공간을 가로지르는 것으로
CPU 제어권을 운영체제한테 넘겨야 하기 때문에 인터럽트 라인을 세팅해서 CPU 제어권이 운영체제한테 넘어가게 한 다음에 실행한다


시스템 콜 부르면 CPU 제어권이 운영체제한테 넘어감 >
커널 모드에서 운영체제 주소공간에 있는 코드가 실행 >
프로그램하나 실행될 때마다 유저모드 커널모드 계속 반복
(모드빗이 계속 변경)

📍 프로세스

문맥

프로세스의 현재 상태를 나타내는 중요한 요소가 담겨있다
(과거의 상태, 함수 몇개 호출, 어디 실행중, 주소공간 등)

운영체제는 각 프로세스마다 PCB 가지고 있다
CPU를 얼마나 쓰고 메모리 얼마나 썼는지 알 수 있다 > 이걸 봐야 문맥을 알 수 있다

프로세스 상태

CPU에서 기계어를 수행하고 있는 프로세스는 매 순간 하나가 존재한다

  • Running
    • 기계어 수행하고 있는 프로세스
  • Ready
    • CPU 기다리고 잇는 프로세스
  • Blocked
    • CPU에서 기계어 실행이 불가능한 프로세스
      블럭상태는 꼭 하드웨어가 오래걸려서 그런 건 아니다 => 원하는 이벤트가 발생하지 않아서 줘봤자 일을 못하는 상태
      공유데이터 누가 쓰고있으면 또 쓰려는 다른 프로세스는 블럭 상태
      ex) 디스크에서 파일 읽어야 하는 것 (오래걸리는 작업 끝날 때까지는) cpu 얻어봐야 무의미한 상태

운영체제가 PCB를 통해서 관리한다

  • 생성중인 상태, 종료중인 상태가 프로세스
    • 생성되거나 종료되었으면 프로세스가 아니다
      온전하게 프로세스가 되면 레디상태 >
      레디상태는 CPU 주면 바로 실행(당장 필요한 부분은 메모리에 올라가 있어야 한다) >
      CPU 스케줄러가 프로세스한테 CPU 주면 러닝

CPU를 내어주는 경우 3가지

  1. 타이머 스케줄러 걸려서 ReadyQueue 뒤에 줄서기
  2. 오래걸리는 작업에 들어가서 블럭상태로 들어가는 프로세스
    (오래걸리는 작업이 끝나면 인터럽트 걸어서 레디로 바꾼 뒤 다시 줄세운다)
  3. 작업이 다 끝나서

📍 PCB(Process Control Block)

운영체제는 PCB를 통해 각 프로세스를 관리한다

  • 프로세스마다 하나씩 PCB를 가지고 있다
  • 프로세스 상태, 아이디, 스케줄링 되기위한 우선순위같은 정보(모든 프로세스가 대등하지 않다)
  • 각각의 프로세스마다 현재 문맥들의 정보를 전부 pcb에 따로 보관한다
    왜? 매번 PCB 뺏길 때 어디까지 수행했는지 문맥 저장해놔야 다음에 CPU에서 실행됐을 때 그 다음지점부터 실행이 가능하다

📍 문맥교환

하나의 프로세스에서 다른 프로세스로 CPU가 넘어가는 것 (반드시 커널 개입)
PCB를 통해 빼앗기는 프로세스의 문맥을 저장하고 얻는 프로세스의 문맥을 복원한다

문맥교환과 아닌 것을 헷갈리지 말자

문맥 교환이 아닌 경우

  • a 실행되다가 인터럽트나 시스템 콜 들어와서 커널로 넘어갔다가 다시 a로 넘어가는 것
    인터럽트는 다른 프로그램으로 인해 들어올 수 있다

  • a 실행되다가 키보드 입력 요청 > 키보드 컨트롤러가 인터럽트 > a에서 커널에게 넘어감 > 이걸 커널이 레디큐에 키보드 요청을 넣는다 > 그 다음에 다시 a로 넘어감

문맥 교환인 경우

  • a에게 주어진 할당시간 끝나서 타이머 인터럽트 > B에게 CPU를 넘긴다

  • I/O 같이 오래걸리는 작업을 커널에게 요청함 > 커널이 I/O 컨트롤러에게 부탁 > I/O 작업하는 동안 레디큐에 있는 b에게 CPU 넘겨줌

  • 문맥교환을 할 때는 오버헤드가 크다 (캐시메모리 때문)

📍 프로세스 스케줄링 큐

  • Job queue
    • 실행중인 모든 프로세스
  • Ready queue
    • CPU를 당장 얻어도 되는 것이 레디큐
  • Device queue
    • 오래걸리는 I/O작업

  • PCB가 각 프로세스마다 하나씩 있다
  • 시스템 안에는 프로세스가 여러 개 있다
    스케줄링 되는 예를 보여주는 모습
    I/O 요청 / 타이머 / 자식 프로세스 / 인터럽트

📍 스케줄러

운영체제에서 스케줄링을 하는 코드 (결국 운영체제)

장기 스케줄러(=메모리 스케줄러)

  • 메모리에 들어오도록 허락
  • 타임쉐어링 시스템에는 보통 장기 스케줄러가 없음
  • 현대 운영체제가 장기스케줄러는 채택하지 않고 있다 > 실행시키면 바로 메모리에 올라가기 때문
    *디그리 오브 멀티프로그래밍 : 메모리 올라간 프로그램 몇 개냐
    이걸 조절하는 역할을 장기스케줄러가 한다

단기 스케줄러

  • 누구에게 얼마나 CPU를 줄지 결정
  • 자주 호출
  • 빠른 단위로 수행

중기 스케줄러

  • 메모리가 너무 부족할 때 특정 프로세스를 통째로 메모리에서 쫓아내는 것
  • 스왑영역으로 보낸다

  • Suspended
    • 중기 스케줄러때문에 메모리에서 쫓겨난 상태
    • 외부적인 이유
      (사람이 정지시키는 경우 등)

블락과 서스펜디드의 특징

  • 둘다 일을 아예 못하는 정지된 상태
  • 둘 다 CPU 얻지 못한다
  • 블럭 상태는 원인이 해당하는 이벤트가 발생하면 다시 돌아갈 수 있다 (예를 들어 I/O 요청끝나면)
  • 서스펜드에서 메모리로 올라가는 것은 발생시킨 원인이 해결되어야 한다

액티브 상태

Running

  • 러닝은 두개로 쪼개져있다(유저모드, 커널)
    프로세스가 자기 코드를 수행중이면 유저모드에서 러닝
    (운영체제가 러닝이다 라는게 아님, 운영체제는 본인의 상태를 스스로 표현하지 않는다)

Ready

Blocked

  • 블럭은 cpu만 얻지 못한거지 I/O 등을 스스로 열심히 일을 하고 있는 상태 > 따라서 액티브하다고 한다

인액티브 상태

Suspended

  • 메모리 통째로 빼앗김
  • 메모리 얻는 법
    • 외부에서 메모리를 주면 다시 위에 위에 액티브한 상태로 올라간다
      (사람이 정지시켰으면 사람이 프로세스를 재개해줘야한다)
  • 서스펜디드 상태에서도 화살표가있다
    메모리 뺏김 > CPU 작업 할 수 없음 > I/O 하다가 들어갔으면 I/O작업은 할 수 있다 (I/O로 인해 블록 됐지만 메모리를 빼앗겨서 서스펜디드 됐을 때) > I/O 끝낫을때 서스펜디드 블록에서 서스펜디드 레디로 갈 수 있다

*스왑 아웃: 메모리에서 통째로 쫓겨나는것
스왑 인: 메모리로 들어오는 것

운영체제가 코드를 수행중일 때 운영체제를 불렀던 a라는 프로세스가 운영체제를 호출했던 a는 어떤 상태?
이 관점에서 CPU를 빼앗겼다기보다는 여전히 CPU에서 일하는걸로 간주하는게 더 타당


위 그림은 a의 입장에서는 다 러닝상태
b한테 뺏겼으면 화살표가 진행되는게 아니라 잘리고 다른 프로세스가 CPU를 잡아서 가는 것
여기는 A의 일생만 그린것으로 때문에 A는 쭉 러닝

참고)
디스크 I/O와 관련된 인터럽트는 하드웨어 & 소프트웨어 인터럽트 둘 다!
디스크 I/O 하기 위해서는 본인이 못하기 때문에 운영체제한테 요청한다
I/O 작업 시작될 때는 시스템콜로 운영체제한테 요청 > CPU가 디스크 컨트롤러에게 요청 (소프트웨어 인터럽트)
I/O가 다 끝나면 디스크 컨트롤러가 CPU한테 인터럽트 걸어서 끝낫다고 알려줌(하드웨어 인터럽트)

사용자 프로그램 코드 실행되다가 하드웨어, 소프트웨어 각종 인터럽트 들어와서 CPU가 운영체제한테 넘어갔더라도 방금 전의 프로세스가 여전히 운영체제 모드 여전히 러닝하고 있다.

  • 인터럽트 코드 수행 도중에 더 우선순위가 높은 인터럽트가 들어올 수도 있다 (제자리로 도는 화살표)
  • 커널 코드가 다 끝나고 나서 사용자 프로세스한테 되돌아가면 유저모드의 사용자 코드가 수행

📍 스레드

프로세스 중에서 CPU 수행 단계

원래 프로세스는 메모리 주소 공간이 있다 (코드 데이터 스택)
운영체제는 이걸 관리하기 위해 PCB를 두고 있다

프로그램 실행할 때 브라우저 여러개 띄울 수 있다 > 여러 프로세스가 되는 구조 > 각 브라우저가 각각의 코드 데이터 스택을 가진 메모리 주소공간이 만들어지고 PCB가 하나씩 만들어질 것

이를 효율적으로 하기 위해 스레드를 사용

  • 즉, 여러 프로그램 띄우더라도 프로세스 하나만 만드는 것
    코드 데이터 스택 구조가 하나가 될 수 있다
    다른 것들은 프로세스 쉐어
  • 프로그램 카운터 값만 스레드에서 각각의 위치를 나타나게 한다
    CPU 수행과 관련된 부분만 별도, 그 외는 단일 프로세스로 쉐어하는 구조

프로그램 카운터와 레지스터만 별도
나머지는 공유
독자절으로 가지는 부분은 스레드, CPU관련 부분
공유하는 부분은 태스크
스레드 구조가 되면 가벼운 프로세스가 된다


프로세스 하나를 관리하기 위한 구조
스레드에 필요한 정보만 뽑아서 관리를 한다
(둘 다 같은 내용의 그림)

스레드 장점


프로세스 a에서 프로세스 b로 넘어가는 컨텍스트 스위치에서는 오버헤드가 많다
하지만 스레드1에서 스레드2로 넘어갈 때는 오버헤드가 없고 더 효율적이다

  • 빠른 응답
    • 하나 스레드가 읽어오는 동안에 또 다른 스레드가 바로 화면에 출력 (사용자 편-안)
  • 자원 공유효과
    • 스레드간 자원을 공유
  • 경제적
    *위 세 가지는 일반적으로 CPU 한 개 있는 환경
    마지막은 CPU 여러 개 있는 환경에서의 효과
  • 병렬성 추구 가능
    • 시피유가 여러 개 있을 때는 각각의 스레드를 서로 다른 CPU에 배치하면 결과가 더 빠르다

스레드 구현 방법


사용자 프로그램 단에서 스레드를 관리하는것은 유저 스레드
커널이 직접 지원 해주는 스레드를 사용하는게 커널 스레드
스레드는 빠른 처리 가능해서 리얼타임의 목적으로 사용되기도 한다

프로세스 관리

  • 부모 프로세스가 자식 프로세스를 만든다
  • 직접 못하는 작업은 운영체제한테 시스템 콜로 요청할 수 있다
    • 자식프로세스 만들어 달라는 커널의 함수 호출
  • 계층 구조가 트리 형태로 만들어진다
    • 맨 위에 최초의 프로세스
      자식이 여러 개면 트리형태
  • 경우에 따라 자원을 부모와 공유
    • 원칙적으로 공유하는게 아니라 자원을 가지고 싸우는 형식 (일반적)
  • 자식 프로세스가 만들어지면 둘은 별개의 프로세스기 때문에 각자 수행
    • 부모와 자식간의 관계가 경쟁하는 관계 혹은 자식이 종료될 때까지 부모가 기다리는(블록) 모델

  • 프로세스가 만들어지면 독자적 주소공간이 만들어진다
    • 주소공간은 부모를 그대로 복사
      (부모의 코드 / 데이터 / 스택 주소공간을 그대로 복사)
      (전역변수나 데이터도)
    • 현재 부모가 수행하고 있는 위치부터 자식이 실행
  • 부모를 복제하고 덮어씌워서 완전히 새로운 프로그램 돌리는 것 > exec() 시스템 콜

프로세스 종료

  • exit
    • 모든 자원 반납
    • 부모한테 종료한다는 사실 알린다 (항상 자식이 먼저 죽는다)
      • 정상적으로 종료되면 부모에게 통보
      • 자식이 나쁜짓을 해서 부모가 강제로 죽이는 abort
  • 부모가 종료되기 전에 자식을 먼저 종료시키고 부모가 종료한다
    프로세스 계층 구조의 트리의 자식 말단부터 차례로 종료
    ex) 창 띄워놓고 프로그램 돌리다가 창 끄면 다 꺼지는것
    창이 부모, 그 안의 프로그램은 자식들

C언어로 된 자식 복제 코드

포크 하는 순간 동일한 구조의 프로세스가 하나 더 생성

  • 부모와 자식은 fork 한 다음부터 실행하게 된다
  • 부모 자식 구분
    • 포크 시스템 콜을 호출한 결과값인 return value가 부모 프로세스인 경우에는 양수
    • 자식 프로세스인 경우에는 0


위는 헬로우 호출하고 다른 프로그램을 덮어 씌우는 함수
프로그램 이름(경로 포함) 따옴표 안에다가 적어주고, 세 번째 아규먼트는 프로그램에 전달할 아규먼트, 마지막에 0 전달
헬로우 프로그램이 아니라 새 프로그램으로 덮어 씌워진다
뒤에 다른 내용을 덧붙이면 헬로우 출력, execlp 하는 순간 새로운 프로그램 덮어씌워지기 때문에 그 아래는 실행될 수 없다
보통 포크하고 자식한테 새 프로그램 돌리고 나는 그냥 계속 돌리는 방식으로 사용


0개의 댓글