💸딱 하루, 인프런 천원샵 오픈!

[인프런 워밍업 클럽 3기 - CS] - 2주차 발자국(운영체제)

[인프런 워밍업 클럽 3기 - CS] - 2주차 발자국(운영체제)

💻 운영체제

📌 프로세스 동기화

프로세스는 다른 프로세스와 데이터를 주고 받으며 통신한다.

여러 프로세스가 공유자원에 접근하여 데이터를 수정하고 읽을 경우, 동기화 문제가 발생할 수 있다.

공유 자원 : 프로세스 간 공유되는 변수나 파일

임계 구역: 여러 프로세스가 동시에 사용하면 안되는 구역

경쟁 조건: 공유 자원을 서로 사용하기 위해 경쟁하는 것

상호배제의 요구사항

  1. 임계 구역에는 하나의 프로세스만 접근할 수 있다

  2. 여러 요청에도 하나의 프로세스만 접근 가능하다

  3. 임계 구역에 들어간 프로세스는 최대한 빠르게 나와야 한다.

상호배제 메커니즘

  1. 세마포어

    1. 세마포어 변수를 갖고있는 프로세스가 먼저 실행되고 작업이 완료되면 signal()을 통해 변수 반환

    2. 세마포어 변수가 없는 프로세스는 대기 wait()

    3. 세마포어 변수를 할당받으면 프로세스 실행 가능

    4. 공유 자원 수에 따라 세마포어의 수 증가

  2. 모니터

    1. 운영체제의 차원이 아닌 프로그래밍 언어에서 처리

    2. 자바에서 synchronized가 붙은 함수가 실행되면 다른 프로세스가 접근 불가

    3. 함수를 임계 구역에 감싸지 않아도 되어 편리하게 구현 가능

 

📌 데드락

교착상태(데드락): 여러 프로세스가 서로 다른 프로세스의 작업이 끝나기를 기다리다가 아무도 작업을 진행하지 못하는 상태

필요조건

1. 상호배제: 한 프로세스가 리소스를 점유하였다면 다른 프로세스 접근 불가

2. 비선점: 점유중인 프로세스의 리소스를 뺏어갈 수 없음

3. 점유와 대기: 한 프로세스는 리소스를 점유하고 있는 상태에서 다른 프로세스의 리소스 점유를 대기해야 함

4. 원형대기: 점유와 대기를 하는 프로세스들의 관계가 원형이어야 함.

회피

교착상태는 발생할 수 밖에 없다 > 발생의 원인을 줄이거나 빠르게 해결

 

안정상태 (시스템 총 자원이 14라 가정)

image

  1. 현재 총 12개의 작업이 할당되었다.

  2. P1이 4개의 자원을 요청하면 사용 가능한 자원이 2(14-12)개 남아있기에 거절

  3. P2가 2개의 자원 요청하면 사용 가능한 자원 2개 할당

  4. P2의 작업이 마무리되면 사용 가능한 자원 6개로 증가

  5. 나머지 프로세스들의 요청 예상 자원 커버 가능

 

불안정상태

image

  1. 현재 사용 가능 자원은 1(14-13)개이다.

  2. 모든 프로세스들의 요청 예상 자원을 할당해줄 수 없다.

  3. 모든 프로세스들이 최대 자원을 요구하지 않는다면 교착 상태에 빠지지 않을 수 있지만

  4. 가능성이 높다

 

  1. 가벼운 교착상태 검출 : 타이머 이용. 프로세스가 일정 시간동안 작업을 진행하지 않는다면 교착상태로 검출

     

    1. 일정시간마다 체크포인트 생성, 교착상태 예상 시에 롤백

  2. 무거운 교착상태 검출: '자원 할당 그래프'를 통해 프로세스에 할당된 자원을 모니터링

    1. 교착상태를 일으킨 프로세스 강제종료

    2. 체크포인트로 롤백

       

 

📌 메모리

1. 레지스터: 가장 빠른 기억 저장소이자 휘발성 메모리. 메인 메모리 데이터를 CPU레지스터를 가져와 연산 후 계산 결과를 다시 메인 메모리에 저장

2. 캐시: 메인 메모리에서 필요할 것 같은 데이터를 미리 캐시에 저장

3. 메인메모리(RAM): 실제 운영체제와 프로세스들이 저장되는 휘발성 메모리

4. 보조저장장치: 비휘발성 메모리이며 프로그램, 파일을 저장

 

물리 주소(절대 주소): 메모리 공간의 실제 주소

논리 주소(상대 주소): 사용자가 다루는 메모리 주소

1. 사용자가 프로그램 실행 - 사용자 입장에서는 0x0000 메모리로 작업

2. 프로세스는 실제 물리주소 0x4000 주소에 저장

3. 사용자가 0x0100 주소의 데이터 요청 (논리 주소)

4. 논리 주소(0x0100)와 재배치 레지스터(실행 중인 프로세스의 물리주소 - 0x4000)을 더해서 0x4100의 값을 전달

 

메모리 할당 방식

유니 프로그래밍 환경에서는 하나의 프로세스만 메모리에서 동작이 가능하다. 만약 메모리의 용량을 초과한 프로그램을 실행시키려면?

스왑 과정이 존재하기 때문에 실제 메모리에 전체 프로세스가 올라가있는 것에 비하면 속도가 느리다.

메모리 오버레이: 실행시킬 프로세스를 분할시켜, 사용되는 부분을 메모리에 올리고 나머지는 하드디스크 스왑 영역에 저장

 

멀티 프로그래밍 환경에서는 어떻게 동작?

1. 가변 분할 방식(세그멘테이션): 프로세스가 크기에 따라 메모리를 분리

- 연속된 메모리 공간에 할당되기에 내부 단편화 현상 없음

- 외부 단편화 발생: 연속된 공간에 할당을 해야하니 5MB 프로그램을 3MB와 2MB 공간에 할당이 불가하다.

2. 고정 분할 방식(페이징): 프로세스 크기 상관없이 메모리 분리 (만약 5MB 프로그램을 2MB 고정 분할 메모리에 저장시키려면 2 / 2 / 1로 분리) -> 비연속 메모리 할당

- 구현이 간단하고 오버헤드가 적음

- 내부 단편화 발생: 위 예시에서 2MB 분할 공간에 1MB만 사용

3. 버디 시스템(가변 + 고정): 2의 승수로 메모리를 분할하여 할당

- 전체 메모리 영역을 하나의 프로세스가 올라갈 수 있을 정도로 2의 승수로 나눠서 분할

- 최소한의 내부 단편화, 간단한 메모리 합치기

 

조각 모음 : 외부 단편화에서 분리된 메모리 공간을 합치는 작업 - 실행 중인 프로세스를 일시적으로 멈춰야 해서 오버헤드 발생

 

📌 더 찾아본 점

  1. IPC(Interprocess Communication)란?

    1. 프로세스 간 협업을 위해 데이터를 공유하기 위해서는 IPC 기술이 필요하다

image 공유 메모리(Shared Memory): 공유 메모리 지역을 지정하고 프로세스들은 공유 메모리에 접근하여 데이터를 공유한다. (producer-consumer problem)

메세지 패싱(Message Passing): 협력하는 프로세스 간 메세지 전달을 통해 데이터 동기화(communication link)를 통해서 각 프로세스가 소통한다.

 

  1. race condition이란?

다수의 프로세스(혹은 쓰레드)가 같은 데이터를 동시에 접근하거나 처리하면, 실행되는 순서에 따라서 결과가 달라진다.

이를 해결하려면 특정 시간에 하나의 프로세스만 공유 자원을 다뤄야 한다. 즉, 프로세스는 동기적으로 실행되어야 한다.

1. 상호배제(Mutual Exclusion)을 보장해주어야 한다.

- 한 프로세스가 "임계영역(citical section)"을 실행 중일 때, 다른 프로세스는 임계 영역을 실행할 수 없다.

2. 데드락(deadlock)을 회피(진행)

- 임계 영역에 들어갈 프로세스를 정하는 건, 임계 영역에 들어가야하는 프로세스들만 참여할 수 있다. - 영역에 들어가는 과정이 무한정 지연되는 것을 방지

3. 유한 대기(Bounded Waiting) (starving 기아 상태 방지)

- 임계 영역에 들어가기를 요청한 프로세스는 무한정 기다리면 안된다.

 

  1. 상호배제 메커니즘

    1. Mutex Locks: 임계 영역에 진입하면 lockacquire()release()를 통해 주고 받음

    2. Semaphore: 공유자원 수용가능 수에 따라 정수형 변수 s 변수를 초기화. wait(s)signal(s)를 통해 각 작업에서 s값을 중가, 차감

 

  1. 자원할당 그래프(Resource0Allocation Graph)

운영체제에서 자원이 할당되어 있는 상태를 그래프로 그려, 시각적으로 데드락의 여부를 판단

- T = {T1, T2, ..., Tn}: 실행 중인 쓰레드

- R = {R1, R2, ..., Rm}: 할당될 자원 타입

- T_i → R_j: i 쓰레드가 j 자원을 요청한다

- R_j → T_i: j 자원을 i 쓰레드에 할당되어 있다.

image위 예제에서는 3개의 쓰레드와 4개의 자원이 있으며 2개의 순환 구조를 갖고 있다.

T1 → R1 → T2 → R3 → T3 → R2 → T1

T2 → R3 → T3 → R2 → T2

모든 쓰레드는 하나의 자원을 점유 중인 상태에서 다음 자원을 점유하기 위해 대기 상태에 있고, 이러한 상태가 원형을 이루어지고 있기 때문에 데드락에 빠질 확률이 높다고 판단한다.

 

  1. 메모리 주소 바인딩

1. symbolic address: 소스 프로그램에 변수와 같이 메모리 주소를 상징적으로 저장

2. relocatable address: 실제 메모리 주소가 결정될 수 있는 재배치 가능 주소

3. absolute address: 컴파일 타임에 결정되는 메모리 주소

 

image

1. compile time: 컴파일 시점에 물리 주소가 결정 (`absolute address`로 결정)

2. load time: 컴파일 시점에 메모리 위치가 확정나지 않았다면, relocatable code로 변환. 프로세스가 로드되어 메모리에 올라갈 때 물리 주소가 결정

3. execution time: 프로그램 실행 도중에 메모리 주소가 변경될 수 있다면 런타임까지 바인딩 지연. MMU에 의해서 논리 주소를 물리 주소 결정

 

📌 백엔드 면접 질문해보기

  1. 데드락과 라이브락의 차이점?

데드락은 두 개 이상의 쓰레드가 자원을 점유하고 있지만 다른 자원을 점유하기 위해 대기 중인 교착 상태를 의미합니다. 라이브락이란 두 개 이상의 쓰레드가 충돌을 회피하기 위해 실패 작업을 반복하지만 진전이 없는 상태를 의미합니다.

데드락은 교착 상태에서 모든 프로세스가 멈춰버리지만 라이브락은 실패 작업을 계속 시도하기 때문에 멈추지는 않습니다.

 

  1. 락 기반 동기화와 락 프리 알고리즘이란?

락 기반 동기화란 하나의 프로세스만 공유자원을 사용하도록 강제하여 동기화시키는 방법입니다. 뮤텍스나 세마포어가 락 기반 동기화에 해당됩니다. 락 기반 기법은 두 개 이상의 스레드가 서로 상대방의 락의 해제를 기다린다면 데드락이 발생할 수 있으며, 낮은 우선순위의 스레드가 자원을 먼저 점유하면 높은 우선순위의 스레드가 대기를 해야하는 우선순위 역전 등의 문제가 있습니다.

락 프리 알고리즘이란 락을 사용하지 않고도 동기화를 유지시킬 수 있는 알고리즘입니다. 해당 알고리즘은 CAS(Compare And Swap) 같은 원자적 연산을 통해 동기화를 보장하는데, 원자적 연산이란 연산이 중간에 중단없이 시작되지 않거나 완전히 수행되는 것(all or nothing)을 의미하며 CAS는 해당 원자적 연산을 기반으로 현재값을 읽고 예상했던 값과 일치할 경우에만 값을 변경하며 일치하지 않는다면(다른 쓰레드의 개입) 업데이트하지 않는 방식입니다.

 

  1. Node.js에서 데드락이 발생할 수 있을까?

     

    공식 문서에 따르면 Node.js에는 락이 존재하지 않아서 데드락이 발생될 확률이 매우 적다고 적혀있습니다. 다만 libuv에서 제공하는 동기적인 메서드를 실행할 때, js파일에 대해 대기 상태가 발생할 수 있는데 이는 데드락이라기 보다는 Blocking에 해당합니다. 또한 이러한 동기 I/O 메서드에 대해서도 콜백을 포함한 비동기 처리 함수가 함께 제공됩니다. (readFileSync / readFile)

 

  1. V8 엔진 메모리 관리 방식은?

     

    V8 엔진은 node.js의 실행 엔진이며 컴파일과 GC를 포함하고 있다.

    V8 메모리 구조(Resident set)은 크게 HeapStack 메모리로 구분된다.

Heap에는 크게 New Space와 Old Space가 존재하며 New Space는 짧은 생명주기를 갖는 새로 생성된 객체들이 저장되며 2번의 minor GC에도 제거되지 않은 객체는 Old Space로 이동되어 저장된다. New Space는 minor GC, Old Space는 Major GC로 참조되지 않는 변수들을 가비지 컬렉팅 한다.

 

  1. Buffer와 Stream의 메모리 사용 방식 차이?

     

    Buffer: 데이터를 조각(chunk)내어 buffer에 다 차우면(buffering) 일괄로 데이터를 전송. - 메모리의 사용 비율이 크지만 데이터 처리 속도가 크게 향상

    Stream: 데이터 청크와 버퍼의 크기를 작게하여 지속적으로 데이터를 전달하는 방식 - 메모리 사용이 적지만 데이터 실시간 효율성이 높아짐

 

📔 회고

🚀 최종 목표 : 더 효율적인 백엔드 개발을 위해 기본적인 운영체제 지식들을 확실히 잡아가기

🚀 매주 규칙:

  1. 각 섹션마다 하나의 .md 파일을 생성하고, 섹션 내 각 유닛은 헤더로 구분

  2. 강의를 듣고 최대한 이쁘게 (?) 정리해놓기

     

  3. 매 강의를 듣고 궁금하거나 이해가 안가는 부분은 추가적으로 더 찾아서 정리해두기

  4. 매 강의 내용을 백엔드 관점에서 고민해보고 GPT와 대화를 통해 정리

첫 주차 발자국과 미션을 수행한 후에 이번 주에는 더 구체적이고 집중적으로 강의를 듣고 복습할 수 있게 방식을 수정하였다.

규칙 3번에서 매 강의를 듣고 궁금하거나 이해가 안가는 부분에 대해서는 보통 검색을 통해 블로그를 찾아보고 정리하였었다. 하지만 블로그에 적혀있는 글마다 내용이 다르기도 하고 궁금증이 해소되지 않는 부분들이 있어, 더 찾아보던 도중 CS로 유명한 공룡책(Operating Systerm Concepts) 무료 영문 pdf파일을 발견하였고 그 날 강의에 해당하는 주제에 맞춰 공룡책을 공부하고 개념을 탄탄히 하는 것에 집중하였다. 추가적으로 인프런 강의 중에 무료로 공룡책에 대한 내용을 설명해주는 강의도 있어 함께 공부를 하니 더 깊이 있고 쉽게 개념을 잡아갈 수 있었던 것 같다.

규칙 4번에서 처음에는 백엔드 관점으로 강의 내용을 살펴보려 하였는데, 생각보다 모호하고 막연하다고 생각하여 규칙을 조금 변경하였다. GPT에는 Node.js 전문가 프롬프트가 제공되어 있어서, 해당 프롬프트를 이용하여 오늘 배운 내용에 대해 이야기하고 백엔드 면접 질문을 리스트로 달라고 요청하면 기초부터 고급 그리고 node.js를 활용한 심화 질문까지 리스트업해준다. 해당 부분에 대해서 스스로 답변해보고 답변하지 못한 부분이나 설명이 부족한 부분은 다시 AI와 대화하면서 지식을 채울 수 있었던 것 같다.

결과적으로 3. 매 강의 듣고 해당 내용 공룡책 & 강의 1회 정독 및 수강 / 4. 매 강의에 대한 내용 백엔드 면접 질문을 추려서 답변하고 개념 구체화하기 이렇게 수정하여 2주차를 진행하였다. 기존 독학으로 CS를 공부하였을 때는, 그 범위가 너무 넓어 어떤 부분을 공부하고 있는지도, 어디까지 공부해야할 지도 전혀 감을 잡을 수 없었는데 이렇게 강의를 수강하고 그 강의의 깊이있는 개념까지 찾아가며 공부하다보니 확실히 체계가 잡히고 개념도 탄탄히 공부해 볼 수 있는 것 같다. 해당 방식으로 공부하면 딱 몰입할 수 있을만큼 적당히 공부하는 것 같아서 다음 주에는 추가적인 규칙을 넣기보다 기존 규칙을 유지해보려고 한다.

 

댓글을 작성해보세요.


채널톡 아이콘