시스템 프로그래밍시간에 배운 개념중 개인적으로 중요하다고 생각한 개념을 정리해보았다.
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. 참고자료
'CS' 카테고리의 다른 글
OS 파일 시스템: 파일 테이블과 I/O 성능 개선 기법 (0) | 2024.12.30 |
---|---|
스레드 기본 개념 (0) | 2024.12.23 |
OS가 관리하는 Clock & Hardware Clock (0) | 2024.12.15 |
표준 I/O 라이브러리의 등장 배경: 시스템 콜 성능 최적화 (1) | 2024.12.08 |
CPU & I/O Bound Job: 최적의 스레드 개수 (0) | 2024.11.21 |