CS/operation system

3. 프로세스와 스레드

hanjongho 2021. 10. 5. 00:07

프로세스 : 프로그램이 실행중인 상태(메모리에 올라가 있음)로 프로그램 + PCB

  • 커널 프로세스, 일반(사용자) 프로세스 두 가지로 존재 : 사용자 프로세스가 커널 기능을 사용하려면 시스템 콜을 이용해야하고, 커널 프로세스는 사용자 모드 → 커널 모드로 전환되어 요청을 처리하는 이중모드를 지원함. 자원을 보호하기 위함이며 사용자가 이렇게 커널에 접근하는 건 시스템콜을 이용한 자발적 접근과 인터럽트에 의한 비 자발적 접근이 있음

프로세스의 상태

  • 생성 : 프로그램이 메모리에 올라와 프로세스로 생성이 되고, PCB가 생성된 실행 준비가 완료된 상태
  • 준비 : CPU를 얻을 때 까지 기다리는 상태
  • 실행 : 해당 프로세스가 타임슬라이스를 얻어 CPU를 사용하는 상태. 프로세스 간 컨텍스트스위칭이 발생
  • 대기 : 실행 상태인 프로세스가 입출력을 요청하면 완료될 때까지 기다리는 상태. 완료되면 준비상태로 이동함
  • 완료 : 프로세스가 종료된 상태. 메모리, PCB 제거됨
  • 휴식 : 일시적으로 쉬는 상태. 메모리, PCB 유지됨
  • 보류 : 일시 정지 상태(메모리가 꽉 차서 일부 프로세스를 메모리 밖으로 내 보냈거나, 프로그램에 오류가 있어서 실행을 미룬 상태, 악의적인 프로세스, 입출력 지연 등) → 스왑영역으로 이동 됨

프로세스의 구조

  • 코드 영역(정적) : 프로그래머가 작성한 프로그램의 본문이 작성되어 있고 읽기 전용임 
  • 데이터 영역(정적) : 위 코드가 실행되면서 사용되는 변수나 파일 같은 데이터가 저장 됨
  • 힙 영역(동적) : 동적으로 할당되는 변수 영역(c - malloc(), java - new 등)
  • 스택 영역(동적) : 프로세스를 실행하기 위해 부수적으로 필요한 데이터를 보관 함. 프로세스를 실행하면서 함수를 호출했을 때 돌아올 메모리의 주소 저장, 지역변수 등을 저장

PCB(Process Control Block) : 프로세스에 관한 모든 정보들을 가지고 있고, 각 프로세스가 생성될때 같이 생성되고 프로세스가 완료되면 같이 제거

  • 포인터 : 첫번째 블록에 저장. 준비, 대기 상태의 큐를 구현할 때 이 포인터를 이용하여 PCB들을 관리 함. 예를 들어 대기 상태에서 같은 입출력을 기다리는 프로세스들은 이 포인터를 이용하여 큐를 구성해둔다. 이렇게 하면 인터럽트가 발생했을 때 대기 상태에서 해당 프로세스를 효율적으로 찾을 수 있음
  • 프로세스 상태 : 두번째 블록에 저장. 생성, 준비, 실행, 대기, 완료, 휴식, 보류 등이 있음
  • 프로세스 구분자 : 프로세스를 구분하기 위함
  • 프로그램 카운터 : 다음에 실행될 명령어의 위치를 가르킴
  • 프로세스 우선순위 : 커널 프로세스가 사용자 프로세스보다 우선순위가 높은 것처럼 이러한 정보를 가지고 있음
  • 메모리 관련 정보 : 해당 프로세스가 메모리 어디에 저장되어 있는지 담겨있는 위치 정보(경계, 한계 레지스터, 세그먼테이션, 페이지 테이블 포함) 
  • 할당된 자원 정보 : 프로세스를 실행하기 위해 사용되는 입출력 자원 등에 대한 정보를 가지고 있음
  • PPID와 CPID : 부모프로세스와 자식프로세스를 나타내는 정보

컨텍스트스위칭 : CPU를 차지하던 프로세스가 타임슬라이싱이 끝남에 따라 나가고 새로운 프로세스가 CPU 스케줄링에 의해 디스패치되어 들어오는 작업. 실행상태에서 나가는 PCB에는 지금까지의 작업내용을 저장하고 실행상태로 들어오는 PCB의 내용으로 CPU가 세팅 된다. 인터럽트가 발생하는 것 또한 인터럽트를 처리하는 프로세스가 실행되기 때문에 컨텍스트스위칭이 발생

 

프로세스 복사 : 시스템 콜을 통해 fork() 함수를 호출하면, PCB를 포함한 부모 프로세스 영역의 대 부분이 복사되어 같은 프로세스가 만들어진다.

  • 변경되는 부분 : 프로세스 구분자와, 위치하고 있는 메모리 주소, PPID와 CPID
  • 장점
    • 하드디스크에서 프로그램을 가져와서 올리는게 아닌 기존 프로세스를 복사하기 때문에 생성속도가 빠름
    • 부모 프로세스가 사용하던 모든 자원이 상속되어 바로 사용할 수 있음
    • 자식 프로세스가 종료되면 자원들이 부모 프로세스로 회수되어 효율적으로 관리할 수 있음

프로세스 전환 : 시스템 콜을 통해 exec() 함수를 호출하면, 프로세스는 그대로 둔 채 프로세스의 구조체(PCB, 메모리, 부모-자식 관계)를 재활용하여 완전히 다른 프로세스로 전환된다. 

  • 변경되는 부분 : 코드 영역을 새로운 프로그램 코드로 대체 → 데이터 영역 포함, 스택 영역 리셋. PCB 중 PC 비롯한 레지스터 값들이 처음 사용되는 것 처럼 리셋 됨. 

프로세스 계층 구조의 장점 : 여러 작업의 동시 처리, 부모 프로세스가 자원을 회수 하기 때문에 운영체제가 직접 자원을 회수할 필요가 없음. 이 구조는 객체지향 프로그래밍의 기본이 되는 구조로써 자바의 경우 모든 객체는 Object 객체의 자식이 되어 자원회수가 용이하다. 

 

좀비 프로세스 : 부모 프로세스가 먼저 종료되거나 자식프로세스가 비정상적으로 종료되어 부모 프로세스에 자원 회수가 되지 않는 경우. 자원이 낭비되어 효율적인 운영에 방해가 되고 운영체제가 직접 자원을 주기적으로 회수해야 함  

 


스레드(Thread) : CPU가 처리하는 작업의 단위

  • 과정(명령어가 실행되는 과정) : 스레드를 이루는 각 단계는 CPU의 1클록 당 1번씩 진행됨
    • 명령어 패치(IF, Instruction Fetch) : 다음에 실행할 명령어를 명령어 레지스터에 저장
    • 명령어 해석(ID, Instruction Decode) : 명령어를 해석 
    • 실행(EX, Execution) : 해석한 결과를 통해 명령어 실행
    • 쓰기(WB, Write Back) : 실행된 결과를 메모리에 저장

병렬처리 : 동시에 여러 개의 명령을 처리하여 작업의 능률을 높이는 방식

  • 파이프라인 기법 : CPU 사용을 극대화 하기위해, 명령을 겹쳐서 실행함. 여러 개의 명령어를 위의 4가지 과정들로 전부 분할한 뒤, 각 단계들을 동시에 처리하는 하드웨어를 독립적으로 구성함. 
    • 데이터 위험 : 데이터 의존성에 따른 문제. 첫번째 명령어에서 사용되는 데이터를 필요로 하는 두번째 명령어는 앞의 명령어가 끝날 때까지 동시에 실행되면 안됨 → 명령어 단계 지연으로 해결
    • 제어 위험 : 분기하는 if문이나 goto문을 통해 프로그램 카운터가 갑자기 변해버리면, 동시에 처리되고 있는 명령어가 무의미해짐 → 분기 예측이나 분기 지연 방법으로 해결
    • 구조 위험 : 서로 다른 명령어가 같은 자원에 접근할 때 발생 → 해결하기 어려움 
  • 슈퍼스칼라 기법 : 파이프라인 기법을 처리하는 코어를 여러 개 구성하여 복수의 명령어를 실행. 현대의 CPU의 구조
  • 슈퍼파이프라인 기법 : 파이프라인 기법에서 1클록 당 1번씩을 더 세분화하여 1클록 당 더 많은 명령어를 처리함. 슈퍼컴퓨터에 사용
  • 슈퍼파이프라인 슈퍼스칼라 기법 : 슈퍼파이프라인 기법을 처리하는 코어를 여러 개 구성하여 복수의 명령어를 실행. 슈퍼슈퍼컴퓨터에 사용(?)

멀티스레드 : 프로세스 내 작업을 소프트웨어적으로 여러 개의 스레드로 분할함으로써 작업의 부담을 줄이는 프로세스 운영 기법. 정적 영역에 대한 공유를 하고, 동적인 영역만 스레드 별로 갖기 때문에 효율적. 한 스레드가 입출력으로 인해 작업이 진행되지 않더라도 다른 스레드들은 작업을 하고 있어 더 빠르게 작업을 끝냄. 하지만 한 스레드에 문제가 생기면 전체 프로세스에 영향을 끼침.

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

멀티태스킹 : 운영체제가 CPU에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법. 

멀티프로세싱 : 여러개의 CPU 혹은 하나의 CPU 내 여러 개의 코어에 스레드를 배정하여 동시에 작동하는 것(슈퍼스칼라 기법)

CPU 멀티스레드 : 하나의 CPU에서 하드웨어적으로 여러 스레드를 동시에 처리하는 병렬 처리 기법