sol 개발 블로그 로고
Published on

[ASAC 스터디] 운영체제 1

Authors
  • avatar
    Name
    Chan Sol OH
    Twitter

목차

ASAC CS 면접 준비 스터디를 하면서 정리하는 글입니다.

프로세스와 스레드의 차이를 설명해보세요.

  • 프로세스와 스레드의 차이 [참고]
프로세스는 Ready 상태에서 dispatch되어 CPU가 스케줄링하는 프로그램입니다.
프로세스는 자녀 프로세스를 생성할 수 있고, 다른 프로세스와 메모리를 공유하지 않아서 격리되어 있습니다.
또한 프로세스는 5가지 상태를 가질 수 있습니다.
New (create), Ready, Run, Blocked or Wait, 그리고 Terminated or Completed

스레드는 프로세스의 한 부분입니다. 따라서 한 프로세스 안에 여러 스레드가 있을 수 있습니다.
스레드는 code, data, 그리고 자원을 다른 스레드와 공유합니다.
스레드는 5가지 상태를 가질 수 있습니다. New, Running, Waiting, TimeWaiting and Terminated

----차이점----

스레드는 생성과 제거에 있어서 프로세스 보다 더 적은 시간이 걸립니다. 다만, 프로세스와 달리 스레드는 격리되지 못합니다.

CPU 스케쥴링에 따라 실행 중인 프로그램이 프로세스기 때문에 멀티 프로그래밍은 멀티 프로세스와 같은 말이다.
다만,프로그램(프로세스) 안에 멀티 스레드가 있을 수 있다.

 프로세스가 blocked해도 다른 프로세스 상태에 영향을 주지 않습니다.
user-level 스레드 같은 경우 한 스레드가 I/O로 인한 blocked가 전체 스레드를 blocked하게 합니다.
다만 kernel-level 스레드는 user-level 스레드처럼 전파되지 않습니다.

마지막으로 프로세스는 고유의 Process Control Block(PCB), Stack, Address 공간을 가집니다.
스레드는 부모의 PCB, Thread Control Block, Stack, 그리고 공통 Address 공간을 갑니다.
  • 스레드끼리 공유하는 자원과 공유하지 않는 자원은 어떤게 있을까요? [답]
스레드는 보통 lightweight process라고 불립니다.

프로세스의 메모리 맵은 다음과 같이 5개의 요소로 나눌 수 있습니다.
stack : 프로세스는 stack 영역을 나누고 각각을 스레드에게 제공합니다.
  호출된 함수의 인자, 지역 변수, return memory address을 저장합니다.
  스레드가 작업을 완료하면 해당 스레드의 스택 영역이 프로세스에 의해 회수됩니다.- 독립적
heap : 동적으로 할당된 변수를 저장하기 위해 예약됩니다.
  이는 프로세스의 가상 address 공간 상에서 스택 영역 반대에 위치해 있습니다. - 공유
text (code) : 실행할 명령어를 저장합니다. - 공유
initialized data : 초기화된 전역 또는 정적 변수를 저장합니다. - 공유
uninitialized data : 초기화되지 않는 전역 또는 정적 변수를 저장합니다. - 공유

스레드는 code(text), data(initialized, uninitialized data), 그리고 file 영역은 공유하고,
stack pointer, instruction pointer, stack, 그리고 registers는 독립적입니다.

멀티스레드를 하면 이점은 OS가 새로운 스레드를 생성할 때 새로운 메모리 맵을 만들지 않고,
이미 존재하는 프로세스의 메모리 맵을 스레드가 사용할 수 있다.
다만, 멀티스레드가 공유하는 자원을 안전한 방법으로 일관된 상태를 유지해야합니다.
  • 프로세스끼리 어떻게 소통할 수 있을까요? [참고]
프로세스끼리 소통하는 것은 interprocess communication(IPC)이라고 합니다.
IPC는 동기나 비동기식으로 프로세스들 간 데이터를 공유하기 위해 OS가 제공하는 메커니즘입니다.

IPC를 사용하는 애플리케이션은 클라이언트나 서버로 불립니다.
클라이언트는 데이터를 요청하고, 서버는 클라이언트 요청에 따른 데이터를 응답합니다.

IPC는 커널이 제공하는 기능의 수를 줄이는 microkernels나 nanokernels의 설계 프로세스에서 중요합니다.
필요한 기능은 서버에게 요청해서 얻을 수 있기 때문입니다. 다만, 일반 monolithic kernel에 비해서 통신량이 크게 증가합니다.

대부분의 OS에서 제공하는 IPC 기술은 다음과 같습니다.
File : 디스크나 파일 서버에 저장된 파일에 여러 프로세스가 접근하는 방식입니다.
Socket : 네트워크 인터페이스를 통해 컴퓨터 내,외부 프로세스에 데이터를 전달하는 방식입니다.
  보통 TCP를 사용하지만, 드물게 메세지 지향인 UDPSCTP를 사용합니다.
  TCP를 사용할 경우 message boundary를 보존하지 않기 때문에 수신자는 boundary를 확인하기 위해 별도의 방법을 사용해야합니다.
Message Queue : Socket과 비슷하지만 message boundary를 보존하는 데이터 스트림입니다.
  여러 프로세스가 큐에 읽기와 쓰기를 할 수 있습니다. 이렇게 함으로써 클라이언트와 서버가 직접 연결하지 않고 통신할 수 있게됩니다.
Shared memory : 여러 프로세스가 공유할 수 있는 메모리 블럭에 접근하는 방식입니다.
  이를 통해 프로세스가 통신할 수 있고, 불필요한 복사본을 생성하지 않습니다.

컨텍스트 스위칭에 대해 설명해보세요.

Context switching은 CPU를 사용하여 프로세스를 한 상태에서 다른 상태로 변경하는 데 사용하는 과정을 말합니다.

OS 상에서 여러 프로세스가 PCB에 저장되어 있을 때, CPU가 현재 실행 중인 프로세스의 state를 저장하고,
다른 프로세스의 state를 불러오는 작업을 context switch라고 합니다.
context switch가 일어나면 kernel은 기존 프로세스의 PCB에 context를 저장하고, 새 프로세스의 context를 불러옵니다.

Context switch에 걸리는 시간은 시스템이 아무것도 하지 못하는 불필요한 시간입니다.

그러나 context switching이 필요한 이유는 다음과 같습니다.
쉬운 스위칭 : 시스템 상에서 한 프로세스가 바로 다른 프로세스로 전환될 수 없습니다.
  Context switching을 통해 OSCPU의 자원을 사용해서 여러 프로세스를 스위칭하는 작업을 쉽게할 수 있습니다.
자원의 유한성 : Context switching을 사용하면 모든 프로세스가 하나의 CPU를 사용해서 실행할 수 있으며,
  시스템 상에 state를 저장할 수 있습니다.
병렬성 : Context switch은 추가적인 프로세서 없이 한 CPU가 여러 프로세스를 병렬적으로 처리할 수 있습니다.

동기와 비동기의 차이, 블로킹과 넌블로킹의 차이 그리고 장단점에 대해 설명해보세요.

Blocking은 애플리케이션이 I/O 작업을 호출했을 때, 그 작업이 다 끝날 때까지 다른 작업을 하지 않는 것을 의미합니다.

Non-Blocking은 Blocking과 반대로 애플리케이션이 I/O 작업을 호출했을 때, 다른 작업을 할 수 있는 것을 의미합니다.
Non-Blocking을 위해선 동시에 여러 작업을 할 수 있어야하기 때문에 멀티 테스킹을 할 수 있어야합니다.

동기 (Synchronous) I/OI/O 작업이 끝날 때까지 애플리케이션이 기다리는 것을 의미합니다.
비동기 (Asynchronous) I/O는 백그라운드에서 작업을 진행하고 애플리케이션은 그 결과에 대해서 관심이 없습니다.
만약 I/O 작업이 끝나고 추가적인 작업을 원한다면 callback 함수를 실행하도록 합니다.

보통 I/O 작업은 애플리케이션 프로세싱 보다 긴 시간이 필요하기 때문에 비동기적으로 실행하면 더 높은 성능을 보여줍니다.
하지만, Non-Blocking이나 Asynchronous로 애플리케이션과 I/O 작업을 동시에 진행하면
메모리 상에 스리드끼리 공유할 수 있는 heap 영역이나 data 영역에 대한 스레드 안전성을 보장해야합니다.

멀티스레드 프로그래밍에 대해 설명해보세요.

[참고]

스레드의 모음은 프로세스입니다. 만약 계산기 프로그램이 싱글스레드라면 화면을 보여주는 작업과 게산하는 작업은 동시에 할 수 없을 것입니다.
이처럼 멀티 태스킹을 수행하기 위해선 멀티 프로세스나 멀티 스레드가 필요합니다.

스레드는 생성될 때 프로세스의 메모리 맵을 사용할 수 있어서 프로세스에 비해 시작하고 끝내는데 시간이 짧습니다.
 멀티 프로세스는 OS가 작업을 관리하지만, 멀티 스레드는 프로그래머가 제어할 수 있습니다.
그렇기에 멀티 프로세스 보다 멀티 스레드가 더 자주 사용됩니다.

스레드는 5가지 상태를 가질 수 있습니다.
New : 스레드가 생성되는 시점입니다.
Runnable : 스레드가 주어진 작업을 실행하는 시점입니다.
Waiting : 다른 스레드의 작업을 기다리고 있는 시점입니다. 다른 스레드가 작업을 끝냈다는 신호를 주면 다시 Runnable 상태로 돌아갑니다.
Timed Waiting : runnable 스레드가 구체적인 시간 동안 멈추는 시점입니다. 시간이 다 되거나 이벤트가 발생하면 다시 Runnable 상태로 돌아갑니다.
Terminated (Dead) : 스레드가 작업을 끝내고 제거되는 시점입니다.

Thread-safe 하다는 의미와 설계하는 법을 설명해보세요.

[참고]

멀티스레딩이라는 것은 분리된 스레드가 동시에 작업을 실행하는 것을 의미합니다.
하지만 스레드끼리는 heap 영역, data 영역 같이 공유할 수 있는 자원이 있습니다.
이런 공유 자원을 다양한 스레드가 자유롭게 접근하고 업데이트하면, 그 결과는 예측할 수 없게 됩니다. 따라서 Thread-safe한 설계가 필요합니다.

무상태성 : 대부분의 경우에서 멀티스레딩의 에러는 부정확한 상태 공유 때문에 발생합니다.
  그렇다면 스레드가 실행하는 작업을 무상태로 만든다면 해결 할 수 있습니다.
  , 스레드 외부에 어떤 상태도 참조하지 않고 상태를 저장하지 않고 오직 입력된 인자에 의해서 작업의 결과가 결정되도록 하는 것입니다.
불변성 : 다른 스레드 간 다른 상태를 저장할 필요가 있을 때, 스레드가 가지고 있는 상태를 불변하게 만든다면, Thread-safe하게 만들 수 있습니다.
Thread-Local Field : 스레드의 작업 중 변할 필요가 있는 객체가 있을 수 있습니다.  경우 스레드 내부에서만 사용하는 필드를 사용할 수 있습니다.
  Thread-Local인 필드의 객체는 오직 스레드 내부에서만 사용되며 절대로 다른 스레드와 공유하면 안됩니다.
  만약 스레드 외부로 객체를 공개할 필요가 있다면, 객체를 복사한 후 공개해야합니다.
Concurrent Collections : java 같은 경우 Concurrent Collections를 사용할 수 있습니다.
  Concurrent Collections는 내부 데이터를 segments로 쪼개고 각 스레드에게 접근할 수 있도록하기 때문에 Thread-safe하고 성능이 좋습니다.
Atomic Objects : java는 atomic class를 제공합니다.
  atomic class는 thread-safe하고 클래스 내부에 변수가 하나 있을 때 유용합니다.
Synchronized : java는 Synchronized 키워드를 이용해 메서드나 블럭을 thread-safe하게 만들 수 있습니다.
  Synchronized 키워드가 붙은 코드는 오직 한 스레드만 실행시킬 수 있습니다.
ReentrantLock : Synchronized 키워드를 사용하면 스레드가 락을 얻기 위해 무한히 대기해야합니다.
  ReentrantLock 락은 가장 오래 기다린 스레드에게 락을 제공하는 공정한 락을 사용할 수 있습니다.
  이를 통해 계속 락을 얻지 못하고 기다리는 기아 상태를 방지합니다.

프로세스 동기화에 대해 설명해보세요.

[참고]

프로세스 동기화란 멀티 프로세스 상황에서 다양한 프로세스가 동시에 접근할 수 있는 자원을 예측 가능한 방법으로 공유하는 것을 의미합니다.
프세스 동기화의 주요한 관점은 서로 다른 스레드끼리 간섭하지 않는 것과 동시 접근 때문에 부정확한 데이터를 읽는 것을 방지하는 겁니다.

멀티 프로세스에서 동기화는 데이터 일관성과 무결성 그리고 데드락 같은 동기화 문제를 피하는 겁니다.
이러한 동기화 전략을 위해 semaphore, monitors, 그리고 critical section이 사용됩니다.

동기화 문제로 race condition으로 인해 잘 못된 값이 공유 변수에 저장될 수 있고,
critical section으로 인해 활동성 장애가 일어날 수 있습니다.

Race condition은 여러 프로세스가 공유 코드, 공유 메모리, 또는 공유 변수에 접근할 때,
각 프로세스들이 경쟁적으로 critical section에 접근해서 잘못된 결과물을 저장하는 것을 말합니다.
Race condition을 해결하는 방법은 공유 변수를 조작하는 부분인 critical section에 atomic 변수를 사용하거나 lock을 거는 방법이 있습니다.

Critical section은 공유 변수를 조작하는 작업이 있는 코드 블럭이고,
공유 변수를 일관되게 유지하기 위해 몇가지 조건을 충족해야합니다.
상호 배제(Mutual Exclusion) 조건은 오직 한 스레드만 critical section에 코드를 실행시킬 수 있습니다.
진행 (Progress) 조건은 critical section에 진행 중인 프로세스가 없을 때, 대기 중인 프로세스가 무한히 대기하지 않도록 해야합니다.
예를 들어 프로세스가 critical section에서 나갈 때, critical section에 들어올 다음 프로세스를 선택하는 방식으로 어떻게든 다음 프로세스를 들어오게 만들어야합니다.
한정된 대기 (Bounded Waiting) 조건은 한 프로세스가 너무 오래 기다리면 안된다는 것을 의미합니다.
 한 프로세스가 critical section에 들어가도록 요청했을 때, 일정한 횟수만큼 다른 프로세스가 들어갈 수 있다는 것을 의미합니다.

Semaphores는 여러 스레드가 정수값을 공유하는 시그널링 메커니즘입니다.
공유된 정수 값이 0이나 일정 수 이하가 될 때 대기중인 스레드가 semaphore를 얻을 수 있는 방식입니다.
semaphore는 바이너리 semaphore와 카운팅 semaphore가 있습니다.
바이너리 semaphore는 0 또는 1의 상태를 가질 수 있는데, 초기 모든 프로세스는 1로 초기화된 mutex semaphore를 공유합니다.
그리고 프로세스는 lock이 0이 될 때까지 대기합니다. 그리고 프로세스는 lock을 1로 만들고, critical section에 들어갑니다.
카운팅 semaphore는 보통 제한된 자원을 여러 프로세스끼리 공유할 때 사용됩니다.
lock이 0 이상이 될 때까지 프로세스가 대기하게 되고, critical section에 들어갈 때는 lock에 1을 빼고, 나갈 때는 1을 더합니다.

semaphore는 race condition을 방지할 수 있고, 자원을 잘 분배할 수 있지만, 잘 못 구현될 경우 데드락이 일어날 수 있습니다.

교착상태와 기아상태의 해결방법에 대해 설명해보세요.

[참고]

데드락은 한 스레드가 자원에 락을 걸고 다른 자원의 락을 얻으려고 기다릴 때,
다른 스레드가 그 락을 걸고 있고, 이미 락이 걸린 자원을 얻으려고 대기해서 두 스레드가 아무건 작업도 하지 못하고 대기만 하는 상태를 의미합니다.

기아 상태는 우선순위가 높은 프로세스는 계속 락을 얻지만, 우선순위가 낮은 프로세스는 계속 락을 얻지 못하고 대기만하는 상태를 의미합니다.

세마포어와 뮤텍스의 차이에 대해 설명해보세요.

[참고]

뮤텍스와 세마포어는 커널에서 동기화를 위해 제공하는 서비스입니다.
뮤텍스는 락킹 메커니즘을 이용하는 바이너리 세마포어의 한 종류입니다. 보통 코드의 상호 배제적인 부분을 한정할 때 사용합니다.
뮤텍스는 race condition을 방지하고 데이터를 일관되게 유지시킬 수 있습니다.
하지만, critical section이나 변수를 오직 한 스레드만 사용할 수 있기 때문에 활동성 장애가 발생할 수 있습니다.

세마포어는 정수 값을 멀티 스레드가 시그널 메커니즘을 이용해서 공유합니다.
카운팅 세마포어를 사용하면 여러 스레드가 공유 자원이나 critical section에 접근할 수 있습니다.
하지만, 세마포의 wait과 signal 함수가 데드락을 방지하도록 안전하게 사용돼야합니다.
또한 여러 스레드가 접근할 수 있다는 건 상호 배제 원칙을 위반하기 때문에 동시성 문제의 원인이 될 수 있습니다.

메모리의 구조에 대해 설명해보세요.

  • 함수 호출에 관련된 스택 프레임도 설명해보세요.

    [참고] [참고]

메모리에는 코드 영역, 데이터 영역, 힙 영역, 스택 영역이 있습니다.
코드 영역은 프로그램의 코드가 들어가는 영역입니다.
CPU는 코드 영역의 코드를 하나씩 가져가서 처리합니다.
데이터 영역은 전역, 정적 변수가 저장되는 영역입니다.
프로그램 시작과 함께 할당되며, 프로그램이 종료되면 소멸합니다.
스택 영역은 함수 시작과 함께 생성되고 함수가 끝나면 소멸되는 영역입니다.
스택 영역에 저장되는 함수의 호출 정보를 스택 프레임이라고 하며 스택 프레임에는 함수 호출 인자, 지역 변수, return memory address가 저장됩니다.
 영역은 사용자가 직접 관리하는 메모리 영역입니다.
사용자에 의해 동적으로 할당되고 해제됩니다.

가상 메모리에 대해 설명해보세요.

[참고]

가상 메모리는 물리적 메모리를 보안하기 위해 RAM에 있는 데이터를 디스크로 전달하는 기술입니다.
디스크 파일로 메모리 조각을 저장하가는 방식은 컴퓨터가 보조 메모리를 마치 주 메모리처럼 사용할 수 있게합니다.

가상 메모리는 시스템 성능 향상을 위해 꼭 필요합니다.
하지만, 너무 많이 의존하면 오히려 느려질 수 있는데 그 이유는 하드디스크 같은 보조 기억장치는 RAM에 비해 매우 느리기 때문입니다.
 OS가 너무 자주 RAM과 가상 메모리 사이 데이터를 스왑하게 되면, 컴퓨터 속도가 느려지는 thrashing 상태가 됩니다.