프로세스는 실행중인 프로그램
으로 정의한다. 즉 실행되고 있는 각각의 프로그램이 모두 프로세스이다. 프로세스들은 각자 고유한 가상 메모리를 가지고 있기 때문에, 실제 물리 사용량과는 상관없이 프로세스 자신이 전체 메모리를 전부 가진 것처럼 작동하며, 이러한 착각을 지원하기 위해 가상 메모리 할당과 페이징 같은 기능들을 제공한다.
다른 프로세스와 통신하려면 프로세스 간 통신(IPC)을 사용해야 한다. 프로세스 간 통신은 프로세스들이 서로 통신하고 데이터를 교환하는 방법을 말한다.
프로세스를 생성 그리고 제거하는 일련의 사이클에서 OS는 가상 메모리 범위를 잡고, 프로세스를 세팅하고, 제거 시엔 가상 메모리를 다시 풀어주고 하는 과정에서 쓰레드보다 더 부담을 가지게 된다.
- 컨텍스트 스위칭(Context Switching) : 프로세스가 CPU를 사용하다가 다른 프로세스가 CPU를 사용해야 할 때, 현재 프로세스의 상태를 PCB에 저장하고, 다음 프로세스의 상태를 PCB에서 읽어와서 CPU를 넘겨주는 과정을 말한다.
스레드는 각각의 프로세스의 실행 흐름이다. 하나의 프로세스에서 여러 개 스레드를 만들 수 있다. 레지스터와 스택 메모리를 제외한 힙, 데이터 공간을 공유한다. 이로 인해 빠른 스레드 간 통신을 할 수 있으며, 컨텍스트 스위칭이 가볍고 빠르다. 스레드의 컨텍스트 스위칭 기법은 FCFS(First Come First Served), RR(Round Robin), SJF(Shortest Job First) 등의 스케줄링 알고리즘을 사용한다.
메모리를 공유하기 때문에 그에 따른 Data Race, DeadLock 문제를 피할 수 없어서 Lock이나 Atomic을 사용하여 잘 회피해야 한다. 스레드는 시간이 일정 이상 경과하거나, IO 인터럽트가 들어오거나, Sleep등의 코드가 실행되거나, 스레드 자신이 CPU 점유를 영보하거나 등의 이유로 스위칭 될 수 있다.
기본적으로 lock이란 락을 걸어놓고 들어간 해당 스레드를 제외한 어떤 스레드도 락이 걸린 구역으로 들어가지 못하는 것을 의미한다. 락을 걸어놓은 해당 스레드가 락을 릴리즈(해제)하고 해당 구역에서 나가면, 다른 스레드가 접근할 수 있게 된다.
- 쉽게 헬스장에서 A와 B가 서로 상대방의 기구가 끝나길 기다리고 있는 상황
- A는 프로세스 헬스장 기구를 리소스로 본다.
크리티컬 섹션 안의 명령어들도 스레드가 독접해야 할 자원으로 취급하기 때문이다. 즉, 그 해당 스레드에서 다른 스레드로 context switching이 될 수는 있지만, lock이 된 구역은 해당 스레드 외엔 누구도 진입하지 못하게 됨을 의미한다.
메모리를 공유하여 읽기/쓰기를 수행할 쓰레드들은 공유 메모리에 접근하기 전에 메모리에 대해 락을 시도해야 한다. 읽는 중에 데이터가 바뀌거나 하는 사태를 방지하기 위함이다. 다른 스레드가 이미 점유중일 경우, 새로 락을 시도하려던 스레드는 스레드는 점유중인 스레드가 락을 해제하거나, 타임아웃이 될 때 까지 대기해야 한다.
만약 A,B 자원을 돌 다 사용해야 하는 스레드 C, D 스레드 C가 A를 스레드 D가 B를 가지고 서로의 객체를 반납하기만을 기다리고 있다면 이러한 상태를 데드락이라 한다.
즉 데드락이란, 특정한 자원(메모리, 레지스터, CPU 연산장치 등)을 사용하는 스레드들이 서로의 자원을 반납하기만을 무한히 기다린다면, 이것을 데드락이라고 한다. (식사하는 철학자)
- 멀티스레드 환경에서는 매우 간단하게 발생할 수 있으며, 발생하면 해결하기 어려운 문제이다.
- 락을 구현하기 위한 방법으로는 뮤텍스, 세마포어, 모니터 등이 있다.
- 상호 배제(Mutual Exclusion) : 자원은 한 번에 한 프로세스(스레드)만 사용할 수 있다.
- 점유 대기(Hold and Wait) : 프로세스(스레드)가 자원을 가진 상태에서 다른 자원을 기다린다.
- 비선점(No Preemption) : 다른 프로세스(스레드)가 이미 점유한 자원을 강제로 뺏을 수 없다.
- 순환 대기(Circular Wait) : 프로세스(스레드)가 자원을 기다리는데, 그 자원을 기다리는 프로세스(스레드)가 다시 자원을 기다리는 상황이 발생한다.
- 예방(Prevention) : 데드락이 발생하지 않도록 하는 방법 (상호 배제, 점유 대기, 비선점, 순환 대기 조건 중 하나를 제거) but, 자원의 효율성이 떨어질 수 있다.
- 상호 배제 조건 제거: 여러 프로세스(스레드)가 동시에 자원을 사용할 수 있도록 한다. (매우 어렵고 현실적으로 어려움)
- 점유 대기 조건 제거: 프로세스(스레드)가 실행되기 전 필요한 모든 자원을 할당한다. (자원을 미리 할당받아야 하므로 자원의 낭비가 발생할 수 있다.)
- 비선점 조건 제거: 자원을 강제로 뺏을 수 있도록 한다. (비선점이 불가능한 자원이 존재할 수 있다. 작업 내용을 잃을 수 있음)
- 순환 대기 조건 제거: 자원에 고유한 번호를 할당하고, 번호 순서대로 자원을 요청하도록 한다. (자원의 낭비가 발생할 수 있다.)
- 회피(Avoidance) : 데드락이 발생할 가능성이 있는 상황을 피하는 방법 (은행원 알고리즘, 자원 할당 그래프 알고리즘)
- 탐지(Detection) : 데드락이 발생하면 탐지하여 데드락을 해결하는 방법 (자원 할당 그래프 알고리즘) (실제로 많이 사용)
- 회복(Recovery) : 데드락이 발생하면 복구하는 방법 (프로세스 종료, 자원 선점)
추가적으로 데드락 무시(Deadlock Ignorance) 방법도 있다. 데드락이 발생하면 시스템이 무시하고 무한정 기다리는 방법이다. 하지만 이 방법은 데드락이 발생하면 시스템이 멈추는 문제가 발생할 수 있어서 실제로 사용되지 않는다. (윈도우/리눅스)