Ch. 02 프로세스 (병행 프로세스)

지니🧸·2023년 9월 13일
0

운영체제

목록 보기
3/28

이 게시글은 <쉽게 배우는 운영체제> (함호종, 원종권 지음)을 공부하며 적은 개인 노트입니다.

1. 프로세스 소개

1.1 프로세스 정의

프로세스(Process)/태스크(Task): 실행 중인 상태의 프로그램

  • 비동기적(asynchronous) 행위
  • 실행 중인 procedure
  • 운영체제 내부에서 실행의 기본 단위
  • 운영체제 내에 PCB(Process Control Block) 존재

프로세스 특성

  • 자원 소유의 단워
    • 각각의 프로세스는 자신의 실행 이미지 적재와 실행에 필요한 추가적인 메모리 공간을 가져야 한다
    • 위는 각 프로세스마다 구별되어야 함
    • 해당 프로세스가 접근하고자 하는 파일, 입출력장치들은 CPU 단위로 할당받아 관리되어야 한다
  • 디스패칭의 단위
    • 프로세스는 하나의 프로그램이 운영체제로부터 CPU의 자원을 일정 기간 동안 할당받아 명령어를 실행하는 것
    • 운영체제는 여러 개의 프로세스가 병렬적으로 실행되게 하기 위해서 CPU의 사용 시간을 각각의 프로세스에 골고루 나눠야 함


[이미지 출처]

프로세스의 메모리 구조

프로세스는 실행 중인 프로그램임 >> 그 역할을 수행하기 위해서 CPU를 점유 해야 함

프로세스 점유란?
CPU 할당시간/메모리, 파일, 입출력 장치와 같은 자원을 사용해야 한다는 뜻

프로세스 주소 공간


[이미지 출처]

  • 코드 영역
    • CPU가 실행하는 코드를 저장하는 영역
    • Code segment
    • 프로그램의 코드는 변경되면 안되기 때문에 읽기만 가능
  • 정적 데이터 영역
    • 프로그램의 전역변수(global), 정적변수(static), 배열(array), 구조체(structure) 등을 저장하는 영역
    • Data segment
    • 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸됨
    • 함수 내부에 선언된 static 변수는 프로그램이 실행 될 때 공간만 할당되고, 그 함수가 실행 될 때 초기화됨
    • 전역변수, static 값을 참조한 코드는 컴파일 후 Data 영역의 주소값을 가르키도록 변경
  • BSS 영역
    • 초기값 없는 전역 변수, 정적 변수, 배열 등이 저장됨
  • 힙 영역
    • Heap segment
    • 자유 메모리 공간
    • 실행 중에 동적으로 할당 및 해제되는 메모리 공간
    • 저장 내용
      • 함수 및 함수 내 변수
      • 클래스, 클로저와 같은 참조형의 데이터 값
    • 읽고 쓰기가 가능하다
    • 스택 영역과는 반대로 메모리의 하위 주소에서 상위 주소 방향으로 추가 할당됨
    • 런타임에 크기 결정됨
  • 스택 영역
    • Stack segment
    • 지역(local) 변수, 매개변수(parameter), 리턴 값 등 잠시 사용되었다가 사라지는 데이터를 저장하는 영역
    • 원시타입의 데이터가 값과 함께 할당됨
    • Heap 영역에 생성된 Object 타입의 데이터 참조값이 할당됨
    • 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸
      • Stack frame: 스택 영역에 저장되는 함수의 호출 정보
    • 힙 영역과 반대로 메모리의 상위 주소에서 하위 주소 방향으로 추가 할당됨
    • 컴파일 타임에 크기가 결정되기 때문에 무한정으로 할당할 수 없음
      • 재귀함수가 너무 깊어지거나, 함수의 지역변수가 너무 많아져서 stack 영역을 초과하면 stack overflow 발생

프로세스 상태


[이미지 출처]

  • 실행
    • 명령어들이 실행되는 상태
    • 프로세스가 CPU를 점유하고 있는 상태
  • 대기/보류
    • 프로세스가 어떤 이벤트(입출력 종료 등)가 일어나기를 기다리는 상태
  • 준비
    • CPU에게 할당되기를 기다리는 상태

실행, 준비, 보류 상태의 프로세스는 메인 메모리에 상주하고, 생성 및 종료 상태의 프로세스는 메인 메모리 밖에 존재한다

  1. 운영체제는 새로운 프로세스를 생성하면 비실행 상태로 초기화하여 준비한다
  2. 생성된 프로세스는 실행되기를 기다린다
  3. 실행 중인 프로세스가 종료되거나 입출력을 기다리는 등 인터럽트를 당하면 비실행 프로세스 중 선택된 프로세스가 실행상태로 변한다 (이 때 중이던 프로세스는 비실행 프로세스가 된다)

프로세스 상태 변환: Context Switching

운영체제는 프로세스 스케쥴러를 이용해 프로세스 상태변환을 수행한다

Scheduler

스케쥴러가 각 큐마다 다음 차례의 프로세스를 결정함

Queue

  • 프로세스의 스케쥴링을 위해 운영체제는 큐를 지원한다
  • 링크드 리스트로 관리됨

Queue의 종류

  • Job queue: 메인 메모리에 올라가기 위한 프로그램들이 대기하는 곳
  • Ready queue: 메인메모리에 적재되어, CPU로부터 자원을 받기 위해 기다리는 곳
  • Device queue: 장치를 사용하기 위해 기다리는 곳
  • Waiting queue: 특정한 이벤트마다 대기하는 프로세스를 유지시켜주는 곳

프로세스 상태 변환

Dispatcher: 준비큐에서 선택된 프로세스에 CPU의 제어를 넘기는 모듈
Dispatch: CPU가 이전의 프로세스 상태를 PCB에 저장하고, 또 다른 프로세스의 정보를 PCB에 읽어 레지스터에 적재(CPU의 할당)하는 과정. CPU 자원의 할과 잃음을 의미

  • Context switching overhead: PCB 정보를 저장하고 로드하는데 드는 비용

프로세스 상태 변환 종류

  • 실행 프로세스가 자발적으로 CPU를 반환하기 전에 그 프로세스의 시간 할당량이 만료되면 프로세스는 준비상태가 됨
  • 실행 프로세스가 CPU에서 수행하다가 입출력 명령 등을 내게 되면 대기(보류)상태가 됨
  • 보류된 프로세스는 보류된 이유가 제거되면 준비상태가 됨
  • 준비상태인 프로세스는 dispatcher에 의해 CPU가 부여되면 다시 실행상태가 됨
준비 -> 실행

준비 큐의 맨 앞에 있는 프로세스에 CPU가 배당되어 실행

dispatch(프로세스명): 준비 -> 실행

시간 할당량: 다중 프로그래밍 운영체제 환경에서 실행 프로세스에게 일정시간만 CPU를 사용할 수 있도록 제한하는 시간

실행 -> 준비

프로세스가 실행 상태에 있다: 프로세스가 CPU를 점유하고 있다

운영체제는 인터럽트 클럭으로 지정된 시간 동안만 프로세스가 CPU를 점유하도록 제한한다. (독점 방지)

인터럽트를 발생시켜 운영체제가 CPU 제어권을 가져감 >> 실행상태였던 프로세스는 준비상태로 바뀌고, 준비큐의 첫 프로세스가 실행상태가 됨

timerunout(프로세스명): 실행 -> 준비

실행 -> 보류(대기)

실행상태의 프로세스가 지정된 시간 이전에 입출력 연산 등을 필요로 할 경우 또는 새로운 자원 요청 등 문제가 발생하면, 그 프로세스는 스스로 CPU를 양도함

스스로 보류 상태로 전환한다

block(프로세스명): 실행 -> 보류(대기)

보류(대기) -> 준비

입출력 작업이 끝났을 때에 발생하는 깨움(wake up) 상태.

보류상태로부터 준비상태로의 변환.

wakeup(프로세스명): 보류(대기) -> 준비

프로세스 제어 블록 (PCB)


[이미지 출처]

Process Control Block

  • 각 프로세스는 프로세스 제어 블록에 의해 운영체제 내에 표현됨
  • 프로세스에 관현 모든 중요한 정보를 저장하는 데이터블록
  • 프로세스를 생성할 때 만들어짐
    • 실행 종료된 프로세스의 프로세스 제어 블록도 삭제됨
  • 모니터 메모리 영역에 저장됨
  • 운영체제 내에서 한 프로세스의 존재를 정의함
  • 프로세스 교체작업에서 다시 수행할 대기 중인 프로세스에 관한 저장값을 PCB에 저장함
  • PCB들인 링크드 리스트 방식으로 운영돼 PCB 주소값의 연결로 이루어짐
  • 저장 내용: PID, 프로세스의 현 상태, 포인터, 우선순위, 스케줄링 정보, 기억장치, 시스템 자원, 계정 정보, 레지스터의 값
  • PID (Process ID): 프로세스가 생성될 때 부여받는 프로세스의 고유 식별자
  • 프로세스 상태: 신규/생성/준비/실행/대기/중단 등 상태 표시
    • Process state
  • 포인터
    • 부모 프로세스에 대한 포인터
    • 자식 프로세스에 대한 포인터
    • 프로세스가 위치한 메모리 주소에 관한 포인터
    • 할당된 자원에 대한 포인터
  • 프로그램 카운터(PC): 프로세스를 수행하기 위한 다음 명령의 주소를 표시
    • Program counter
  • 레지스터: 누산기, 인덱스 레지스터, 범용 레지스터, 조건 코드 등에 관한 정보
    • Register
    • 컴퓨터 구조에 따라 수/형태의 변화
    • 인터럽트가 발생하면 PC와 함께 저장되어 나중에 다시 수행될 때 원상복구를 가능케 함
  • 계정 정보: CPU 사용시간, 실제 사용시간, 사용 상한시간, 계정 번호, 작업/프로세스 번호 등
  • 입출력 상태 정보: 특별한 입출력 요구 프로세스에 할당된 입출력 장치, opened 파일 목록 등
  • 메모리 관리 정보: 메모리 관리에 필요한 정보
    • 메모리 영역을 정의하는 하한/상한 레지스터
      • = 경계 레지스터
    • 페이지 테이블의 정보

위의 정보는 인터럽트 처리, 자원 할당, 스케줄링 등을 수행하는 운영체제의 모든 모듈에 의해 판독/수정될 수 있다

2. 프로세스 관리

운영체제에는 프로세스 생성, 종료, suspend, 재시작, 우선순위의 변화, 보류, 디스패치 등을 수행할 수 있는 매커니즘이 있다

2.1 프로세스 생성

운영체제는 프로그램을 선택하여 수행하하기 시작하면, 프로세스를 관리하기 위한 프로세스 제어 블록을 만들고, 프로세스에게 주소 공간을 할당해야 한다

프로그램을 선택하여 수행하기 시작한다는 것은, 새로운 프로세스를 생성하고 추가한다는 것이다

프로세스의 생성은 일괄처리 환경에서 작업이 제출될 때 또는 대화형 환경에서는 새로운 사용자가 로그온할 때 이루어짐

새로운 프로세스의 생성은 운영체제의 도움이나 사용자의 애플리케이션 요청에 의해서 생성됨

  • 즉, 하나의 프로세스가 다른 프로세스를 생성할 수 있음
  • 자식 프로세스: 생성된 프로세스
  • 부모 프로세스: 생성한 프로세스
  • 부모 프로세스는 자식 프로세스를 트리구조로 생성함
  • 자식 프로세스도 부모 프로세스가 되어 자식 프로세스를 생성할 수 있음

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

각 프로세스를 식별하고 관리하기 위해 고유의 PID(Process Identifier)를 가짐

  • 부모 프로세스와 자식 프로세스 간 자원 공유 옵션
    • 모든 자원 공유
    • 자식은 부모의 일부 자원만 공유 가능
    • 부모와 자식 간 공유 안함
  • 자식 프로세스 생성 후, 부모 프로세스는 뭐해?
    • 자식 프로세스가 끝날 때까지 기다리는 경우 >> waiting queue로
    • 자식 프로세스와 함께 동작 >> 멀티 프로세싱 환경
  • 자식 프로세스의 주소 공간
    • 부모 프로세스와 동일한 새 프로세스인 경우: 부모 프로세스의 프로그램/데이터가 완전히 복사됨
    • 새로운 프로그램을 실행하는 프로세스인 경우: 새로운 프로그램을 메모리에 적재하고 실행

fork(): Linux/UNIX환경에서 부모 프로세스와 같은 새로운 프로세스를 만드는 시스템 콜 함수

  • 생성된 자식 프로세스는 부모 프로세스의 데이터와 프로그램이 완전 복사되어 같은 프로그램을 수행하는 프로세스임
    • 부모-자식 프로세스 간 편리한 의사소통
  • 부모와 자식 프로세스 간 구분
    • 함수는 부모 프로세스에서 자식의 PID를 반환함
    • 자식 프로세스에서는 0을 반환함
  • 새로운 프로세스에 따로 메로리를 할당
    • 결과적으로 PID가 다른 프로세스가 새로 생긴다는 점에서 exec() 함수와 큰 차이
      [이미지 출처]

exec(): Linux/UNIX 환경에서 새로운 프로그램을 실행하는 프로세스로 기존 프로세스를 대체하는 함수

  • 같은 PID가 새로운 프로세스에 적용됨
    • exec() 함수를 호출한 프로세스는 새로운 프로세스에 의해 덮여 쓰임
  • 프로그램 코드, 메모리, 파일 등 프로세스 자원이 새로 바뀜

wait(): 자식 프로세스가 종료될 때까지 현재 프로세스의 동작을 멈추는 시스템 콜 함수

  • 자식 프로세스가 종료되면 자식 프로세스 종료 시그널이 발생하여 waiting queue에 있는 부모 프로세스가 ready queue로 넘어가서 다시 실행이 가능해짐

프로세스 생성에 필요한 작업

  • 프로세스 이름 결정
  • 시스템에 알려진 프로세스들의 리스트에 이 프로세스를 삽입
  • 프로세스에 초기 우선순위 부여
  • 프로세스 제어 블록 생성
  • 프로세스에 초기 자원 할당
    • 프로세스의 상태 정보, 프로그램 카운터, 스택 포인터 등의 초기화
    • 자원 요청
    • 프로세스 제어 정보

프로세스 생성 과정

  1. 새로운 프로세스에게 프로세스 식별자 할당
  2. 프로세스의 모든 구성 요소를 포함할 수 있는 주소공간과 프로세스 제어 블록 공간 할당
  3. 프로세스 제어 블록 초기화
  4. 해당 리스트에 삽입 (링크)

2.2 프로세스 소멸

프로세스의 마지막 명령이 실행되면 그 프로세스가 종료됨 >> 운영체제에게 프로세스의 삭제를 요청한다

  • 일괄처리 환경의 운영체제:
    • 작업종료를 의미하는 신호로 인터럽트를 발생시켜 통보
    • 또는 중지 명령을 시스템 호출하여 프로세스를 완료
  • 대화형 환경:
    • 사용자가 로그오프하거나
    • 터미널을 끌 때

프로세스 소멸 종류

  • 정상적인 완료: 프로세스가 운영체제의 서비스 호출
  • 시간 초과: 프로세스가 명시된 전체시간을 초과하면서 실행되거나 이벤트 발생을 기다림
  • 실패: 파일 검색 실패, 명시된 횟수를 초과하여 입출력에 실패
  • 산술 오류, 보호 오류, 데이터 오류 등
  • 메모리 부족, 접근 위반 등

프로세스 소멸 함수

exit()

  • 현재 프로세스가 종료되어 SIGCHILD 시그널이 부모 프로세스로 전달됨
  • wait()된 부모 프로세스는 자식 프로세스의 exit()함수에 의해 정상 종료됨을 알 수 있음
  • 함수 호출 후, 자식 프로세스에 할당된 모든 자원은 운영체제에 의해 할당해제됨
  • exit() 함수가 없더라도 프로그램 코드의 끝에 도달하거나 return 키워드를 만나면 자동으로 exit()함수가 호출됨

abort()

  • 해당 함수를 호출한 프로세스에 SIGABRT 시그널을 보냄
    • SIGABRT 시그널: 프로세스를 비정상적으로 종료 시키고 코어 덤프 파일을 생성함
  • 부모 프로세스가 자식 프로세스를 강제 종료시키는 경우
    • 자식이 할당된 자원을 초과하여 사용
    • 자식에게 할당된 작업이 더이상 필요 없음
    • 부모가 종료되었고, 운영체제가 부모 종료시 자식이 실행되는 것을 허용하지 않음

좀비 프로세스

  • 자식 프로세스가 부모 프로세스보다 먼저 죽은 경우, 부모 프로세스가 종료 상태를 회수하기 위해 커널이 자식 프로세스의 최소한의 정보(PID, 종료상태 등)을 남겨둠
  • 부모 프로세스는 wait함수를 호출해 이 상태를 회수하면 남은 모든 정보가 제거됨
    • 자식 프로세스는 완전 소멸
  • 하지만 부모 프로세스가 wait함수를 호출하지 않아 최소한의 정보가 메모리에 남아있는 경우를 좀비 프로세스라 부름

고아 프로세스

  • 부모 프로세스가 자식 프로세스보다 먼저 종료되어 부모 프로세스가 없는 자식 프로세스
  • 운영체제는 고아 프로세스를 허용하지 않음
    • 부모 프로세스가 먼저 종료되면 자식 프로세스의 새로운 부모 프로세스로 init(PID = 1)이 설정됨
  • init 프로세스는 자식 프로세스가 종료될 때까지 기다린 후 wait함수를 호출하여 고아 프로세스의 종료 상태를 회수함
    • 이렇게 좀비 프로세스가 되는 것을 방지
  • 고아 프로세스를 회수해야 하는 이유
    • 프로세스가 시스템의 자원을 낭비할 수 있음
    • 시스템이 프로세스가 종료될 때까지 추적해야 함
    • 성능 저하의 원인

2.3 프로세스 제거

시스템으로부터 프로세스를 제거한다

  • 프로세스에 속해있던 자원은 다시 시스템으로 돌아감
  • 프로세스는 시스템 리스트에서 사라짐
  • 프로세스 제어 블록이 회수됨

2.4 Suspend와 재시작

프로세스의 준비/실행/보류 상태만 사용하면 시스템의 활동시간이 대부분 유휴 상태가 됨 (입출력 동작 시간이 일반적인 연산시간보다 너무 느리기 때문)

그래서 일시정지 상태가 필요

일시정지 상태

  • 프로세스를 일시정지시켰다가 다시 실행시킴
  • 시스템 전체의 부하를 증가시키지 않고 프로세스에게 서비스를 제공할 수 있음
  • 특정 이벤트의 발생을 기다리기 위해 대기상태가 된 거라서 해당 이벤트를 발생시키면서 즉시 실행상태로 변환할 수 있는 이점
  • 다중 프로그래밍 환경에서 프로세스가 입출력 요구 외의 다른 원인으로 수행이 중단되어 있는 상태도 의미함
    • (예) 자원부족 상태
      • 동적자원 할당 상황에서 충분히 발생 가능

재시작: 중단원인이 제거되어 다시 수행됨


[이미지 출처]

2.5 프로세스 우선순위 변경

  • PCB내의 우선순위 값은 경우에 따라 변경될 수 있음
  • 프로세스 스케줄러는 준비큐의 우선순위를 이용해 작업함

우선순위 결정 요소

낮은 순위높은 순위
CPU 중심 프로세스입출력 중심의 프로세스
속도가 빠른 디스크 입출력 프로세스속도가 느리면서 빠른 응답을 요구하는 단말기 입출력 프로세스

우선순위간 차이

  • 낮은 순위의 프로세스에는 시간할당량이 많이 제공됨
  • 우선순위가 높은 프로세스는 할당량을 적게 제공함

입출력 중심의 프로세스는 CPU를 자주 할당받으면서 짧게 사용함
CPU 중심 프로세스는 CPU의 할당횟수는 적으나 한번 할당받으면 길게 사용함

2.6 문맥교환(Context switching)

실행 중인 프로세스가 인터럽트되면 운영체제는 다른 프로세스를 실행상태로 바꾸고 프로세스에게 제어를 넘김

  • 입출력 인터럽트: 입출력 동작 관련
  • 클럭 인터럽트: 프로세스의 할당시간 관련

프로세스 교환: 실행 중인 프로세스로부터 제어를 인수해 다른 프로세스에게 제어를 넘겨주는 과정

프로세스를 전환하기 위해서는 이전의 프로세스 상태 레지스터 내용을 보관하고, 새 프로세스의 레지스터를 적재하는 과정이 필요함 (문맥교환)


[이미지 출처]

  • 현재 P1이 실행 중. 문맥교환 요청 발생
  • 제어는 사용자 모드에서 운영체제 모드로 넘어감
  • 문맥교환을 위해 실행 중인 P1의 실행 문맥을 메모리에 있는 해당 프로세스의 PCB에 저장
  • 문맥 교환 시작
    • 새로운 프로세스의 dispatch
  • 커널은 다음 실행할 준비 상태의 P2의 실행 문맥을 메모리에 있는 P2의 PCB에 저장 >> P2 실행

문맥교환 오버헤드

  • 기억장치의 속도, 레지스터 수, 특수 명령어의 존재에 따라 상이
  • 스레드를 이용해 문맥교환을 효율적으로 처리할 수 있음

3. 스레드

스레드/경량 프로세스 (Light Weight Process, LWP): 프로세스가 실행될 때의 제어만을 분리한 개념
중량 프로세스 (Heavy Weight Process, HWP): 전형적인 프로세스

3.1 스레드 개요

스레드:

  • 명령어가 CPU를 이용하여 실행되어지는 객체의 기본 단위
  • CPU수행의 기본 단위
  • 프로그램 명령을 실행하는 프로세스 내의 개체
  • 명령어를 독립적으로 실행할 수 있는 하나의 제어 흐름
    Thread ID, Program counter, Register set, Stack space
    스레드 내용:
  • 스레드ID
  • Program counter (PC), Stack pointer
  • 레지스터
  • 스레드 실행 시의 상태
  • 스레드별 정적 저장소 (stack)
  • 프로세스 내 다른 스레드가 공유하는 프로세스의 메모리 접근

스레드의 특징:

  • 한 프로세스 내에서 동시 작업이 가능함
  • 같은 프로세스 내에 있는 스레드는 같은 주소공간에 존재, 자원 및 상태 공유, 동일 데이터에 접근 가능
  • 동일 프로세스 내에서
    • 공유: 프로세스의 프로그램 코드, 각종 주소 공간, 시스템 자원
    • 분리: 프로그램 카운터, 각종 레지스터, 스택 공간


[이미지 출처]

스레드의 장점

  • 어플리케이션 성능 향상
    • 코드/데이터 공유로 어플리케이션이 같은 주소 공간 내에서 서로 다른 스레드의 활동을 허용함
    • 성능 향상
    • 다중 스레딩: 일부가 차단되거나 긴 작업을 수행해도 실행하는 프로그램을 계속 허용할 수 있음
  • 문맥교환 비용 감소
    • 프로세스 생성을 위한 메모리와 자원할당은 비용이 큼
    • 스레드는 프로세스의 자원을 공유하기 때문에 문맥교환과 스레드 생성이 더 효율적임
    • 하지만 같은 자료를 공유해서 동기화문제 발생 가능
  • 다중 CPU 구조 이용
    • 다중 스레딩은 스레드가 여러 CPU에서 병렬로 실행할 수 있는 환경제공

3.2 단일 스레드와 다중 스레드

Single thread process와 Multi-threaded process

프로세스는 하나의 스레드 또는 여러 개의 스레드를 포함한다

단일 스레드

하나의 프로세스에서 하나의 스레드가 실행되는 전통적인 방식

다중 스레드

하나의 프로세스에서 여러 스레드의 실행을 지원함

3.3 스레드 상태 변화

스레드의 상태: 준비, 실행, 대기, 종료

스레드간 문맥교환은 전체 프로세스를 대기상태로 전환시킬 필요 없다는 유연성을 가진다

스레드로 병렬처리가 가능해진다

4. 병행 프로세스

병행 프로세스: 프로세스 여러개가 동시에 수행 상태에 있음

  • 서로 관련이 없는 프로세스면 독립적으로 수행
  • 프로세스 간 협력이 필요할 수 있음
    • 이 경우에는 비동기적 수행

병행 프로세스는 제한된 자원을 공유하기 위해 빈번하게 사호작용하게 되는데, 상호작용하는 프로세스들이 순서에 맞게 실행되도록 프로세스 간 동기화가 필요하다

4.1 병행 프로세스 과제

병행성: 여러 프로세스가 같은 CPU를 공유 or 메모리/입출력 장치 공유

공유 데이터에 대한 동시 접근은 데이터의 불일치를 가져올 수 있음

  • 공유 자원의 상호배제
    • 공유 자원을 상호 배타적으로 사용한다
    • 한 순간에 하나의 프로세스만 사용해야 한다
    • (예) 프린터, 통신망 등
  • 하나의 기능을 함께 수행하는 프로세스간 동기화
    • 병행 프로세스 간 공유 데이터에 대한 동시 접근은 자료 불일치를 발생시킬 수 있음
    • 상호배제 또한 동기화의 예시
  • 결정성 확보 (determinacy)
    • 프로세스는 동시에 수행되는 다른 프로세스의 실행속도 또는 진행순서에 관계없이 주어진 초기값에 관해 항상 같은 결과값을 내도록 결정성이 확보되어야 함
  • 프로세스의 교착상태 해결
    • 여러 프로세스가 각자 사용하는 자원을 점유하고 있으면서 다른 프로세스가 점유하고 있는 자원을 요청하여 대기하는 상태 >> 교착상태
    • 이를 해결해야 함
    • 병행 프로세스의 병렬 처리 능력을 극대화해야 함
  • 프로세스 간 통신 (Inter Process Communication, ICP)
    • 데이터 교환을 위한 통신

5. 상호배제와 동기화

상호배제 (Mutual Exclusion)

  • 특정 비공유 자원을 한 순간에 한 개의 프로세스만 사용할 수 있도록
  • 하나의 프로세스가 공유 데이터를 사용하는 동안 다른 모든 프로세스는 그 데이터를 접근할 수 없도록
  • 차례대로 한번에 하나의 프로세스만 읽거나 쓰도록

5.1 임계영역

경쟁조건 (Race condition): 여러 프로세스들이 경쟁적으로 한 변수를 조작하는 경우, 실행 결과값은 접근이 일어나는 특정 순서에 따라 달라진다

경쟁 조건을 예방하기 위해 한 번에 오직 하나의 프로세스만이 한 변수를 조작하도록 보장해야 한다 >> 동기화의 필요성

임계자원: 둘 이상의 프로세스가 공유할 수 없는 자원
임계영역 (Critical section): 프로그램에서 이 자원을 사용하는 부분

  • 어떤 프로세스가 임계영역을 수행하면 다른 프로세스는 모든 임계영역으로의 진입이 차단된다
  • 임계영역을 떠나는 프로세스는 출구상호배제를 수행 >> 다른 프로세스가 임계영역에 들어갈 수 있도록

임계영역 과정

  • 각 프로세스는 자신의 임계 영역에 진입하기 위해 진입허가를 요청해야 함
    • 진입 영역 (entry section): 이 요청을 구현함
  • 진입 영역에서 기다리다가 진입 허가가 나면 프로세스는 임계 영역에 들어감
  • 출구 영역 (exit section): 임계 영역을 빠져나왔음을 알리는 코드
  • 나머지 영역 (remainder section): 그 밖의 코드

프로세스 간 임계영역 진입 충돌을 막기 위해 위 과정은 단일 사이클 내에 일어나야 한다

임계영역 문제의 해결 요소

임계영역 문제의 해결을 위해서는 다음 사항이 만족되어야 함

  • 상호배제: 한 프로세스가 임계영역에서 실행 중일 때 다른 어떤 프로세스도 임계영역을 수행할 수 없다
  • 진행: 임계영역을 수행하는 프로세스가 없고, 여러 프로세스가 임계영역에 진입하고자 할 때에는 처리하지 않은 프로세스 중에서 임계영역에서 수행시킬 대상을 결정해야 함
    • 이 결정은 무한정 미루어질 수 없다
  • 제한된 대기
    • 한 프로세스가 임계영역에 대한 요청 후부터 수락되기까지의 기간 내에 다른 프로세스가 임계영역을 수행할 수 있는 횟수에 제한이 있어야 함

5.2 세마포어 (Semaphore)

세마포어:

  • 프로세스 동기화를 위한 구조
  • 멀티프로그래밍 환경에서 공유 자원에 대한 접근을 제한하는 방법

세마포어의 종류

  • 범용/카운팅 세마포어 (General/Counting semaphore)
    • 세마포어의 초기값: 0이상의 수
  • 이진 세마포어 (Binary semaphore)
    • 세마포어의 초기 값: 0 또는 1

세마포어 작동 과정

운영체제에서 프로세스가 그 자원을 사용할 수 있는지 여부(자원의 유무)와 자원이 자유 상태인지 여부를 나타냄

세마포어 S

  • 두 개의 표준 단위 연산에 의해서만 접근되는 변수
  • 정수 값을 가짐

세마포어의 표준 단위 연산: P, V

  • P: 프로세스를 대기시키는 Wait 동작
    • 프로세스가 임계영역에 진입하려고 할 때 프로세스를 대기시키는 Wait 동작에 의해 접근됨
  • V: 대기 중인 프로세스를 깨우는 신호를 보내는 Signal 동작
    • 임계영역에서 나오는 프로세스가 호출함
  • 하나의 프로세스가 세마포어의 값을 수정할 때 동시에 같은 세마포어의 값을 수정할 수 없다

프로세스를 대기시킨다

P(S): while S <= O do no-op // S가 0면 1이 될 때까지 기다려야 함
	S := S - 1; // S가 0이므로 다른 프로세스는 진입하지 못함

대기 프로세스를 깨운다

V(S) : S := S + 1; // S를 1로 만들어 다른 프로세스가 들어올 수 있도록 해제한다

세마포어에 의한 상호 배제의 구현

repeat
	P(S);
  임계영역
  	V(S);
  나머지 영역
until false;

구현

세마포어는 한 프로세스가 임계영역에 있으면 이 임계영역에 들어가려는 프로세스는 진입코드에서 계속 반복 순환해야 하는 바쁜 대기가 요구되는 단점이 존재한다

바쁜 대기 (busy waiting): 임계 영역에 들어갈 수 있을 때까지 아무 작업도 하지 않고 임계 영역에 접근이 가능한지 무한으로 검사만 하고 있는 현상

바쁜 대기를 방지하기 위해 세마포어의 wait, signal 동작을 수정한다

  • wait() 연산 실행 후 세마포어 값이 양수가 아니면 바쁜 대기를 하는것이 아닌 프로세스를 일시 정지하도록 한다
    • 일시 중지 연산은 프로세스를 세마포어에 연관된 대기 큐에 넣고 프로세스 상태를 대기 상태로 전환
  • 세마포어 S 변수 값을 대기하면서 일시 중지된 프로세스는 다른 프로세스가 signal() 연산을 실행하면 재시작 된다
  • 프로세스는 wakeup() 연산에 의해 재시작되는데 프로세스를 대기 상태에서 준비 상태로 변경함
    • 프로세스는 준비 큐에 들어감

세마포어 장단점

  • 장점: 상호배체 및 프로세스 간 조정을 위한 유연성 제공
  • 단점: P(wait)과 V(signal) 연산이 프로그램 전체에 존재하므로 프로그램 작성이 어려움

5.3 모니터

모니터는 세마포어와 비슷한 용도로 사용되지만 제어가 쉽다

  • 프로세스 내 서로 다른 스레드 간 동기화에 사용
  • 프레임워크/라이브러리 자체에서 제공되는 소프트웨어 모듈
  • 가볍고 빠르다

모니터 구조

모니터의 syntax


[이미지 출처]

프로그래머가 정의한 procedure와 data 형태 선언들의 집합으로 구성됨

  • 모니터 procedure: 모니터 내에서 정의된 지역변수와 매개변수만 접근할 수 있음
  • 모니터의 지역변수: 지역 procedure에 의해서만 접근됨
  • 개인 데이터와 procedure: 순차적으로 재사용 가능한 특정 공유 자원을 할당하는데 필요함

모니터의 구조


[이미지 출처]

  • 하나 또는 그 이상의 모니터 procedure, 초기화 코드, 개인(지역) 데이터, 모니터 입력 대기(진입)큐로 구성됨
  • 한 번에 하나의 프로세스만 모니터 안에서 활동하도록 보장함
    • 여러 사용자에 의해 동시에 접근하려는 모니터 내부의 데이터를 보호함
    • 모니터가 실행되는 동안 진입하려는 프로세스는 모니터 입력 대기큐에서 차단됨

모니터 작동 과정

  • 자원을 가진 프로세스는 자원의 반납을 위해 모니터 진입 루틴을 호출함
    • 모니터 진입 루틴은 대기 중인 프로세스 중 하나가 모니터에 들어가 자원을 얻을 수 있도록 신호를 보냄
  • 무기한 연기를 방지하기 위해 이미 대기 중인 프로세스가 새로 도착한 프로세스보다 더 높은 우선순위를 받도록 함

조건 구조

모니터에 한 개 이상의 조건 형태의 변수를 정의할 수 있다

var x, y : condition;

조건 변수에서 호출될 수 있는 연산:

  • x.wait(): 작업을 유보하도록 한다
    • 이 동작을 호출한 프로세스는 다른 프로세스가 x.signal()을 호출할 때까지 기다림
  • x.signal(): 하나 이상의 x.wait()에 의해 유보된 작업 재개
    • 대기 중인 프로세스 중 하나만 재개
    • 중단된 프로세스가 없으면 signal 연산은 효과 없음

+) 뮤텍스 (Mutex)

뮤텍스

  • 운영체제의 동기화 기법
  • 상호배제로 임계역에 하나의 스레드만 들어갈 수 있음
    • 이진 세마포어와 유사함
  • 서로 다른 프로세스간 동기화에 사용
  • 운영체제 커널, 프레임워크, 라이브러리에 의해 제공됨
  • 무겁고 느림

뮤텍스는 LockingUnlocking을 이용해 프로세스들의 공유자원에 대한 접근을 조율함

  • 한 스레드가 임계영역에 들어가기 위해 lock한다
  • 스레드가 임계영역에서 나갈 때 unlock한다

이진 세마포어 vs. 뮤텍스

  • 뮤텍스는 lock을 설정한 프로세스만이 lock을 해제할 수 있음
  • 이진 세마포어는 lock을 설정한 프로세스와 해제하는 프로세스가 서로 다를 수 있음
  • 자원의 소유권을 가질 수 있는지의 차이
    • 세마포어는 소유할 수 없음
    • 뮤텍스는 소유할 수 있음
      • 소유주가 그에 대한 책임을 진다
  • 범위
    • 세마포어: 시스템 범위에 걸침
      • 파일 시스템 상의 파일로 존재
    • 뮤텍스: 프로세스 범위
      • 프로그램 종료시 자동으로 지워짐

+) IPC (Inter Process Communication)

IPC: 프로세스 간 통신

  • 프로세스는 완전히 독립된 실행객체
    • 다른 프로세스의 영향을 받지 않음
    • 하지만 통신이 동일 프로세스 내 스레드만큼 수월하지는 않음

그래서 커널 영역에서 IPC를 제공한다

IPC가 왜 필요한가?

  • 정보 공유: 여러 사용자가 동일한 정보를 필요로 할 때
  • 계산 가속화: 특정 작업을 빠르게 실행하기 위해 해당 작업을 나눠서 병렬로 실행
  • 모듈성: 특정 시스템 기능을 별도의 프로세스(스레드)로 구분해 모듈식 형태로 시스템 구성
  • 편의성

IPC의 종류

1. 메시지 전달 (Message passing)

  • 커널이 제공하는 API를 이용해 커널 공간을 통해 통신하는 방법
  • 메시지 큐를 사용함
    • 송신 프로세스는 큐에 enqueue
    • 수신 프로세스는 큐에 dequeue
    • 메시지 큐는 커널단에서 관리됨
      • 커널이 데이터 송수신을 컨트롤할 수 있음
  • 장점: 별도로 구축할 것 없이 커널을 사용함
  • 단점: 커널을 이용하기 때문에 시스템 콜이 필요함
    • 시스템 콜 오버헤드 발생
    • 매번 커널을 통해 정보/메시지 전달은 프로세스 복사 두번을 필요로 함 >> 2회 모두 시스템 콜 사용
  • 종류: 파이프, 시그널, 메시지큐, 소켓

메시지 전달 연산

커널이 send(message)receive(message) 연산을 제공한다

  • send연산을 사용하기 때문에 줄 수 있는 메모리는 channel의 크기로 제한됨
  • 메시지의 크기는 고정될 수도 가변적일 수도 있음

메시지 전달 종류

파이프 (Pipe): 하나의 프로세스가 파이프를 통해 다른 프로세스로 메시지를 직접 전달함

[이미지 출처]

  • 파이프는 두 개의 프로세스를 연결함
    • 하나는 데이터를 쓰기만 할 수 있음
    • 다른 하나는 데이터를 읽기만 할 수 있음
    • 일방향 통신만 가능함 >> half-duplex 통신
  • half-duplex 방식이라서 데이터는 한쪽 방향으로만 이동함
    • 양방향 통신을 위해서는 두 개의 파이프가 필요함
  • 용량 제한 존재함

시그널 (Signal): 프로세스 ID를 통해 특정 프로세스에게 메시지를 전달함

  • 시그널 ID에 따라 어떤 이벤트인지 알 수 있음

메시지 큐 (Message queue): 고정 크기의 메시지를 연결 리스트를 통해 통신함


[이미지 출처]

  • 메시지 단위의 통신
  • 메시지 큐 ID를 통해 통신함
  • 여러 프로세스가 동시에 데이터를 다룰 수 있음
  • 메시지 큐는 메모리 공간임

소켓 (Socket): 네트워크 상에서 프로세스 간 통신함

[이미지 출처]

  • local/remote 통신이 모두 가능함

2. 공유 메모리 (Shared memory)

Shared memory는 공유 메모리를 이용해서 데이터를 주고 받음

  • IPC를 위해 공유 메모리 영역을 구축해야 함
    • 이 영역에서 자원/데이터를 주고 받음
  • 장점:
    • 커널 의존성이 낮음 >> 속도 빠름
    • 데이터 복사의 오버헤드 발생 안함 >> 속도 빠름
    • 유저 수준에서 IPC가 가능해서 통신이 자유로움
  • 단점: 자원과 데이터를 공유하기 때문에 동기화 이슈 발생

공유 메모리 영역

  • 공유 메모리 영역은 프로세스의 힙 영역과 스택 영역 사이에 존재함
  • 공유 메모리를 생성하는 프로세스의 메모리 공간에 위치함
  • 다른 프로세스는 자신의 주소에 연결시켜 사용함
  • 생성된 공유 메모리는 커널에 의해 관리되기 때문에 컴퓨터 재시작 또는 직접적 공유 메모리 공간 해제 외에는 관련 프로세스가 모두 종료되더라도 유지됨
    • shmctl(): 공유 메모리를 제거하는 시스템 콜

프로세스 간 공유 메모리 사용 조건

  • 메모리 보호 제약 해제
  • 동일한 위치를 동시에 사용하지 않도록 보장해야 함
  • 공유 메모리를 버퍼로 활용하여 정보를 생성하고, 소비/동기화 필수

공유 메모리 생성

동일한 키를 알고 있는 프로세스끼리 공유 메모리를 생성할 수 있음

  1. 프로세스 A에서 공유 메모리 생성
  2. 프로세스 A에 공유 메모리 attach 및 사용
  3. 프로세스 B도 공유 메모리 attach 및 사용

참고

profile
우당탕탕

0개의 댓글