🎁[속보] 인프런 내 깜짝 선물 출현 중🎁

인프런 워밍업 클럽 스터디 3기 - OS <3주 발자국>

프로세스 동기화

프로세스 간 통신(IPC: Inter-Process Communication)

ipc.webp

프로세스는 독립적인 실행 단위이기 때문에 기본적으로 타 프로세스의 메모리 영역에 접근할 수 없다. 서로 다른 프로세스들이 데이터를 교환하고 협력할 수 있도록 도와주는 메커니즘이다.

통신의 분류는 크게 3가지로 분류될 수 있다.

  1. 같은 컴퓨터 내 프로세스 간 통신

  2. 같은 프로세스 내 스레드 간 통신

  3. 네트워크를 이용한 원격 프로세스 간 통신

같은 컴퓨터 내 프로세스 간 통신
  1. 파일

    파일은 프로세스 들이 하나의 파일을 이용해 데이터를 쓰고 읽어서 데이터를 전달하는 방식이다.

  2. 파이프

    파이프는 운영체제가 생성한 특별한 메모리 구조로 한 쪽 프로세스는 쓰고 다른 쪽 프로세스는 읽는 단방향 통신이다.

    익명 파이프(anonymous pipe) : 부모-자식 처럼 관련된 프로세스 간 통신

    명명 파이프(named pipe) : 이름이 있는 파일형태로 관련이 없는 프로세스 간 통신

  3. 메시지 큐

    운영체제가 관리하는 큐 형태의 자료구조로 메시지 단위로 데이터를 주고 받는 형식이다. 비동기 통신이 가능하고 메시지 단위여서 데이터 구조화에 용이하다.

  4. 공유 메모리

    여러 프로세스가 동시에 접근 가능한 메모리 영역을 운영체제가 할당하여 공유 메모리 영역을 통해 읽고 쓸 수 있다. 단 동기화 문제로 인해 세마포어, 뮤택스 등의 관리가 필요하다.

  5. 시그널

    특정 이벤트(kill 혹은 ctrl+c)를 발생을 알리는 비동기 통신 메커니즘. 데이터 전달 보다는 프로세스 제어에 사용된다.

스레드 간 통신

스레드는 프로세스 내의 존재하는 실행 단위로 같은 프로세스내의 스레드들은 기본적으로 코드, 데이터, 힙 영역을 공유한다.

이때 전역 변수, 힙 영역을 통해 데이터를 쉽게 주고 받는다.

공유 자원에 대한 동시 접근 시 동기화가 매우 중요하여 동기화 도구들을 활용한다.

  • 뮤텍스(Mutex) : 한 번에 하나의 스레드만 공유 자원 접근할 수 있게 한다.

  • 조건 변수(Condition Variable) : 특정 조건이 충족될 때까지 스레드를 대기 상태로 두었다가 조건이 충족되면 통지하는 방식

  • 세마포어(Semaphore) : 0이상의 정수 값을 가지고 있고 0이되면 다른 스레드의 접근을 막는 방식. 프로세스가 자원을 얻을 때 wait(P 연산)으로 세마 포어 값을 감소시키고 signal(V 연산)으로 자원을 반환할 때 세마포어 값을 증가시킨다.

    wait(S):
      S -= 1
      if (S < 0):
        현재 스레드를 대기 큐에 넣고 대기 상태로 전환
    signal(S):
      S += 1
      if (S <= 0)
        대기 큐에서 한 개의 스레드를 깨워 실행 준비 상태로 전환

네트워크를 통한 원격 프로세스 간 통신
  1. 소켓(Socket)

    네트워크 프로토콜(TCP/IP, UDP)를 활용하여 서로 다른 컴퓨터의 프로세스가 데이터를 주고 받을 수 있도록 하는 추상화된 인터페이스

    네트워크를 통해 통신 가능하며 표준화된 인터페이스라는 장점이 있지만, 데이터의 직렬화/역직렬화의 문제와 네트워크 프로그래밍의 복잡성이 존재한다.

  2. RPC(Remote Process Call)

    다른 컴퓨터에 있는 프로세스 함수를 로컬 함수 처럼 호출할 수 있도록 해주는 기술.

    내부적으로 소켓 통신 등을 사용하고 호출 과정, 파라미터 전달, 결과 반환을 추상화하여 원격 함수 호출을 구현할 수 있다.

    분산 시스템 개발에 용이하지만 성능 오버헤드 문제와 호출 실패 시 처리가 복잡하다.

공유자원과 임계구역

공유자원(Shared Resource)

여러 프로세스(또는 스레드)들이 함께 사용하는 자원(메모리, 파일, 변수)를 의미한다.

동시에 여러 프로세스가 공유하기 때문에 접근 순서에 따라 결과가 달라질 수 있다.

컨텍스트 스위칭등으로 인해 결과를 예측하기 어려운 상황이 발생하며 이를 동기화 문제 (Synchronization Problem)이라고 한다.

임계구역(Critical Section)

공유 자원에 접근할 때 반드시 보호해야 하는 코드 영역

여러 프로세스가 동시에 임계구역에 접근하면 의도치 않은 결과가 발생하게 된다.

접근 순서와 타이밍 문제로 발생하는 경쟁적 상태를 경쟁 조건(Race Condition)이라고 한다.

임계 구역 문제 해결을 위한 조건

  1. 상호 배제(Mutual Exclusion)

    한 번에 단 하나의 프로세스만 임계구역에 접근 가능

  2. 진행(Progress)

    임계구역을 사용하지 않는 프로세스가 다른 프로세스의 접근을 방해하지 않아야 한다.

  3. 유한 대기(Bounded Waiting)

    어떤 프로세스든 일정한 시간 내에 반드시 임계 구역에 진입할 수 있어야 한다.

    프로세스의 기아 상태(Starvation)를 피하고 프로세스의 공평한 자원 할당이 필요하다.

임계구역 문제 해결 방법

세마포어

운영체제 차원에서 제공하는 동기화 도구로 공유자원의 접근 권한을 관리하는 정수형 변수이다.

세마포어의 값은 일반적으로 자원의 개수를 의미하며 보통 이진(0,1) 세마포어를 사용하여 임계 구역을 보호한다.

공유 자원에 접근하려는 프로세스를 대기 큐에 넣고 운영체제가 대기 큐에 있는 프로세스에게 세마포어를 통해 접근 권한을 제어한다.

두 가지 기본 연산을 통해 관리한다.

  • wait() : 자원이 없으면 프로세스는 자원이 확보될 때까지 대기 상태로 들어간다.

  • signal( ) : 자원을 사용한 후 다시 자원을 반납하여 대기중인 프로세스에게 공유자원을 사용하게 한다.

int s = 1;
wait(s)
// 임계 구역 사용
signal(s) 

단 signal()과 wait()의 호출 순서가 잘못되면 세마포어 값이 혼동되어 데이터 손상이나 교착상태(deadlock)를 유발할 수 있다.

뮤텍스(Mutex)

세마포어의 특수한 형태로 오직 한 프로세스만 임계구역 접근을 허용하는 이진 세마포어(binary semaphore)이다.

C언어에서 POSIX 스레드 API를 사용하면 pthread_mutex_lock()pthread_mutex_unlock()으로 표현된다.

모니터

운영체제가 아니라 프로그래밍 언어 수준에서 제공하는 동기화 메커니즘이다.

모니터는 공유 데이터와 이를 조작하는 프로시저(메서드)를 하나의 객체로 묶은 형태로 제공한다.

프로그래머는 직접 wait(), signal()을 호출할 필요 없이 언어에서 자동으로 동기화 관리를 한다.

public class SharedCounter {
    private int count = 0;
​
    public synchronized void increment() {
        count++;
    }
​
    public synchronized int getCount() {
        return count;
    }
}

자바에서는 synchronized 키워드를 통해 동기화 코드를 작성하게 된다.

synchronized 메서드를 호출하는 동안에는 다른 synchronized 메서드가 접근 불가능 핟록 배타적 접근이 보장된다.

데드락(Deadlock)

deadlock.webp

데드락

여러 프로세스가 한 프로세스의 작업을 끝나기 까지 기다리다가 작업을 진행하지 못하는 상태를 교착 상태(데드락)이라고 한다.

교착상태는 공유 자원 때문에 발생한다. 공유 자원이 없다면 교착상태는 발생하지 않는다.

식사하는 철학자 포크가 3개인 식사에서 2개를 사용하여 식사를 하는 데 동시에 포크를 집어 더 이상 포크가 없는 상태이다.

필요조건

데드락이 발생하려면 다음과 같은 조건이 존재한다.

공유자원에는 상호 배제가 일어난다. 공유 자원은 다른 프로세스가 사용할 수 없는 리소스이다.

비선점이다. 한 프로세스가 선점하고 있을 때 다른 프로세스는 선점할 수 없다.

점유와 대기. 어떤 프로세스가 리소스 A를 원하는 상태에서 리소스 B를 원하는 상태여야 한다.

원형 대기. 점유와 대기를 하는 프로세스들의 관계가 원형을 이루고 있다는 것이다.

데드락의 예방은 매우 비효율적이고 해결하는 쪽으로 이 문제를 해결해야 겠다고 생각했다.

데드락 해결

교착상태 회피

전체 자원과 할당된 자원을 기준으로 안정상태와 불안정상태를 점검하고 유지한다.

은행원 알고리즘

은행은 대출을 원하는 사람들에게 대출을 해준다. 은행에 돈이 없고 대출자들에게 돈을 받지 못한다면 은행은 돈을 빌려줄 수 없다.

운영체제는 프로세스에게 자원을 할당하기 전에 전체 자원을 알고 있어야 한다. 이를 시스템의 총 자원이라고 한다. 프로세스는 각 자원이 필요한 최대 숫자를 운영체제 에게 알려준다.

안정상태는 프로세스마다 최대 요구자원과 현재 할당된 자원, 요청이 예상되는 자원을 계산한다. 최대 요구자원 - 현재 할당된 자원으로 요청이 예상되는 자원을 계산한다. 프로세스가 운영체제의 자원을 요구하면 프로세스의 요청이 예상되는 자원을 충족하는 곳에 자원을 할당하고 프로세스의 작업이 끝나 반납하는 경우 다른 프로세스에 자원을 할당한다.

불안정 상태는 현재 프로세스에 할당이 되어 있는 데 현재 시스템의 사용 가능 자원이 다른 프로세스의 최대 요구 자원을 충족시키지 못하는 상태를 말한다.

교착상태 검출
가벼운 교착상태 검출

프로세스가 일정 상태동안 작업을 수행하지 않는다면 교착상태가 일어난 것으로 간주하고 교착상태를 해결한다.

일정 시점마다 체크포인트로 작업을 저장하고 교착상태를 해결하기 위해 일정 시점으로 프로세스를 되돌린다.

무거운 교착상태 검출

자원할당 그래프를 통해 운영체제가 현재 어떤 프로세스가 자원을 사용하는 지 확인하고 교착상태에서 해결한다.

프로세스는 각 자원을 차지하고 있고 다른 자원을 요청하고 있다. 순환 구조가 생긴다면 교착상태를 일으킨 프로세스를 강제 종료한다. 그리고 다시 실행시킬때 체크포인트로 롤백 시킨다.

OS가 지속적으로 자원할당 그래프를 통해 그래프를 지켜봐야 하고 유지해야 하기 때문에 오버헤드가 발생한다.

가벼운 교착상태 검출에서 발생하는 억울하게 종료되는 프로세스는 발견되지 않는다.

쉬어가기

컴파일과 프로세스

코드가 어떻게 프로세스로되고 메모리로 할당되는가?

컴파일 언어는 컴파일을 거쳐 기계어로 실행파일을 만든다. 기계어로 실행파일을 만들기 때문에 속도가 빠르다.

인터프리터는 실행시 코드를 한 줄식 해석하여 만들어 낸다.

프로세스의 메모리 구조는 코드영역, 데이터영역, 스택과 힙 영역으로 이루어진다.

컴파일된 프로그램은 어떻게 프로세스를 차지하게 되는가?

코드는 -> 전처리기 -> 컴파일러 -> 어셈블러 -> 링커 -> 실행파일로 각 단계를 거쳐 바뀌게 된다.

프로세스는 이런 파일을 실행할 때 pcb를 만들고 cpu 스케쥴러에 의해 실행되고 멈춘다.

메모리

메모리 종류

여러 종류의 메모리가 존재한다. 왜 컴퓨터에 여러 메모리 종류가 필요할까?

레지스터 : 가장 빠른 기억장소로 cpu 내에 존재한다. cpu의 32비트와 64비트는 레지스터의 크기를 의미한다. cpu는 계산할 때 메모리에서 값을 불러와 레지스터에 저장한 뒤 값뒤 메모리에 저장한다.

캐시 : 메모리에 있는 값을 레지스터로 가져오기 위해서는 캐시에 미리 저장해 둔다.

메모리 :

보조저장장치 : 전원이 공급되지 않아도 저장이 되는 보조저장창치이다.

메모리와 주소

폰 노이만 구조는 모든 프로그램을 메모리에 올려 실행한다. 멀티 프로그래밍 환경에서는 여러 프로그램이 메모리에 올라간다.

메모리는 1바이트마다 주소를 남긴다.

cpu bit에 따라 64비트 레지스터는 한번에 여러 처리를 할 수 있어 프로그램 처리 능력이 향상된다.

물리 주소를 몰라도 논리주소를 통해 프로그램에 접근할 수 있다. 운영체제는 특별하기 때문에 운영체제를 따로 만든다. 하드웨어 적으로 경계 레지스터를 만들어 프로세스가 경계 레지스터를 침범하지 못하게 만든다.

절대주소(물리주소공간)와 상대주소(논리주소공간)라는 개념이 존재한다. 메모리 관리자(MMU)는 사용자가 접근할 때마다 접근 주소를 재배치 레지스터에 따라 데이터에 접근하여 값을 가져온다.

메모리 할당방식

메모리에 비해 프로그램의 크기가 클 경우 실행 시켜야할 부분만 메모리에 올리고 나머지는 디스크에 저장한다. 이를 오버레이라고 한다.

메모리에 여러 프로세스가 올라오는 환경에서는 두 가지 방식으로 나뉜다.

  1. 가변 분할 방식 : 프로세스 크기에 따라 메모리를 할당한다. 세그멘테이션이라고도 부른다. 프로세스가 종료되면 메모리가 내려가지만 외부 단편화가 발생할 수 있다. 조각 모음을 하려면 프로세스의 작업을 일시 중단해야 한다. 따라서 오버헤드가 발생한다.

  2. 고정 분할 방식 : 프로세스 크기와 상관없이 고정으로 메모리를 할당한다. 페이징이라고도 부른다. 내부단편화를 통해 메모리에 낭비가 발생한다.

  3. 버디 시스템 : 위의 둘을 합친 방식이다. 전체 메모리를 2로 나누어 프로세스 단까지 나누고 프로세스를 할당한다. 이 방식의 장점은 세그멘테이션 처럼 프로세스를 잘 할당하고 내부 단편화가 크게 나타나지 않는다.

가상메모리

가상 메모리 개요

컴퓨터 마다 메모리의 크기는 다르다. 그래서 가상메모리를 사용한다.

하드 디스크 내의 스왑 영역으로 옮기고 거기에서 메모리에서 꺼내와서 실행한다.

MMU는 부족한 물리 메모리와 메모리 변환 과정같은 복잡한 과정을 거친다.

MMU는 매핑 테이블을 통해 프로세스와 위치를 저장한 뒤 사용한다.

세그멘테이션

프로그램 관점에서 메모리는 서로 인접할 필요가 없지만 프로세스는 각 비슷한 위치에 존재한다.

사용자 프로세스, cpu는 논리주소를 사용한다. MMU가 고정 주소로 변환해준다.

MMU는 세그먼트 번호와 base address와 bound address를 가지고 있고 이를 통해 메모리를 관리한다.

세그멘테이션은 메모리를 분할할수 있고 각 영역을 모듈로 처리하여 분할이 쉽다.

페이징

논리주소공간과 물리주소공간이 있다. 페이징에서 논리주소공간은 일정한 크기로 나뉜다. 물리주소공간 역시 일정하게 나뉜다.

MMU는 페이지 테이블을 가지고 있다. 인덱스와 프레임으로 이루어져 있다. 논리주소를 페이지의 크기로 나누어 페이지 넘버를 구한다. 오프셋은 논리주소 % 페이지의 크기이다. 페이지 넘버와 오프셋을 통해 프레임 번호에 오프셋을 더해 물리주소를 알아낸다.

페이징은 모두 크기가 같아 바운드 어드레스가 필요없다.

페이지드 세그멘테이션

세그멘테이션과 페이징을 혼합하여 단점을 취한다.

가변분할 방식으로 각 영역을 공유하기 편하고 메모리 접근 보호에 편하다. 페이징은 메모리를 효율적으로 관리할 수 있다.

메모리 접근 권한은 읽기 쓰기 실행으로 나뉜다. 각 영역마다 접근 권한이 있다. 코드는 읽기, 쓰기, 데이터는 읽기, (쓰기), 스택과 힙은 읽기, 쓰기 권한이 있다.

각 권한은 MMU가 관리하는데 권한이 없다면 프로세스를 종료한다.

세그먼트 번호, 권한비트, 페이지넘버, 페이지 수로 MMU는 테이블을 관리한다.

디맨드 페이징(가져오기 정책)

프로세스가 실행될때 모든 모듈이 메모리에 올라오는 것이 아니라 필요한 부분만 올라온다.

지역성 이론 : 공간의 지역성으로 현재 위치와 가까운 데이터에 접근한다. 시간의 지역성으로 최근 접근했던 데이터가 오래 전에 접근했던 데이터보다 접근할 가능성이 높다.

디맨드 페이징은 사용될거 같은 데이터는 메모리에 올리고 다른 데이터는 스왑영역으로 보낸다.

페이지 테이블 엔트리는 다음 구조이다. 접근비트, 변경비트(변경 유무), 유효비트, 읽기,쓰기,실행비트(권한비트), 프레임

물리 메모리에 없다면 스왑영역에 접근한 뒤 메모리에 올라가게 되고 프로세스는 다시 실행된다.

페이지 교체 정책

스왑영역에서 메모리를 불러들이는데 메모리에 공간이 없다면 메모리에 어떤 페이지를 스왑영역으로 보낼지 결정하는 것이 페이지 교체 정책이다.

  1. 랜덤으로 다양하게 보내기

  2. 가장 오래된 페이지를 보내기

  3. 가장 오랫동안 쓰이지 않을 페이지를 보내기

  4. 최근 가장 사용이 적은 페이지를 보낸다.(LRU)

LRU가 가장 좋은 방법인데 시간은 오버플로우가 나므로 일정시간마다 초기화하는 접근을 초기화 하는 클락 알고리즘을 사용한다.

클락 알고리즘은 스왑이 일어나야 할때 접근 되지 않은 페이지를 스왑으로 보낸다.

접근 비트와 변경 비트를 사용하여 둘다 0인 페이지 를 스왑으로 보내는 향상된 클락알고리즘도 있다.

스레싱과 워킹셋

멀티프로그래밍이 늘어나면 물리 메모리에 올려야할 데이터가 늘어나고 스왑해야할 작업이 늘어난다.

cpu 사용률이 더 낮아진다. 이를 스레싱이라고 부른다.

스레싱은 물리 메모리의 용량이 너무 낮은 것에 기인한다. 충분한 크기라서 스레싱이 발생하지 않는다면 성능향상은 일어나기 힘들다.

프로세스가 실행되면 page fault가 발생할 때 페이지를 더 많이 할당한다. page fault가 일어나지 않는다면 페이지 수를 줄여 적절한 페이지를 유지한다.

워킹셋은 프로세스가 실행상태가 되는 컨텍스트 스위칭이 일어날 때 사용한다. 워킹셋을 한꺼번에 올리고 내린다.

입출력 장치

주변장치

주변장치는 메인보드에 있는 버스로 연결된다. 주소버스 데이터버스, 컨트롤버스로 연결된다.

장치의 상태를 보관하는 레지스터로 입출력 작업에서 저장된다.

캐릭터 디바이스와 블록 디바이스로 구분된다.

전송단위가 캐릭터인지 블록인지로 구분된다.

캐릭터 디바이스는 상대적으로 적은 데이터를 전송하고 블록 디바이스는 큰 단위로 연결한다.

마우스/키보드

마우스와 키보드는 캐릭터디바이스 이다.

디바이스 컨트롤러가 어떤 동작을 했는지 받고 운영체제에 이벤트를 넘기고 운영체제는 프로그램으로 데이터를 넘겨 동작을 수행한다.

하드디스크 / Flash Memory(SSD)

하드디스크는 플래터와 스핀들이 존재한다. 헤드와 디스크암으로 자성으로 하드 디스크의 섹터 단위로 데이터를 읽는다.ms 단위로 움직이는데 다른 장치들은 ns단위로 읽으므로 상대적으로 느리게 인식된다.

flash memory는 기계적으로 헤드를 움직이는 것이 아니라 전기적으로 데이터를 읽는다. 그로인해 하드디스크보다 상대적으로 더 우위에 있다.

파일시스템

파일과 파일시스템

파일은 하드디스크와 ssd에 저장된다. 사용자는 운영체제를 통해 안전하게 저장한다.

운영체제는 파일시스템을 통해 파일을 관리한다. 파일관리자는 파일테이블을 통해 파일을 관리한다.

  1. 파일과 디렉토리를 만든다.

  1. 파일과 디렉토리 수정/삭제한다.

  1. 파일의 권한을 관리한다.

  1. 무결성을 보장한다.

  1. 백업과 복구를 한다.

  1. 파일을 암호화한다.

디스크는 전송단위가 블록이다. 파일관리자가 중간에서 관리한다.

windows의 경우 확장자를 통해 파일의 구분을 하고 바로 연결되는 프로그램을 관리할 수 있다.

파일은 헤더와 데이터로 구분한다. 헤더에는 확장자나 사용자 등이 저장된다.

파일 디스크립터는 저장장치에 존재하다가 파일을 호출하면 파일을 호출할 수 있다.

순차파일구조는 파일이 순차적으로 저장되어 있다. 모든 데이터가 순서대로 기록되기 때문에 공간의 낭비가 없고 단순하다. 특정 지점으로 이동이 어려워 삭제와 수정이 어렵다.

직접파일구조는 해시 함수를 통해 저장위치를 파악하여 저장한다. json도 이러한 방식이다. 주소에 따라 데이터를 저장한다. 해시 테이블을 이용하기 때문에 저장과 접근이 빠르지만 해시 함수를 잘 선정해야 한다.

인덱스파일구조는 두 가지 방식이 모두 가능하다. 파일은 순차로 저장되어 있는데 인덱스 테이블을 통해 블록 번호를 알아내어 바로 데이터에 접근한다.

디렉토리

디렉토리는 1개 이상파일과 디렉토리를 가질 수 있다. 루트는 /를 사용하고 구분을 통해 사용한다. 디렉토리 역시 파일이다. 단지 디렉토리에는 파일정보가 저장되어 있다.

파일과 디스크

파일제어 테이블은 파일안에 여러개의 블록으로 나누어 저장한다.

불연속할당(연결할당) 연결리스트를 통해 해당 노드가 포인터를 통해 다른 노드를 이동하여 데이터를 조회한다. 시작 노드만 가지고 있고 이를 순회하다가 null을 만날때까지 저장하여 모든 데이터를 가져온다.

불연속할당(인덱스할당) 데이터의 인덱스를 가지고 있는 노드를 통해 가져온다.

빈공간을 계속 찾는 것은 비효율적이다. 효율적 관리를 위해 free block list를 통해 비어있는 블록을 남겨놓는다. 사용자는 데이터를 지워도 파일할당 테이블의 데이터를 남기므로 포렌식을 통해 복구할 수 있다.

댓글을 작성해보세요.


채널톡 아이콘