프로그래밍/소켓 프로그래밍 입문

윈도우 운영체제에서의 파일 I/O 구조: 요청과 처리

데일리 백수 2025. 1. 24. 21:07

파일 I/O라는 작업은 실제로는 운영체제(OS) 레벨에서 이뤄지며, 사용자 애플리케이션(유저모드 프로세스)은 단지 “이 파일에 이만큼의 데이터를 읽거나 쓰고 싶다”는 요청을 보낼 뿐입니다. 아래는 이러한 I/O 동작 과정을 단계별로 설명하고, 동기/비동기 처리의 중요성을 살펴봅니다.

1. 사용자 애플리케이션과 운영체제의 역할

1.1 사용자 애플리케이션(유저모드)

  • I/O 요청:
    • 예: ReadFile, WriteFile, fread, fwrite 등.
    • 실제 디스크나 네트워크 카드에 접근하지 않고, OS에게 “이 작업을 해달라”라고 요청.
  • 프로세스 수준 코드:
    • 코드는 비즈니스 로직(또는 애플리케이션 로직)만 담당.
    • 디스크나 네트워크 장치와 직접 통신 불가능.

1.2 운영체제(커널 모드)

  • I/O 관리:
    • 요청을 큐(Queue)에 등록.
    • I/O 스레드나 드라이버가 장치와 실제로 통신해 데이터를 읽거나 씀.
  • 장치 제어:
    • 디스크, 네트워크 카드, 메모리 등 하드웨어 자원을 직접 제어.

2. 운영체제의 I/O 처리 흐름

  1. 프로세스가 I/O 요청
    • 예: 사용자 코드에서 WriteFile(hFile, pBuffer, nSize, ...) 호출.
    • OS는 이 정보를 I/O 요청 큐에 넣고, 호출 함수는 즉시 반환(비동기) 또는 대기(동기).
  2. 운영체제의 I/O 스케줄러
    • 에서 요청을 꺼내 실제 디바이스 드라이버에게 작업 지시.
    • “파일의 위치가 여기이니, 이 위치부터 이만큼 써라/읽어라.”
  3. 디바이스 드라이버
    • 하드웨어와 직접 통신.
    • 디스크, 네트워크 어댑터 등 물리 장치와 상호 작용.
  4. I/O 완료
    • 작업이 끝나면 운영체제는 결과(성공/실패, 전송 바이트 수 등)를 애플리케이션에게 알림(비동기) 또는 함수 복귀(동기).

3. 동기 vs 비동기 I/O

3.1 동기 I/O

  • 대기(Wait):
    • I/O 요청 후, 작업이 완료될 때까지 해당 스레드는 블로킹.
    • 다중 I/O가 동시에 발생하면, 각 작업마다 스레드가 대기해야 하므로 성능 저하.
  • 간단 구현:
    • 코드 작성이 직관적이며, 절차대로 진행.

3.2 비동기 I/O

  • 즉시 반환:
    • I/O 요청 후, 함수는 곧바로 리턴.
    • 작업 완료 시점에만 별도로 알림(이벤트, 콜백 등)을 받음.
  • 고성능:
    • 여러 I/O를 동시에 걸어두고, 각각 완료될 때마다 처리.
    • 스레드가 블로킹되지 않아, CPU 자원을 효율적으로 사용.

4. Write 속도 vs Read 속도

4.1 일반적인 쓰기 속도 지연 이유

  1. 디스크 쓰기 비용:
    • 디스크에 실제 데이터를 기록하는 과정은 랜덤 액세스일수록 느림.
  2. 파일 확장:
    • 새로운 파일을 생성하면 처음 사이즈는 0.
    • 라이트(write) 시, 파일 끝 위치를 확장해야 하므로 추가 작업 발생.

4.2 파일 포인터 이동

  • 파일 포인터:
    • 현재 파일에서 읽거나 쓸 위치를 가리킴.
    • 쓰기가 일어나면, 이 포인터가 자동으로 오른쪽(파일 끝)으로 움직여 데이터가 이어서 기록됨.
  • 상대적 위치 vs 절대 위치:
    • 상대 위치: 현재 파일 포인터 기준.
    • 절대 위치: 파일 시작점 기준으로 계산(오프셋).

5. 위치 정보와 사이즈 정보

I/O 요청을 할 때, OS는 “파일의 어느 위치에, 얼마만큼”이라는 정보를 받아야 합니다.

  1. 파일 핸들(hFile):
    • CreateFile로 생성된 핸들.
    • 내부적으로 해당 파일의 메타데이터, 위치 정보 등을 관리.
  2. 오버랩(OVERLAPPED) 구조체(비동기 I/O에서 주로 사용):
    • Offset 필드를 통해 읽고 쓸 파일 위치 지정.
    • 비동기로 처리 시, 요청 완료 시점까지 대기하지 않고 다른 작업 수행 가능.