본문 바로가기
코딩테스트

04. 운영체제(1,2,3)

by 왜안돼요 2025. 2. 12.
728x90

운영체제의 큰 그림

운영체제는 종류에 관계 없이 운영체제가 제공하는 핵심적인 기능은 비슷하다. 이러한 운영체제의 핵심 기능을 담당하는 부분을 커널이라고한다. 커널은 마치 자동차의 엔진이나 사람의 심장과도 같은 핵심부라고 할 수 있다. 

 

운영체제에는 크게 2가지 핵심 기능이 있다. 하나는 자원 할당 및 관리이고, 또 하나는 프로세스 및 스레드 관리이다.

 

운영체제의 역할

운영체제의 핵심 기능인 자원 할당 및 관리에 대해 알아보기 위해서는 우선 자원이 무엇인지부터 이해해야 합니다. 자원(혹은 시스템 자원)이란 프로그램 실행에 마땅히 필요한 요소를 의미한다. 실행에 필요한 데이터를 자원(소프트웨어)이라고 하기도 하고, 실행에 필요한 부품을 자원(하드웨어)이라고 하기도 한다. 

 

운영체제는 사용자가 실행하는 응용 프로그램을 대신하여 CPU, 메모리, 보조기억장치 등의 컴퓨터 부품에 접근하고, 각각의 부품들이 효율적으로 사용되도록 관리한다. 또 응용 프로그램이 컴퓨터부품들을 효율적으로 할당받아 문제 없이 실행할 수 있도록 응용프로그램에게 자원을 할당한다. 

 

CPU관리: CPU스케줄링

메모리에는 실행 중인 프로그램이 다수 적재될 수 있지만, CPU가 이들 모두를 동시에 실행할 수 있는 것은 아니다. CPU는 한정된 자원이기 때문에 CPU를 할당받아 사용하기 위해 때로는 다른 프로그램의 CPU 사용이 끝날 때 까지 기다려야한다. 그래서 운영체제는 실행 중인 모든 프로그램들이 공정하고 합리적으로 CPU를 할당받도록 CPU의 할당 순서와 사용 시간을 결정한다. 

 

메모리 관리: 가상 메모리

운영체제는 새롭게 실행하는 프로그램을 메모리에 적재하고, 종료된 프로그램을 메모리에서 삭제한다. 동시에 낭비되는 메모리 용량이 없도록 효율적으로 관리해야한다. 

 

파일/디렉터리 관리: 파일 시스템

메모리보다 더 큰 용량을 갖고 있는 보조기억장치는 더욱 일목요연하게 관리할 필요가 있다. 보조기억장치에 아무렇게나 정보를 저장한다면 마치 어질러진 서랍에서 원하는 물건을 찾듯 원하는 정보에 접근하기 이해 오랜 시간이 소요되기 때문이다. 운영체제는 보조기억장치를 효율적으로 관리하기 위해 파일 시스템을 활용한다. 파일 시스템은 보조기억장치 내의 정보를 파일 및 폴더 단위로 접근,관리할 수 있도록 만드는 운영체제 내부 프로그램이다. 

 

운영체제의 입출력 장치 및 캐시 메모리 관리
컴퓨터 핵심 부품인 CPU,메모리,보조기억 장치 외에 입출력장치와 캐시메모리도 운영체제에 의해 관리되는 자원이다. 운영체제는 일부 입출력장치의 장치 드라이버, 하드웨어 인터럽트 서비스 루틴을 제공하거나 캐시 메모리의 일관성을 유지하는 등의 기능을 제공한다.

 

 

프로세스 및 스레드 관리

단순히 실행 중인 프로그램이라고 지칭했던 대상은 프로세스라고한다. 그리고 스레드는 이 프로세스를 이루는 실행의 단위이다. 메모리에는 여러 프로세스가 적재될 수 있는데, 운영체제는 이 프로세스에 필요한 자원을 할당하고, 스레드는 프로세스가 할당받은 자원을 이용해 프로세스의 작업을 수행한다. 프로세스를 이루는 *스레드가 둘 이상인 경우에는 동일한 작업을 동시에 실행할 수도 있다.

 

 

운영체제는 이렇게 동시다발적으로 실행되는 프로세스와 스레드가 올바르게 처리되도록 실행의 순서를 제어하고, 프로세스와 스레드가 요구하는 자원을 적절하게 배분할 수 있어야한다. 

 

스레드는 동일한 작업을 동시에 실행할 수도 있다는 것에 대한 예
웹 브라우저는
• 한 스레드는 UI 렌더링

• 다른 스레드는 네트워크 요청 처리

• 또 다른 스레드는 사용자 입력 처리

→ 이렇게 동시에 실행될 수 있다.

 

정리하자면

  • 운영체제는 프로세스에 자원을 할당하고
  • 스레드는 그 자원을 이용해 작업을 수행한다
  • 멀티스레딩을 활용하면 여러 작업을 동시에 실행할 수 있다.

 

 

시스템 콜과 이중모드

운영체제도 일종의 프로그램이기 때문에 프로그램이 실행되기 위해서는 반드시 메모리에 적재되어 있어야한다. 사용자 프로그램과는 달리 운영체제는 매우 특별한 프로그램이므로 메모리 내의 커널 영역이라는 공간에 따로 적재되어 실행된다.

사진과 같이 운영체제가 적재되는 커널영역 외에 사용자 응용 프로그램이 적재되는 공간은 사용자 영역이라고한다.

 

일반적으로 웹 브라우저나 게임과 같은 사용자 응용 프로그램은 운영체제와 달리 CPU, 메모리와 같은 자원에 직접 접근하거나 제어할 수 없다. 특정 자원에 접근하거나 조작하는 운영체제 코드를 실행해야한다. 즉 운영체제가 자신의 코드를 실행한 응용 프로그램의 자원 접근 및 조작을 대행하는 셈이다.

 

응용 프로그램은 시스템콜을 호출하여 운영체제 코드를 실행할 수 있다. 시스템 콜은 운영체제의 서비스를 제공 받기 위한 수단(인터페이스)으로, 호출 가능한 함수의 형태를 가진다. 응용 프로그램이 운영체제로부터 어떤 기능을 제공받고자 한다면 다음과 같이 그 기능에 해당하는 시스템 콜을 호출하면 된다.

 

 

 

운영체제에 따라 제공하는 시스템 콜의 종류와 개수는 다양하다. 

 

컴퓨터 내부에서 시스템 콜이 호출되면 다음과 같은 작업이 수행된다. 여기서 핵심은 소프트웨어 인터럽트와 커널모드, 사용자모드이다.

 

소프트웨어 인터럽트 발생 -> CPU의 커널모드 전환 -> 운영체제 코드 실행 -> 사용자 모드로 재전환

 

운영체제에는 인터럽트를 발생시키는 특정 명령어가 있다. 자원에 접근하는 입출력 명령어가 대표적이며, 이러한 명령어에 의해 발생하는 인터럽트를 소프트웨어 인터럽트라고 한다.

 

시스템 콜이 소프트웨어 인터럽트의 일종이다. 따라서 ①사용자 영역을 실행하는 과정에서 시스템 콜이 호출되면 여느 인터럽트와 마찬가지로 CPU는 현재 수행 중인 작업을 백업하고 ②커널 영역 내의 인터럽트를 처리하기 위한 코드(시스템 콜을 구성하는 코드)를 실행한 뒤 ③다시 사용자 영역의 코드 실행을 재개한다.

 

CPU는 명령어를 실행하는 과정에서 사용자 영역을 실행할 때의 모드와 커널 영역을 실행할 때의 모드를 구분하여 실행한다.

사용자 영역에 적재된 코드를 실행할 때의 실행 모드를 사용자 모드라 하고, 커널 영역에 적재된 코드를 실행할 때의 모드를 커널 모드라 한다. 이렇게 2개의 모드로 구분하여 실행하는 것을 이중모드라고 한다.

 

사용자 모드는 *운영체제 서비스를 제공받을 수 없는 실행 모드로, 커널 영역의 코드를 실행할 수 없는 모드이다. 사용자 모드로 실행 중인 CPU는 입출력 명령어와 같이 자원에 접근하는 명령어를 만나도 이를 실행하지 않는다. 사용자 모드로 실행되는 명령어는 실수로라도 자원에 접근할 수 없다.

 

커널모드는 운영체제 서비스를 제공받을 수 있는 실행 모드로, 커널 영역의 코드를 실행 할 수 있는 모드이다. CPU가 커널 모드로 명령어를  실행하면 자원에 접근하는 명령어를 비롯한 모든 명령어를 실행할 수 있다.

 

  사용자모드 커널 모드
CPU 접근 ❌ 직접 X ✅ 직접 O
메모리 접근 ❌ OS가 허용한 부분만 ✅ 모든 메모리 접근 가능
하드웨어 제어 ❌ 불가능 ✅ 가능 (디스크, 네트워크, 프로세스 등)
운영체제 기능 ✅ 시스템 호출을 통해 가능 직접 가능

 

운영체제 서비스를 제공받을 수 없는 실행 모드라는 것은 운영체제의 서비스 (파일 시스템, 프로세스 관리, 네트워크 등)를 사용자 모드에서는 직접 호출 할 수 없다는 뜻이다.
하지만, 시스템 콜을 사용하면 운영체제의 기능을 이용할 수 있다. 사용자 프로그램이 운영체제의 기능을 쓰려면 시스템 호출을 통해 커널 모드로 전환해야한다.

 

응용 프로램은 실행 과정에서 시스템 콜을 매우 빈번하게 호출한다. 이처럼 프로그램은 시스템 콜을 통해 사용자 모드와 커널 모드를 빈번히 오가며 운영체제의 소스 코드를 실행한다.

프로세스와 스레드

메모리에는 컴퓨터가 실행되는 순간부터 다양한 프로세스들이 적재되어 실행된다. 

 

프로세스 유형

  1. 사용자가 보는 공간에서 사용자와 상호작용하며 실행되는 포그라운드 프로세스
  2. 사용자가 보지 못하는 곳에서 실행되는 백그라운드 프로세스
    1. 백그라운드 프로세스중에서도 사용자와 별 다른 상호작용 없이 주어진 작업만 수행하는 데몬

윈도우 운영체제에서는 데몬을 서비스라 부른다.

 

커널영역에는 프로세스 제어블록(PCB)라는 정보가 저장되고, 사용자 영역에는 실행 중인 프로세스가 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 저장된다.

 

 

코드영역

코드영역은 실행 가능한 명령어가 저장되는 공간으로 텍스트 영역이라고도 부른다. CPU가 읽고 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어있는 읽기 전용 공간이다.

 

데이터 영역

데이터 영역은 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간이다. 데이터 영역에 저장되는 데이터는 정적 변수나 전역변수가 대표적이다.

BSS 영역
실제로는 BSS라는 영역도 추가로 구분하는 경우가 많다. BSS 영역은 데이터 영역과 유사하지만 초기화 여부가 다르다. 프로그램을 사용하는 동안 유지할 데이터 중 초깃값이 있는 정적 변수나 전역 변수와 같이 초깃 값이 있는 데이터는 데이터 영역에 저장되고, 초깃값이 없는 데이터는 BSS 영역에 저장된다.

 

코드 영역과 데이터 영역은 프로그램 실행 도중 크기가 변하지 않기 때문에 정적 할당 영역이라고 부른다.

크기가 변할 수 있는 힙 영역과 스택 영역은 동적 할당 영역이라고 부른다.

 

힙 영역

힙 영역은 프로그램을 만드는 개발자가 직접 할당 가능한 저장공간이다. 프로그램 실행 도중 비교적 자유롭게 할당하여 사용 가능한 메모리 공간이라 볼 수 있다. 힙 영역에 메모리 공간을 할당 했다면 언젠가는 해당 공간을 반환해야한다. 

메모리 공간을 반환하지 않으면 할당한 공간이 계속 메모리 내에 남아 메모리 누수문제를 초래할 수 있다. 이러한 문제를 해결하기 위해 프로그래밍 언어에서 자체적으로 사용되지 않는 힙 메모리를 해제하는 가비지 컬렉션 기능을 제공하도 한다.

 

 

스택 영역

스택 영역은 데이터 영역에 담기는 값과는 달리 일시적으로 사용할 값들이 저장되는 공간이다.

함수 실행이 끝나면 사라지는 매개변수, 지역 변수, 함수 복귀 주소 등이 스택 영역에 저장되는 대표적인 데이터들이다.

스택 영역에는 스택 트레이스 형태의 함수 호출 정보가 저장될 수 있다는 점을 기억해야한다.

스택 트레이스란 특정 시점에 스택 영역에 저장된 함수 호출 정보를 말한다.

 

PCB와 문맥 교환

운영체제가 메모리에 적재된 다수의 프로세스를 관리하려면 프로세스를 식별할 수 있는 커널 영역 내의 정보가 필요하다. 이 정보가 바로 프로세스 제어 블록이다. PCB는 프로세스와 관련한 다양한 정보를 내포하는 구조체의 일종으로, 새로운 프로세스가 메모리에 적재됐을 때 커널 영역에 만들어지고 프로세스의 실행이 끝나면 폐기된다.

 

PCB에 담기는 정보는 운영체제마다 차이가 있지지만 대표적으로

  1. 프로세스 식별 번호인 프로세스ID
  2. 프로세스가 실행과정에서 사용한 레지스터 값
  3. 프로세스가 현재 어떤 상태인지를 나타내는 프로세스 상태
  4. 프로세스가 언제 어떤 순위로 CPU를 할당받을지 나타내는 CPU 스케줄링(우선순위 정보)
  5. 프로세스의 메모리상 적재 위치를 알 수 있는 메모리 관련 정보
  6. 프로세스가 사용한 파일 및 입출력장치 관련 정보가 명시된다.

여러 PCB들은 커널 내에 프로세스 테이블의 형태로 관리되는 경우가 많다. 프로세스 테이블은 실행 중인 PCB의 모음을 의미한다. 

새롭게 실행되는 프로세스가 있다면 해당 프로세스의 PCB를 프로세스 테이블에 추가하고, 필요한 자원을 할당한다. 반대로, 종료되는 프로세스가 잇다면 사용중이던 자원을 해제하고 PCB도 프로세스 테이블에서 삭제된다.

 

 

프로세스가 비정상 종료되어 사용한 자원이 회수되었음에도 프로세스 테이블에 종료된 프로세스의 PCB가 남아 있는 경우가 있다. 이러한 비정상 종료 상태를 좀비 프로세스라고한다.

 

 

메모리에 적재된 프로세스들은 한정된 시간 동안 번갈아 가며 실행된다. 이때 프로세스가 실행된다는 말은 운영체제에 의해 CPU의 자원을 할당받았다는 말과 같다.

 

CPU가 프로세스를 구성하는 명령와 데이터를 인출하여 실행하고, 운영체제가 CPU 자원을 항당하기 때문이다. 

다양한 프로세스들이 한정된 시간 동안 번갈아 가며 실행된다는 말은 다양한 프로세스들이 한정된 시간동안 운영체제로부터 CPU의 자원을 번갈아 가며 할당받아서 이용한다는 말과 같다.

 

프로세스의 CPU 사용 시간은 타이머 인터럽트에 의해 제한된다. 

 

타이머 인터럽트란 

시간이 끝났음을 알리는 인터럽트로 타임아웃 인터럽트라고도 부른다

프로세스는 자신의 차례가 되면 정해진 시간만큼 CPU를 이용하고, 타이머 인터럽트가 발생하면 자신의 차례를 양보하고 다음 차례가 올때까지 기다린다.

 

예를 들어 프로세스 A가 운영체제로부터 CPU를 할당 받아 실행되다가 타이머 인터럽트가 발생하여 프로세스 B로 CPU 사용을 양보한다고 가정한다면 이때 프로세스 A는 프로그램 카운터를 비롯한 각종 레지스터 값과 메모리 정보, 실행을 위해 열었던 파일, 사용한 입출력장치 등 지금까지의 중간 정보를 백업해야한다. 그래야 다음에 다시 프로세스A를 실행할 차례가 되었을때 이전까지 실행했던 내용을 이어서 재개할 수 있다.

 

여기서 백업 대상이 되는 중간 정보, 프로세스의 수행을 재개하기 위해 기록해야 할 정보를 문맥이라고한다.

프로세스의 문맥은 해당 프로세스의 PCB에 명시된다.

프로세스가 CPU를 사용할 수 있는 시간이 다 되거나 인터럽트가 발생하면 운영체제는 해당 프로세스의 PCB에 문맥을 백업한다.

그리고 뒤이어 실행할 프로세스의 문맥을 복구한다.  이렇게 실행되는 프로세스가 자연스럽게 바뀌는 것이다.

 

기존 프로세스 문맥은 PCB에 백업하고 PCB에서 문맥을 복구하여 새로은 프로세스를 실행하는 것을 문맥 교환이라고 한다. 문맥 교환은 여러 프로세스가 끊임 없이 빠르게 번갈아 가며 실행되는 원리인 셈이다.

 

프로세스 간 너무 잦은 문맥교환이 발생하면 캐시 미스가 발생할 가능성이 높아져 메모리로부터 실행할 프로세스의 내용을 가져오는 작업이 빈번해지고, 이는 큰 오버헤드로 이어질 수 있다.

 

프로세스의 상태

하나의 프로세스는 여러 상태를 거치며 실행된다. 운영체제는 PCB를 통해 프로세스의 상태를 인식하고 관리한다. 

운영체제마다 프로세스의 상태를 표현하는 방식은 조금씩 차이가 있지만, 대표적인 상태로는 생성, 준비, 실행, 대기, 종료등이 있다.

 

 

  • 생성상태 (new)
    프로세스를 생성중인 상태로, 메모리에 적재되어 PCB를 할당받은 상태이다. 생성 상태를 거쳐 실행할 준비가 완료된 프로세스는 준비 상태가 되어 CPU의 할당을 기다린다.
  • 준비상태 (ready)
    당장이라도 CPU를 할당받아 실행할 수 잇지만, 아직 자신의 차례가 아니기 때문에 기다리고 있는 상태를 말한다. 준비 상태인 프로세스가 CPU를 할당 받으면 실행 상태가 되며, 준비 상태인 프로세스가 실행 상태로 전환되는것을 디스패치라고 한다.
  • 실행상태(running)
    실행 상태는 CPU를 할당받아 실행 중인 상태로, 일정 시간동안만 CPU를 사용할 수 있다. 타이머 인터럽트가 발생하여 프로세스가 할당된 시간을 모두 사용하면 다시 준비 상태가 되고, 실행 도중 입출력장치를 사용하여 입출력장치의 작업이 끝날 때까지 기다려야하면 대기 상태가 된다
  • 대기상태 (blocked)
    프로세스가 입출력 작업을 요청하거나 바로 확보할 수 없는 자원을 요청하는 등 곧장 실행이 불가능한 조건에 놓이는 경우 대기 상태가 된다. 대기 상태로 전환되는 상황은 다양하지만, 입출력 작업을 요청하는 경우가 대표적이다. 대기 상태였던 해당 프로세스는 입출력 작업이 완료되는 등 실행 가능한 상태가 되면 다시 준비 상태가 되어 CPU 할당을 기다린다
  • 종료상태 (terminated)
    종료 상태는 프로세스가 종료된 상태를 말한다. 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리한다.

블로킹 입출력(Blocking I/O) vs 논블로킹 입출력(Non-blocking I/O)

 

블로킹 입출력 (Blocking I/O)

  • 프로세스가 입출력(I/O) 작업을 요청하면, 작업이 끝날 때까지 대기해야 함
  • 입출력 완료 후에야 실행을 재개
  • 예: 파일 읽기, 네트워크 요청에서 데이터가 올 때까지 기다리는 경우

 

예시

1. 프로세스가 네트워크를 통해 데이터를 보냄

2. 데이터가 정상적으로 전송되었는지 확인될 때까지 대기

3. 전송 완료되면 다시 실행 상태로 돌아감

 

장점: 구현이 직관적이고 단순함

단점: 입출력 작업이 끝날 때까지 다른 작업을 못하고 기다려야 함 (비효율적)

 

논블로킹 입출력 (Non-blocking I/O)

  • 프로세스가 입출력을 요청한 후, 결과를 기다리지 않고 바로 다음 작업을 수행
  • 입출력이 완료되면 운영체제가 **알림(이벤트, 인터럽트 등)**을 통해 알려줌
  • 예: 네트워크 요청을 보낸 후 바로 다른 작업을 수행하는 경우

 

 예시

1. 프로세스가 네트워크를 통해 데이터를 보냄
2. 데이터가 정상적으로 전송되었는지 기다리지 않고 바로 다음 명령어 실행
3. 나중에 입출력이 완료되면 이벤트를 통해 결과를 확인

장점: 대기 시간이 없어 효율적이고 빠름

단점: 프로그래밍이 복잡해질 수 있음 (이벤트 처리 필요)

 

📌 정리

구분 블로킹 I/O 논블로킹I/O
실행방식 I/O가 끝날 때까지 대기 I/O 요청 후 즉시 다음 작업 수행
대기 여부 대기 상태로 전환됨 대기 없이 바로 실행
장점  구현이 직관적, 단순함 대기 시간이 없어 효율적임
단점 비효율적(시간 낭비) 이벤트 처리 등의 추가 작업 필요

즉, 블로킹I/O는 기다려야하고, 논블로킹I/O는 기다리지 않고 다른 작업을 할 수 있다.

 

 

멀티프로세스와 멀티스레드

멀티프로세스

  • 여러 개의 프로세스를 동시에 실행하는 방식
  • 예: 웹 브라우저는 각 탭마다 별도의 프로세스를 생성하여 동작
  • 각 프로세스는 독립적인 메모리 공간을 가짐 (자원 공유 X)
  •  한 프로세스에 문제가 생겨도 다른 프로세스에 영향을 주지 않음

특징

  •  프로세스 간 자원 공유 X (독립적 실행)
  • 안정적 (한 프로세스가 죽어도 다른 프로세스 영향 없음)
  • 운영체제의 개입 필요 (문맥 전환 비용이 큼)

멀티스레드

  • 하나의 프로세스 내에서 여러 스레드를 동시에 실행하는 방식
  • 예: 웹 브라우저의 한 탭에서 여러 작업(렌더링, 다운로드, 사용자 입력 처리 등)을 동시에 수행)
  • 같은 프로세스 내의 모든 스레드는 메모리를 공유 (코드, 데이터, 힙 영역 공유)
  • 하지만 각 스레드는 자신만의 스택과 프로그램 카운터를 가짐

특징

  • 자원 공유 O (프로세스 내 스레드끼리 공유 가능)
  • 빠름 (프로세스 간 전환보다 비용 적음)
  • 한 스레드가 문제를 일으키면 전체 프로세스가 영향을 받을 가능성 있음

멀티프로세스 vs 멀티스레드 비교

구분 멀티프로세스 멀티스레드
실행단위 여러개의 프로세스 하나의 프로세스 내 여러 스레드
자원공유 X (독립적임) O(같은 프로세스 내 공유)
안정성 O(한 프로세스에 문제가 발생해도 영향이없음) X(한 스레드에 문제 발생 시 전체 프로세스에 영향이 있음)
성능 문맥 전환 비용이 크다 문맥 전환 비용이 적으
예시 웹 브라우저의 각 탭, 크롬 브라우저의 다중 프로세스 구조 웹 브라우저의 한 탭 내에서 여러작업 수행, 게임엔진의 여러 스레드

 

정리

  •  멀티프로세스는 여러 개의 독립적인 프로세스가 동작하며, 안정적이지만 자원 공유가 어렵고 느림
  •  멀티스레드는 한 프로세스 내에서 여러 스레드가 자원을 공유하며, 빠르지만 한 스레드의 문제가 전체 프로세스에 영향을 줄 수 있음

 

프로세스 간 통신

프로세스 간에 자원을 공유하고 데이터를 주고받는 방법을 프로세스 간 통신이라 한다. 

 

공유메모리

프로세스들이 특정 메모리 공간을 공유하여 데이터를 주고받는 방식

각 프로세스는 자신의 메모리를 읽고 쓰듯이 데이터를 처리

커널 개입이 거의 없어서 통신 속도가 빠름

파일을 이용한 방식도 가능 (예: hi.txt 파일을 공유)

 

공유메모리 기반 IPC는 프로세스가 공유하는 메모리 영역을 확보하는 시스템 콜 기반으로 수행될 수도 있고, 간단하게 프로세스가 공유하는 변수나 파일을 활용할 수도 있다. 가령 프로세스 A는 hi.txt라는 파일을 수정하는 프로세스고 프로세스B는 hi.txt라는 파일을 읽는 프로세스라고 가정했을때 두 프로세스는 hi.txt라는 파일을 매개로 프로세스 간에 통신을 주고 받은 셈이다.

 

 특징

  • 속도가 빠름 (커널을 거치지 않음)
  • 데이터를 읽고 쓰는 방식이 직관적
  • 레이스 컨디션 발생 가능 (동시에 접근할 경우 데이터 충돌 가능)

 

메세지 전달

프로세스 간에 주고받을 데이터가 커널을 거쳐 송수신되는 통신방식이다.

 

각각의 프로세스가 자신의 메모리 영역을 읽고 쓰는것으로 통신이 가능한 공유메모리 기반IPC와 달리, 메시지 전달 기반 IPC는 메시지를 보내는 수단과 받는 수단이 명확하게 구분되어 있다. 

  • 커널을 거쳐 데이터를 송수신하는 방식
  • 메시지를 보내는 시스템 콜(send()), 받는 시스템 콜(recv())을 사용
  • 메시지 전달 기반 IPC를 위한 대표적인 수단으로 파이프, 소켓, 시그널, 원격 프로시저 호출(RPC) 등이 있음

 

파이프

  • 프로세스 간 단방향 데이터 통신을 위한 통로
  • FIFO(First In, First Out) 구조
  • 부모-자식 프로세스 간 통신에 주로 사용됨
  • 양방향 통신을 위해서는 읽기용, 쓰기용 파이프 두 개 필요

먼저 파이프란 단방향 프로세스 간의 통신 도구를 말한다. 다음 그림과 같이 프로세스 A가 파이프의 한 쪽 방향에서 데이터를 쓰면 프로세스B는 파이프 반대쪽으로 그 데이터를 읽을 수 있다. 파이프를 물길처럼 데이터가 흐를 수 있는 공간이라고 생각하며 ㄴ쉬움

파이프에 삽입된 데이터의 관점에서 보면 먼저 파이프에 삽입된 데이터가 먼저 읽히게 된다. 단방향으로만 읽고 쓸 수 있는 파이프로 양방향 통신을 수행할 경우 읽기용 파이프와 쓰기용 파이프 2개를 이용해 양방향으로 통신하는 경우가 많다.

 

 

 

 

익명파이프 vs 지명파이프

단방향 통신 수단인 전통적인 파이프는 익명파이프라고도 부른다 익명 파이프는 양방향 통신을 지원하지 않고 부모 프로세스와 자식 프로세스 간에만 통신이 가능하다는 한계가 있다.

익명 파이를 확장한 파이프를 FIFO 혹은 지명파이프라고 한다. 영문 그대로 네임드 파이프라고 부르는 경우가 많다. 지명 파이프는 양방향 통신을 지원하며 부모 프로세스와 자식 프로세스 간에만 통신이 가능한 것이 아니라 임의의 프로세스 간에도 사용할 수 있다.

 

구분 익명 파이프 지명 파이프
통신 방향  단방향 양방향 가능
사용 가능 프로세스 부모-자식 프로세스 간 통신만 가능 임의의 프로세스 간 통신 가능
특징  기본적인 파이프 네임드파이프(FIFO)라고도 부름

 

요약

구분 공유 메모리 메시지 전달
통신 방식 메모리 공간을 공유 커널을 거쳐 데이터 송수신
속도 빠름(커널을 거치지않음) 느림 (커널을 거침)
동기화 문제 있음(레이스 컨디션이 발생할 수도 있음) 없음(커널이 관리함)
예시 공유변수, 파일, 메모리 매핑 파이프, 소켓, 시그널, RPC

 

즉, 공유메모리는 빠르지만 동기화 문제를 고려해야하고, 메시지 전달 방식은 안정적이지만 속도가 느릴 수 있음

 

시그널

  • 프로세스에게 특정 이벤트가 발생했음을 알리는 비동기적 신호
  • 인터럽트와 관련된 이벤트가 많지만, 사용자가 직접 정의 가능
  • 시그널이 발생하면 프로세스는 하던 작업을 멈추고 시그널 핸들러를 실행 -> 이때 프로세스는 직접 특정 시그널을 발생 시킬 수 있고, 직접 일부 시그널 핸들러를 (재)정의할 수 있다. 

시그널은 직접 메시지를 주고받진 않지만 비동기적으로 원하는 동작을 수행할 수 있는 좋은 수단이다.

 

시그널의 기본 동작과 코어 덤프

 

시그널의 기본 동작

시그널마다 수행할 기본 동작이 지정되어 있음

 

대표적인 기본 동작:

  1. 프로세스 종료 (예: SIGKILL, SIGTERM)
  2. 무시 (Ignore) (예: SIG_IGN)
  3. 코어 덤프(Core Dump) 생성 (예: SIGSEGV, SIGABRT)

코어 덤프(Core Dump)란?

  • 비정상 종료된 프로그램의 메모리 상태를 기록한 파일
  • 디버깅을 위해 사용됨 (예: gdb를 활용한 분석)
  • 프로세스가 특정 시점에서 작업하던 메모리 상태를 확인 가능

동기화와 교착 상태

공유자원

프로세스 혹은 스레드가 공유하는 자원은 말 그대로 공유 자원이라고 한다. 

공유 자원은 메모리나 파일이 될 수도 있고, 전역 변수나 입출력장치가 될 수도 있다. 

다수의 프로세스 혹은 스레드가 동시에 공유자원에 접근할 경우 실행에 문제가 발생할 수 있다.

 

 

임계 구역(Critical Section)이란

공유 자원에 접근하는 코드 중, 동시 실행 시 문제가 발생할 수 있는 부분

예시:

  • 프로세스 A가 공유 메모리에 데이터를 쓰는 코드
  • 프로세스 B가 공유 메모리에서 데이터를 읽는 코드

 

프로세스A가 실행된 뒤 프로세스 B가 실행되는 것은 문제가 되지 않는다. 하지만 반대로 프로세스 B가 실행된 뒤에 프로세스 A가 실행된느것으 문제가 될 수 있다. 아직 쓰이지 않은 메모리를 읽으려 했기때문이다. 즉 프로세스A의 공유메묄 공간에 데이터를 쓰는 코드와 프로세스B의 공유메모리 공간을 읽는 코드는 임계구역이 된다.

 

동시에 파일을 수정하는 스레드를 생각했을때 각 스레드들이 파일을 수정하는 과정은 ①파일을 읽어들이고 ②원하는 내용을 작성한 뒤 ③작성한 내용을 저장하는 과정과 같다.초기 파일에 저장되 ㄴ값이 first라고 가정하고 스레드 A는 threadA를 파일에 추가하는 작업, 스레드 B는 threadB를 파일에 추가하는 작업을 수행한다고 가정했을 때 다음과 같이 스레드 A와 B가 동시에 수행될 경우 스레드 A의 작업 내역은 반영되지 않을 수 읶다.

 

 

혹은 실행도중에 다음과 같이 문맥 교환이 발생하는 경우에도 스레드 A의 작업은 반영되지 않을 수 있다. 따라서 각 스레드가 파일을 수정하는 코드는 임계 구역이 된다.

 

 

이처럼 동시다발적으로 실행되는 프로세스 혹은 스레드를 다룰 때는 언제나 임계 구역을 동시에 실행하지 않도록 유의해야한다. 앞선 예시처럼 프로세스 혹은 스레드가 동시에 임계 구역의 코드를 실행하여 문제가 발생하는 상황을 레이스 컨디션이라고한다. 레이스 컨디션이 발생하면 자원의 일관성이 손상될 수 있기 때문에 2개 이상의 프로세스 혹은 스레드가 임계 영역에 진입하고자한다면 둘 중 하나는 작업이 끝날 떄 까지 대기해야한다.

 

동시 실행 문제: 레이스 컨디션(Race Condition)

  • 동시에 여러 프로세스/스레드가 임계 구역을 실행하면 문제가 발생하는 현상
  • 결과: 공유 자원의 일관성 손상
  • 해결 방법: 동기화(Synchronization)를 통해 임계 구역 관리

레이스 컨디션을 방지하면서 임계 구역을 관리하기 위해서는 프로세스와 스레드가 동기화되어야한다. 프로세스 혹은 스레드의 동기화란 2가지 조건을 준수하며 실행하는 것을 의미한다.

  1. 실행순서 제어 : 프로세스 및 스레드를 올바른 순서로 실행하기
    예: 프로세스 A가 데이터를 쓰기ㅣ전 프로세스 B가 읽지 않도록 제어
  2. 상호 배제: 동시에 접근해서는 안되는 자원에 하나의 프로세스 및 스레드만 접근하기
    예: 한번에 하나의 프로세스만 파일을 수정하도록 제한

동기화에는 실행 순서 제어를 위한 동기화가 있고 상호 배제를 위한 동기화가 있다.

앞에서들었던 예시중 레이스 컨디션의 문제는 프로세스들이 공유 자원을 두고 올바른 순서로 실행되지 않았던것이 원인이다.

바로 실행 순서 제어를 위한 동기화가 필요한 경우이다. 공유 자원에 동시에 접근에 발생했던 레이스 컨디션의 문제는 상호 배제를 위한 동기화가 필요한 경우에 해당한다.

 

동기화의 유형

  • 실행 순서 제어를 위한 동기화
  • 문제 예시: 프로세스 A가 데이터를 쓰기 전에 프로세스 B가 먼저 읽으면 잘못된 데이터 사용
  • 해결 방법: 특정 작업이 끝난 후 다른 작업이 실행되도록 제어

  • 상호 배제를 위한 동기화
  • 문제 예시: 두 개의 프로세스가 동시에 공유 자원을 수정하면 충돌 발생
  • 해결 방법: 뮤텍스(Mutex), 세마포어(Semaphore) 등을 사용해 한 번에 하나의 프로세스/스레드만 접근 가능하도록 제한

 

동기화 기법

뮤텍스 락

동시에 접근해서는 안 되는 공유 자원을 보호하는 동기화 도구

 상호 배제(Mutual Exclusion)를 보장하여 여러 프로세스/스레드가 동시에 임계 구역에 접근하지 못하도록 제한

 

뮤텍스 락의 동작 원리

  1. 임계 구역에 접근하려면 “락(Lock)“을 획득해야 함
    acquire() 함수 호출 → 락을 획득
    한 번에 하나의 프로세스/스레드만 락을 획득 가능
  2. 임계 구역에서 작업을 수행
    락을 가진 프로세스/스레드는 다른 프로세스/스레드가 접근하지 못하도록 보호
  3. 작업이 끝나면 락을 해제해야 함
    release() 함수 호출 → 락을 해제
    다른 프로세스/스레드가 대기 중이었다면, 락을 획득하고 임계 구역에 진입

lock.acquire() → 락을 획득해야만 임계 구역에 진입 가능

lock.release() → 락을 해제하면 다른 프로세스/스레드가 임계 구역에 진입 가능

 

 

 

예를 들어 공유 자원은 1개이고, p1,p2가 공유 자원에 접근하려는 프로세스라고 할 때 p1,p2의 순서로 임계 구역에 접근한다고 가정해씅ㄹ때 뮤텍스 락을 사용하면 다음과 같은 순서로 프로세스가 실행되게 된다

 

  1. 프로세스 P1 acquire()호출, 임계 구역 진입
  2. 프로세스 P2 acquire()호출, lock을 획득하지 못해 임계 구역 접근 불가
  3. 프로세스 P1 임계 구역 작업 종료, release()호출
  4. 프로세스 P2 임계 구역 진입

 

 

 

세마포

여러개의 프로세스 및 스레드까지 특정 자원을 이용할 수 있는 상황에는 세마포를 이용할 수 있다. 뮤텍스락과 비슷하지만 조금 더 일반화된 방식의 동기화 도구이다.

세마포를 이용하면 자원이 여러개 있는 상황에서도 동기화가 가능하다.

 

세마포는 멈춤 신호와 가도 좋다는 신호로 임계구역을 관리한다. 프로세스 및 스레드가 임계 구역 앞에서 멈추라는 신호를 받으면 잠시 기다리고 가도 좋다는 신호를 받으면 그제서야 임계 구역에 들어가게 된다.

 

세마포는 뮤텍스락과 비슷하게 다음과 같은 하나의 변수와 2개의 함수로 구성된다.

  • 변수 S: 사용 가능한 공유 자원의 개수를 나타내는 변수
  • wait() 함수: 임계 구역 진입 전 호출하는 함수
  • signal()함수: 임계 구역 진입 후 호출하는 함수

사용가능한 공유 자원의 개수는 임계구역에 진입할 수 있는 프로세스의 개수와 같다.

공유자원의 개수가 S개일 경우 임계구역에 진입하여 동시에 실행 가능한 프로세스 혹은 스레드도 S개이다. 그리고 뮤텍스 락을 이용할 때 임계 구역 진업 전후로 acquire()와 release() 함수를 호출 했듯 세마포도 임계 구역 진입 전후로 wait()와 signal()함수를 호출하여 사용한다. 

 

wait()는 함수 호출 시 가장 먼저 ①사용가능한 공유 자원의 개수를 나타내는 변수 S를 1 감소 시키고 ②변수S의 값이 0보다 작은지 여부를 확인한다. S를 1 감소시켰을 때 S가 0 이상이라는 것은 사용가능한 공유 자원의 개수가 남아 있었음을 의미한다. 이 경우 wait()를 호출한 프로세스 및 스레드는 임계 구역에 진입한다. 반대로 S를 1감소시켰을때 S가 0 미만이라는 것은 사용하능한 공유 자원의 개수가 남아 있지 않음을 의미한다. 따라서 변수 S의 값이 0보다 작으면 ③wait()를 호출한 프로세스 및 스레드는 대기 상태로 전환되어 임계 구역에 진입할 수 없게 된다.

wait() {
	S--;               1
    if(S < 0){         2
    	sleep()        3
    }
}

 

 

signal 함수는 임계 구역에서의 작업이 끝난 프로세스 및 스레드가 호출한다. signal()의 대략적인 구현 원리는 다음과 같다.

signal()은 함수 호출시 가장 먼저 ①사용가능한 공유자원의 개수를 나타내는 변수S를 1증가 시키고, ②변수 S의 값이 0 이하인지 확인한다. S를 1증가 시켯을때 S가 0보다 크다는 것은 사용가능한 공유 자원의 개수가 1개 이상 남았음을 의미하며 반대로 S를 1 증가시켰을때 0 이하라는 것은 임계 구역에 진입하기 위해 대기하는 프로세스가 존재함을 의미한다. 이 경우 ③ 대기 상태로 접어든 프로세스 중 하나를 준비상태로 전환한다.

signal() {
	S++;              1
    if(S < 0){        2
    	wakeup(p)     3
    }
}

 

함수명 설명
wait() 세마포 값을 감소 (자원 요청)
signal() 세마포 값을 증가 (자원 반환)

 

이진 세마포와 카운팅 세마포

세마포는 크게 2가지 종류로 구분할 수 있다. 하나는 이진 세마포이  고 하나는 카운팅 세마포이다.

위에서 언급한 세마포는 카운팅 세마포로 공유 자원이 여러개 존재하느 경우에 사용 할 수 있는 세마포이다. 이진 세마포는 S가 0과 1의 값을 가지는 세마포이다. S가 0과 1만을 가질 수 있기 때문에 뮤텍스 락과 유사하게 동작한다. 

세마포라는 용어는 일반적으로 카운팅 세마포를 의미하는 경우가 많다.

 

조건 변수와 모니터

조건 변수란 실행 순서 제어를 위한 도구로, 특정 조건 하에 프로세스를 실행/일시 중단함으로써 프로세스나 스레드의 실행 순서를 제어할 수 있다. 조건 변수에 대해 wait()와 signal() 함수를 호출할 수 있는데, wait()함수는 호출한 프로세스 및 스레드의 상태를 대기 상태로 전환하는 함수이고 , signal() 함수는 wait()로 일시중지된 프로세스 및 스레드의 실행을 재개하는 함수이다.

 

  • 아직 특정 프로세스가 실행될 조건이 되지 않았을 때는 wait()을 통해 실행을 중단한다.
  • 특정 프로세스가 실행 조건이 충족되었을 때는 signal()을 통해 실행을 재개한다

예를 들어 cv라는 조건 변수가 있고 프로세스 P1의 실행 도중에 조건 변수 cv에 대해 wait()함수를 호출했다고 가정했을때 해당 프로세스는 다른 스레드가 cv.signal()을 호출하기 전까지 대기 상태로 접어들게 된다.

 

 

 

모니터

공유 자원가 그 공유 자원을 다루는 함수(인터페이스)로 구성되 동기화 도구로, 상호 배제를 위한 동기화뿐만 아니라 실행 순서 제어를 위한 동기화까지 가능하다. 모니터의 동작원리는 단순하다. 프로세스 및 스레드는 공유 자원에 접근하기 위해 반드시 정해진 공유 자원 연산을 통해 모니터 내로 진입해야하고, 모니터안에 진입하여 실행되는 프로세스 및 스레드는 항상 하나여야한다. 이미 모니터 내로 진입하여 실행중인 프로세스 및 스레드가 있다면 큐에서 대기해야함

 

조건 변수를 함께 활용하면 실행 순서 제어를 위한 동기화도 구현할 수있다. 예를 들어 동시에 실행되는 프로세스 A,B중 반드시 A가 먼저 실행되고 다음으로 B가 실행되어야 한다는 조건일 경우 프로세스 B는 모니터 내에서 실행되기에 앞서 프로세스 A의 실행이 끝났는지를 검사한다. 프로세스 B가 모니터 내에서 실행되어도 괜찮은지를 검사하는 것이다. 프로세스 B가 프로세스 A보다 나중에 모니터 내로 진입했을 경우 프로세스 A가 먼저 실행되고 프로세스 B가 실행되어야 한다는 조건이 충족된 셈이므로 프로세스 B는 모니터 내로 진입하여 실행된다.

만약 프로세스 B가 프로세스 A보다 먼저 모니터 내로 진입했을 경우 이는 프로세스A가 먼저 실행되고 B가 실행되어야한다는 조건에 어긋난 경우이다. 따라서 이 경우에는 아래 그림과 같이 특정 조건 변수(cv)에 cv.wait()을 호출하여 프로세스 B를 대기 상태로 접어들게 할 수 있다.

 

프로세스 B가 조건 변수 cv에 대한 wait()을 호출하여 대기 상태로 접어들었다면 프로세스 B가 대기하고 있는 사이 프로세스 A가 모니터 내로 진입하여 실행될 수 있고, 실행 이후 cv.signal()을 호출하여 대기 상태에 있던 프로세스 B를 모니터 안으로 재진입시킬 수 있다. 결국 반드시 프로세스 A 그다음 프로세스 B의 순으로 실행되므로 실행 순서 제어를 위한 동기화가 이루어진다.

스레드 안전

멀티스레드 환경에서 어떤 변수나 함수, 객체에 동시 접근이 이루어져도 실행에 문제가 없는 상태를 의미한다. 레이스 컨디션이 발생했다면 이는 스레드 안전하지 안전하지 않은 상황인것이다. 반대로 어떤 함수가 스레드 안전하다면 이는 여러 스레드에 의해 호출되어도 레이스 컨디션이 발생하지 않는것을 의미한다.

 

교착 상태

일어나지 않을 사건을 기다리며 프로세스의 진행이 멈춰버리는 현상을 말한다. 

프로세스 A는 자원X를 점유한 채 프로세스 B가 점유하고 있는 자원Y의 사용이 끝나기를 기다리고, 프로세스B는 자원 Y를 점유한 채 프로세스 A가 점유한 자원X의 사용이 끝나기를 기다린다라고 했을 때 결국 두 프로세스는 서로가 가진 자원을 기다리다가 프로세스를 실행하지 못할 수 있다. 교착 상태가 발생한다는 것이다.

교착 상태(Deadlock) 정리

  • 프로세스들이 서로 자원을 기다리면서 무한정 대기하는 상황
  • 교착 상태가 발생하면 프로세스들이 더 이상 실행될 수 없음

 

교착 상태의 발생 조건

교착상태가 발생하는 상황에는 4가지 필요 조건이 있다.

하나라도 만족하지 않는다면 교착 상태는 발생하지 않고 4개의 조건이 모두 만족할 때 교착 상태가 발생할 가능성이 생긴다고 보면된다.

  1. 상호 배제
    교착 상태가 발생하는 근본적인 원인은 한 번에 하나의 프로세스만 해당 자원을 이용 가능했기 때문이다. 즉 한 프로세스가 사용하는 자원을 다른 프로세스가 사용할 수 없는 상호 배제의 상황에서 교착 상태가 발생할 수 있다.
  2. 점유와 대기
    한 프로세스가 어떤 자원을 할당받은 상태에서 다른 자원 할당받기를 기달니다면 교착 상태가 발생할 수 있다. 즉 점유와대기의 상황인것
  3. 비선점
    비선점 또한 교착상태 발생의 근본적 문제라고 할 수 있다. 어떤 프로세스도 다른 프로세스의 자원을 강제로 빼앗지 못하는 경우 교착 상태가 발생할 수 있다.
  4. 원형 대기
    프로세스와 프로세스가 요청한 자원이 원의 형태를 이루는 경우이다.  각각의 프로세스가 서로 점유한 자원을 할당받기 위해 원의 형태로 대기할 경우 교착 상태가 발생할 수 있다.
조건 설명
상호배제 한 번에 하나의 프로세스만 자원을 사용할 수 있음
점유와 대기 자원을 점유한 프로세스가 추가 자원을 요청하며 대기
비선점 할당된 자원을 강제로 빼앗을 수 없음
원형 대기 프로세스들이 원을 이루며 자원을 서로 기다림

교착 상태의 해결방법

운영체제는 애초에 교착 상태의 발생 조건에 부합하지 않도록 자원을 분배하는 방식으로 교착 상태를 예방할 수 있고, 교착 상태가 발생하지 않을 정도로 조금씩 자원을 할당하다가 교착 상태의 위험이 있을 때 자원을 할당하지 않는 방식으로 교착상태를 회피할 수도 있다.

자원을 제약없이 할당하다가 교착상태를 검출한 후 회복할수도 있다. 

 

  1. 교착상태 예방
    교착상태를 발생 시키는 4가지 필요 조건 중 하나를 충족하지 못하게 하는방법이다. 프로세스에 자원을 할당할 때 4가지 필요조건중 하나라도 만족하지 않으면 교착 상태는 발생하지 않는다. 가령 한 프로세스에 필요한 자원들을 몰아주고 그 다음에 다른 프로세스에 필요한 자원을 몰아주면 점유와 대기 조건을 만족하지 않으므로 교착 상태가 발생하지 않는다.
    또한 할당 가능한 모든 자원에 번호를 매기고 오름차순으로 할당하는 경우 원형 대기 조건을 만족하지 않으므로 교착상태가 발생하지 않는다.
  2. 교착상태 회피
    교착상태가 발생하지 않을 정도로만 조심하면서 자원을 할당하는 방법이다. 교착 상태 회피는 기본적으로 교착 상태를 한정된 자원의 무분별한 할당으로 인해 발생하는 문제로 간주한다. 프로세스에 할당할 수 있는 자원이 충분한 상황에서 프로세스들이 한두 개의 적은 자원만을 요구한다면 교착 상태는 발생하지 않겠지만, 자원이 한정된 상황에서 모든 프로세스들이 한 번에 많은 자원을 요구한다면 교착 상태가 발생할 위험이 증가하게 된다.
    은행원 알고리즘으로 사전에 회피할 수 있음.
  3. 교착 상태 검출 후 회복
    교착 상태 예방과 회피가 교착 상태 발생을 막기 위한 사전 조치였다면 교착 상태 검출 후 회복은 교착 상태의 발생을 인정하고 처리하는 사후 조치에 해당된다. 이 경우, 운영체제는 프로세스가 자원을 요구할 때마다 그때 그때 자원을 할당하고 주기적으로 교착 상태의 발생 여부를 검사한다. 그러다 교착 상태가 검출괴면 프로세스를 자원선점을 통해 회복시키거나 교착상태에 놓인 프로세스를 강제종료함으로써 회복시킬 수 있다. 자원 선점을 통한 회복이란 교착상태가 해결될때까지 다른 프로세스로부터 강제로 자원을 빼앗아 한 프로세스에 몰아서 할당하는 것을 의미한다

'코딩테스트' 카테고리의 다른 글

03. 배열 & 스택  (0) 2025.02.18
02. 코딩 테스트 필수 문법  (0) 2025.02.11
01. 알고리즘의 효율 분석  (0) 2025.01.21

최근댓글

최근글

skin by © 2024 ttuttak