CS

OS의 I/O Device 추상화 & Block I/O & Non-Block I/O: Multiplexing

컴공오지마라 2024. 12. 1. 21:51

시스템 프로그래밍시간에 배운 개념중 개인적으로 중요하다고 생각한 개념을 정리해보았다.

1. OS의 I/O Device 추상화

1.1 OS는 I/O Device를 File 이라는 개념으로 추상화한다.

운영 체제는 다양한 입출력 장치를 일관된 방식으로 다루기 위해 I/O Device를 파일이라는 개념으로 추상화한다. 이를 통해 운영 체제는 키보드, 파이프, 스토리지, 네트워크 소켓 등 모든 I/O 장치를 동일한 방식으로 다룬다. 예를 들어, 프로그램이 하드 디스크에서 데이터를 읽는 작업과 네트워크에서 데이터를 읽는 작업을 동일한 방식으로 처리할 수 있다. 이 덕분에, 응용 프로그램 개발자는 하드웨어 세부 사항을 신경 쓰지 않고도 입출력 작업을 처리할 수 있다. 운영체제는 “파일 디스크립터”라는 모든 I/O Device 에 대한 인터페이스를 제공하며, 이는 장치 간 차이를 숨기는 역할을 한다. 운영 체제는 이를 통해 다양한 I/O 장치를 효율적으로 관리한다.

1.2 File Descriptor 의 역할

파일 디스크립터는 운영 체제에서 파일, 소켓, 파이프 등 다양한 입출력 장치를 참조하는정수 값이다. 프로세스가 파일을 열거나, 소켓을 연결하는 등의 작업을 할 때, 운영 체제는 이를 파일 디스크립터로 할당해 관리한다. 이후, 프로세스는 이 파일 디스크립터를 통해 해당 장치에 대한 읽기, 쓰기, 닫기 등의 작업을 수행할 수 있다.

2. Block I/O와 Non-Block I/O

2.1 Block I/O

Block I/O에서는 프로세스나 스레드가 *I/O 작업을 즉시 완료할 수 없을 * Blocked 상태로 전환된다.
이 상태에서는 해당 프로세스나 스레드는 I/O 작업이 완료될 때까지 CPU를 사용하지 않고 대기하게 된다.

Block I/O 동작과정

  • I/O 작업 요청: 프로세스나 스레드가 read(), write() 등의 I/O 요청을 보낸다.
  • 즉시 완료 여부 확인: 요청한 작업이 바로 처리 가능하다면 결과를 반환한다.
  • 즉시 완료 불가능: 작업이 디스크에서 데이터를 읽어오거나 네트워크에서 응답을 기다리는 등 시간이 걸리는 작업이라면 프로세스나 스레드는 Blocked 상태로 전환된다.
  • Blocked 상태: 작업이 완료될 때까지 해당 프로세스는 대기하고, CPU는 다른 작업에 할당된다.
  • 작업 완료 후 : I/O 작업이 완료되면 프로세스나 스레드가 Ready(실행가능)상태가 되어 작업을 이어나간다.

참고로 I/O 작업이 정상적으로 실행되면, 프로세스를 실행하는 CPU가 커널모드로 바뀌게 되고 데이터를 커널영역에 받아온 후 이를 유저 스페이스로 이동시키고 I/O작업을 마무리한다.

2.2 Non-Block I/O

Non-Block I/O는 프로세스나 스레드가 I/O 작업을 요청했을 때, I/O 요청에 대한 현재상태를 바로 응답한다.

Non-Block I/O 동작과정

  • I/O 작업 요청: 프로세스나 스레드가 read()write() 같은 I/O 요청을 보낸다. 이때, 작업이 완료되지 않더라도 요청은 바로 반환된다.
  • 즉시 완료 여부 확인: 요청한 작업이 바로 처리 가능하면 결과를 반환하고, 그렇지 않으면 "작업이 완료되지 않았음"을 알리는 상태를 반환한다.
  • 작업 대기 없이 진행: 요청한 작업이 완료되지 않은 경우에도 프로세스는 Blocked 상태로 전환되지 않고 다른 작업(예: 계산, 로그 출력 등)을 계속 수행한다.
  • I/O 상태 확인 (Polling 또는 Callback): 프로세스는 주기적으로 요청한 작업의 완료 여부를 확인하거나, 작업 완료 시 호출되는 Callback을 통해 결과를 처리한다.
    • Polling: 프로세스가 직접 작업 완료 여부를 반복적으로 확인한다.
    • Callback: 작업이 완료되면 지정된 함수가 호출되어 결과를 처리한다.
  • 작업 완료 후 처리:I/O 작업이 완료되면, 프로세스는 그 결과를 받아 필요한 작업을 이어간다.

3. I/O 작업 완료를 어떻게 확인할 것인가?

3.1 Polling (폴링)

  • 프로세스가 주기적으로 작업의 완료 여부를 직접 확인하는 방식. 작업이 완료되었는지 상태를 반복적으로 조회하며, 완료되었을 경우 결과를 처리한다.
  • 장점: 간단한 구현 방식.
  • 단점: CPU 자원을 낭비할 가능성이 높으며, 효율이 떨어질 수 있음.

3.2 Callback (콜백)

  • I/O 작업 요청 시, 작업이 완료되면 호출될 함수를 미리 등록하는 방식이다. 작업이 완료되면 커널이나 라이브러리가 이 함수를 호출하여 결과를 처리한다.
  • 장점: CPU 자원을 효율적으로 활용하며, 비동기 처리에 적합.
  • 단점: 디버깅이 어렵거나 콜백 지옥(CallBack Hell) 문제가 발생할 수 있음.

3.3 Event-Driven 방식

  • 이벤트 루프를 통해 여러 I/O 작업을 감시하며, 작업이 완료되면 이벤트를 발생시키고 해당 작업을 처리한다.
  • 장점: 고성능 처리에 적합하며, 대규모 비동기 작업 관리에 효율적.
  • 단점: 이벤트 루프 설계 및 구현이 복잡할 수 있음.

3.4 정리

방식 장점 단점 사용 사례
Polling 간단한 구현, 빠른 확인 CPU 자원 낭비 가능 짧은 작업, 간단한 시스템
Callback CPU 효율적 활용, 자연스러운 비동기 처리 코드 복잡성 증가, 디버깅 어려움 GUI, 네트워크 서버
Event 고성능 처리 가능, 대규모 시스템에 적합 이벤트 루프 설계 필요, 구현 복잡 고성능 서버, Node.js, Java NIO

최근 가장 널리 사용되는 Non-Blocking I/O 방식은 Event-Driven 방식이라고 한다. 또한, Spring WebFlux와 같은 Reactive Programming 프레임워크에서도 Event-Driven 아키텍처를 채택하는 등 많은 라이브러리에서 Event-Driven 방식을 채택한다고 한다. 이제 Non-Blocking I/O를 구현하는 대표적인 방식인 Multiplexing에 대해서 알아보도록 하자.

4. I/O Multiplexing 이란?

프로그램은 여러개의 I/O 작업의 상태를 동시에 모니터링을 요청하고 OS는 그 중 완료된 I/O 작업들을 한번에 알려주는 방식. Multiplexed I/O를 호출한 스레드나 프로세스는 Blocked 될수도 있고 바로 실행될수 도 있다.

동작과정

  • 여러 파일 디스크립터(소켓, 파일 등)의 상태를 I/O 작업완료 확인 요청 (시스템 콜 호출)
  • 하나 이상의 작업이 준비될 때까지 프로세스는 Blocked 상태로 전환
  • 작업이 가능한 디스크립터들 확인
  • Block 없이 처리 가능한 작업만 수행
  • 위 과정을 반복

5. 참고자료

https://jh-labs.tistory.com/334