안녕하세요! 피피아노입니다 🎵
이번 포스팅에서는 운영체제의 동기화에 대해서 정리해보려고 합니다.
운영체제에서 다중 프로세스와 스레드가 동시에 실행되는 환경에서는 자원 접근과 실행 순서를 제어하기 위한 동기화 메커니즘이 필수적입니다. 하지만 이러한 동기화를 고려하지 않으면 문제가 발생하게 되는데 이러한 내용을 중점으로 정리를 해보겠습니다.
동기화(Synchronization)이란?
우선 동기화에 대한 개념부터 살펴보자면 동기화는 여러 프로세스나 스레드가 공유 자원에 접근할 때 데이터의 일관성을 유지하고 실행 순서를 제어하는 메커니즘입니다. 다중 프로그래밍 환경에서는 여러 프로세스가 동시에 실행되기 때문에, 공유 자원에 대한 접근을 제어하지 않으면 데이터 불일치 문제가 발생할 수 있습니다.
동시다발적으로 실행되는 프로세스와 스레드는 실행 순서와 자원의 일관성을 보장해야 합니다.
운영체제가 제공하는 동기화의 의미
운영체제에서의 동기화의 의미란 크게 실행 순서 제어와 상호 배제를 이 2가지를 이야기합니다.
실행 순서 제어: 프로세스를 올바른 순서로 실행하기
상호 배제: 동시에 접근해서는 안 되는 자원에 하나만 접근하기
동시에 실행되는 프로세스 예시
동시에 실행되는 프로세스 2개를 가지고 예시를 들어서 설명을 해보겠습니다.
첫 번째 프로세스는 1만원을 입금하는 프로세스, 두 번째는 5만원을 입금하는 프로세스 이렇게 2가지의 프로세스가 있다고 가정을 해보겠습니다.
이 각각의 프로세스를 조금 더 자세히 살펴보면 아래처럼 정리할 수 있습니다.
1만원 입금 프로세스
- 잔액을 읽는다.
- 잔액에 1만원을 더한다.
- 더한 값을 잔액에 저장한다.
5만원 입금 프로세스
- 잔액을 읽는다.
- 잔액에 5만원을 더한다.
- 더한 값을 잔액에 저장한다.
이러한 상황에서 만약에 잔액이 10만원이 있는 상태라고 가정을 했을 때 해당 프로세스들이 실행이 된다면 일반적으로는 "1만원 더하고 5만원 더했으니까 내 잔액은 16만원이겠네?" 라고 생각하겠지만 동기화를 고려하지 않으면 이상한 결과가 나올 수 있습니다.
이 상황에 대해서 좀 더 자세히 살펴보겠습니다.

2개의 프로세스를 쭉 나열해서 표현을 해보면 이렇게 나타낼 수가 있는데 각각의 프로세스가 동기화를 고려 안 하고 자기 할 일만 하니까 1번 프로세스는 앞에서 값이 변동이 되는 말든 10만원에 1만원만 더해서 저장하고, 2번 프로세스도 앞에서 값을 변동해도 동기화가 안 되어 있으니까 그냥 자기가 읽어온 잔액(10만원)에 5만원을 더해서 저장하기 때문에 이런 일이 생기게 되는 겁니다.
그럼 이런 문제를 해결하려면 어떻게 해야 할까요? 먼저, 문제의 발생 원인을 분석해보면 동시에 접근해서는 안 되는 자원(공유자원)에 접근 했기 때문에 이런 일이 발생한 겁니다. 그렇다면 해결하려면 동시에 접근을 안 하면 되겠네?
즉, 다른 프로세스가 잔액이라고 하는 공동 데이터에 접근하고 있다면 나머지 프로세스는 기다려야 합니다. 이것을 상호 배제를 위한 동기화라고 합니다.
동기화가 이루어지지 않는 상황 코드로 살펴보기
자 이제 동기화가 이루어지지 않았을 경우 발생하는 문제를 코드로 한번 살펴보겠습니다. 저는 Swift로 코드를 제작해보았습니다.
import Foundation
var sum = 0
var producerDone = false
var consumerDone = false
func produce() {
for i in 0..<100000 {
sum += 1
}
producerDone = true
}
func consume() {
for i in 0..<100000 {
sum -= 1
}
consumerDone = true
}
func main() {
print("초기 합계: \(sum)")
let producerThread = Thread(block: produce)
let consumerThread = Thread(block: consume)
producerThread.start()
consumerThread.start()
while !producerDone || !consumerDone {
Thread.sleep(forTimeInterval: 0.01)
}
Thread.sleep(forTimeInterval: 0.1)
print("실행 이후 합계: \(sum)")
}
main()
코드를 살펴보면 sum은 생산자 스레드는 1씩 더하고, 소비자 스레드는 1씩 빼는 공유 변수입니다. producerDone과 consumerDone은 각각의 스레드가 작업을 마쳤는지 확인하는 플래그입니다.
produce() 함수는 sum에 100,000번 1을 더하고 작업이 끝나면 producerDone의 상태를 true로 변경합니다.
반대로 consume() 함수는 sum에서 100,000번 1을 빼고 작업이 끝나면 consumerDone의 상태를 true로 변경합니다.
main() 함수는 두 개의 스레드를 생성해서 동시에 실행하고 sum에 동시에 접근하고 수정하게 됩니다.
while문을 사용해서 두 스레드가 끝날 때까지 기다립니다.
코드만 봤을 때는 sum이라는 공유 변수에 100,000번씩 1을 더하고 1을 뺐으니까 0이 나오겠네? 라고 생각하실 수 있습니다. 하지만 막상 해당 코드를 실행해보면

이렇게 완전 뜬금없는 값이 나오는 것을 보실 수 있습니다. 심지어는 이 값이 일정하게 나타나는 게 아니라 실행을 할 때마다 매번 값이 달라지게 됩니다.

이렇게 말이죠.
이 문제가 서로의 스레드가 동기화가 되지 않았기 때문에 발생하는 문제입니다.
이런 식으로 동기화를 고려하지 않는다면 전혀 예상치 못한 그리고 균일하지 못한 결과값을 확인할 수 있을 것입니다. 또한 이런 것들을 고려하지 않는다면 디버깅도 힘들어질 거고 우리가 만드는 프로그램이 제대로 동작하지 않을 수도 있습니다. 그렇기 때문에 동기화는 꼭 고려해야 합니다.
감사합니다.
잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!
궁금하신 부분은 댓글로 질문 부탁드립니다!
'CS > 운영체제' 카테고리의 다른 글
[운영체제] CPU 스케줄링 알아보기 (18) | 2024.09.07 |
---|---|
[운영체제] 프로세스의 메모리 구조 (1) | 2023.05.17 |
[운영체제] 프로세스와 스레드 (0) | 2023.05.14 |
[운영체제] 메모리 (1) (0) | 2023.04.16 |
[운영체제] 메모리 및 성능향상 기법 (0) | 2023.03.31 |