운영체제는 프로세스와 프로세스끼리 쉽게 데이터를 주고 받을 수 있는 통신 방법을 제공하는데 이를 프로세스 간 통신(IPC; Inter Process Communication) 이라고 한다.
통신을 단순하게 보면 데이터를 주거나(send) 받는(receive) 것이다. 데이터를 보내는 프로세스는 send(message)를 사용하고 데이터를 받는 쪽은 receive(message)를 사용한다.
공유 메모리 문제점은 상대방이 데이터를 언제 보낼지 받는 쪽은 모른다. 받는 쪽에서는 공유 메모리를 반복적으로 점검해야 한다. 이를 바쁜 대기(busy waiting) 이라고 한다.(상태 변화를 살펴보기 위해 반복문을 무한 실행하며 기다리는 것)
프로세스 간 통신은 동기화 기능이 있느냐 없느냐의 따라 대기가 있는 통신(bloking communication)과 대기가 없는 통신(non-blocking communication) 으로 구분한다.
대기가 있는 통신을 동기화 통신(synchronous communication), 대기가 없는 통신을 비동기화 통신(asynchronous communication) 이라고 한다.
종류 | 통신 기법 | 통신 기기 |
---|---|---|
대기가 있는 통신(동기화 통신) | 파이프, 소켓 | 전화 |
대기가 없는 통신(비동기화 통신) | 공유메모리, 공유 파일 | 전보 |
open("com.txt", O_RDWR)
write(fd, "Test", 5)
read(fd, buf, 5)
close(fd)
: fd가 가리키는 파일, 즉 com.txt 파일을 닫는다.읽기 쓰기는 하드디스크에 데이터 전송 데이터 가져오기 명령이라 할 수 있으므로 파일 입출력도 통신(운영체제 입장에서는 일반 프로세스와 입출력 프로세스 간의 통신)이다.
파일을 이용한 통신은 부모-자식 프로세스 간 통신에 많이 사용되며 운영체제가 프로세스 동기화를 제공하지 않는다. 그래서 프로세스가 알아서 동기화해야 하는데 주로 부모 프로세스가 wait() 함수를 이용하여 자식 프로세스의 작업이 끝날 때 까지 기다렸다가 작업을 시작한다.
파이프는 운영체제가 제공하는 동기화 통신 방식으로 파일 입출력과 같이 open()
함수로 기술자를 얻어 작업한 후 close()
함수로 마무리한다.
파이프는 단방향 통신으로 양방향 통신을 하려면 파이프를 2개를 사용해야 한다.
이름 없는 파이프: 부모와 자식 프로세스 혹은 같은 부모를 가진 자식 프로세스와 같이 서로 관련 있는 프로세스 간 통신에 사용
이름 있는 파이프: FIFO라 불리는 특수 파일을 이용하며 서로 관련 없는 프로세스 간 통신에 사용된다.
공유자원(shared resource)은 여러 프로세스가 공동으로 이용하는 변수, 메모리, 파일등을 말한다. 공유 자원은 공동으로 이용되기 때문에 누가 언제 데이터를 읽거나 쓰느냐에 따라 그 결과가 달라질 수 있다. 2개 이상의 프로세스가 공유 자원을 병행해서 읽거나 쓰는 상황을 경쟁 조건(race condition)이 발생했다고 한다.
공유 자원 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역을 임계구역(critical section)이라고 한다. 임계구역에서는 프로세스들이 동시에 작업하면 안 된다.
임계구역과 전통적인 문제로 생산자-소비자 문제(producer-consumer problem)가 있다. 이 문제에서는 생산자 프로세스와 소비자 프로세스가 서로 독립적으로 작업한다.
임계구역 문제를 해결하는 단순한 방법은 잠금 lock을 이용하는 것이다.
프로세스 P1과 P2는 임계구역에 진입하기 전에 임계구역에 잠금이 걸려있는지 확인(lock == true)
잠겨있는 경우 잠금이 해제될 때 까지 무한 루프를 돌면서 대기[while(lock == true);]
임계구역에 있는 프로세스가 작업을 마치고 잠금을 해제하면(lock = false;) 루프에서 빠져나와 작업
새로 임계구역에 진입하는 프로세스는 잠금을 걸고(lock = true;) 작업
작업을 마치면 다른 프로세스가 사용할 수 있도록 잠금 해제(lock = false;)
프로세스 P1이 while(lock == true)문을 통과하고 lock=true 만나기 전에 주어지 CPU 시간이 다 써서(타임아웃) 준비 상태로 돌아가고 문맥교환이 발생하고 P2가 실행상태로 바뀌면 P2는 lock이 true이기 때문에 P2 또한 임계구역에 진입할 수 있는 문제가 생긴다.
프로세스 P1과 P2 모두 lock1 = true, lock2 = true 문을 실행한 후 타임 아웃이 걸리면 둘 다 무한 루프에 빠지게 된다. 이를 교착 상태(deadlock)라고 한다. 교착상태는 프로세스가 살아 있으나 작업을 진행되지 못하는 상태다.
위에 코드는 프로세스가 3개라면 lock 도 3개로 늘어나기 때문에 확장성 문제도 가지고 있다.
위에 코드는 잠금을 확인하는 문장이 하나 이므로 상호 배제와 한정 대기를 보장한다. 하지만 한 프로세스가 두 번 연달아 임계구역을 사용하지 못하고 서로 번갈아가면서 실행되는 문제가 있다. 이렇게 프로세스의 진행이 다른 프로세스의 로 인해 방해받는 현상을 경직된 동기화(lockstep synchronization)라고 한다.
상호 배제 문제에서 하드웨어로 두 명령어를 동시에 실행하면 임계구역 문제를 쉽게 해결할 수 있다.
왼쪽 코드의 1~2행은 오른쪽 코드의 1행과 같다. 이는 검사와 지정(test-and-set) 이라는 코드로 하드웨어의 지원을 받아 while(lock==true) 문과 lock=true 문을 한꺼번에 실행한 것이다.
임계구역을 하드웨어로 사용하면 바쁜 대기를 사용하여 검사하기 때문에 자원 낭비가 발생한다.
피터슨 알고리즘은 turn이라는 변수를 하나 사용해서 두 프로세스가 lock을 설정하여 임계구역을 못 들어가는 상황을 대비하기 위한 장치다.
피터슨 알고리즘은 세가지 조건을 모두 만족하지만 2개의 프로세스만 사용 가능하다는 단점이 있다.
테커 알고리즘은 세가지 조건을 모두 만족하지만 너무 복잡하다.
세마포어의 P()나 V() 내부 코드가 실행되는 도중에 다른 코드가 실행되면 상호 배제와 한정 대기 조건을 보장하지 못한다. 그러므로 P()와 V()의 내부 코드는 검사와 지정을 사용하여 완전히 실행되도록 구현된다.
모니터는 공유 자원을 내부적으로 숨기고 공유 자원에 접근하기 위한 인터페이스만 제공함으로써 자원을 보호하고 프로세스 간에 동기화시킨다.
모니터는 시스템 호출과 같은 개념이다.
1. 임계구역으로 지정된 변수나 자원에 접근하고자 하는 프로세스는 직접P()나 V()를 사용하지 않고 모니터에 작업 요청을 한다.
2. 모니터는 요청받은 작업을 모니터 큐에 저장한 후 순서대로 처리하고 그 결과만 해당 프로세스에 알려준다.