운영체제 용어 사전
I/Oblocking · non-blocking

DMA

CPU를 거치지 않고 장치가 메모리에 직접 전송하는 방식. 인터럽트와 짝을 이뤄 CPU를 해방한다.

장치가 CPU를 거치지 않고 메모리에 직접 데이터를 옮기는 방식.

그전에 — 준비됐는지 어떻게 아나

폴링    CPU 가 계속 물어본다 "됐어? 됐어?"
        → 즉시 반응한다
        → 기다리는 동안 CPU 를 전부 태운다
        → 아주 짧고 빈번한 장치에만 (고성능 네트워크 카드가 부하가 높을 때 쓴다)

인터럽트 장치가 끝나면 CPU 를 툭 친다
        → 기다리는 동안 CPU 는 다른 일을 한다
        → 대신 인터럽트 처리에 문맥 전환 비용이 든다
        → 대부분의 장치가 이 방식

DMA가 없다면

디스크에서 1MB 를 읽는다

  • CPU 가 4바이트씩 읽어 메모리에 옮긴다 → 26만 번 반복
  • 그동안 CPU 는 아무것도 못 한다 (Programmed I/O)

DMA가 있으면

  • ① CPU 가 DMA 컨트롤러에 지시한다 ("디스크 X 에서 메모리 Y 로 1MB")

  • ② CPU 는 다른 일을 하러 간다

  • ③ DMA 컨트롤러가 직접 전송한다

  • ④ 끝나면 인터럽트로 CPU 에 알린다

  • CPU 는 시작과 끝만 관여한다

대량 전송에서 CPU를 해방하는 것이 DMA의 본질이고, 인터럽트와 짝을 이뤄야 완성된다.

캐시 일관성 문제

DMA 가 메모리를 직접 고치면 CPU 캐시의 사본이 낡는다

  • 하드웨어가 스누핑으로 해결하거나
  • 드라이버가 명시적으로 캐시를 무효화/플러시해야 한다

기다리는 동안 무엇을 하나 — 세 모델

  • blocking — 호출이 끝날 때까지 스레드가 멈춘다

    • 코드가 단순하다. 대신 스레드가 놀고 있다
  • non-blocking — "아직 준비 안 됨"(EAGAIN)을 즉시 반환한다

    • 직접 다시 물어봐야 한다 → 대개 멀티플렉싱과 함께 쓴다
  • async — "다 되면 알려 줘" 를 등록하고 즉시 돌아온다

    • 완료되면 콜백·시그널로 통지받는다 (io_uring · IOCP)
    • 커널이 데이터 복사까지 끝내 준다는 점이 non-blocking 과 다르다

non-blocking 은 "준비됐는지" 를 알려 준다 (readiness) async 는 "다 끝났다" 를 알려 준다 (completion)

이 차이가 리눅스 epoll 과 io_uring 을 가른다

관측

cat /proc/interrupts | head        # 인터럽트 종류별 횟수
iostat -x 1                        # 장치별 I/O · await(대기 시간) · %util
cat /proc/<pid>/io                 # 프로세스별 읽고 쓴 바이트

면접 함정

  • "인터럽트가 폴링보다 항상 낫다" → 이벤트가 폭주하면 인터럽트 처리 비용이 지배한다. 그래서 고부하 네트워크는 폴링으로 전환한다(NAPI).
  • "non-blocking = 비동기" → 다르다. non-blocking은 준비 여부만 알려 주고 읽기는 내가 한다.

zero-copy — 복사를 없앤다

파일을 네트워크로 보낼 때 (전통적 방식)
  ① 디스크 → 커널 페이지 캐시   (DMA)
  ② 커널 → 유저 버퍼            (CPU 복사)
  ③ 유저 버퍼 → 커널 소켓 버퍼   (CPU 복사)
  ④ 소켓 버퍼 → NIC             (DMA)
  • 복사 4번, 모드 전환 4번
sendfile / splice
  ① 디스크 → 페이지 캐시  (DMA)
  ② 페이지 캐시 → NIC     (DMA, 디스크립터만 전달)
  • 복사 2번, 모드 전환 2번

정적 파일 서버·프록시가 빠른 이유가 여기 있다.

io_uring

유저와 커널이 공유하는 링 버퍼 두 개(제출·완료)를 둔다

  • 요청을 링에 쌓고 한 번의 시스템 콜로 여러 개를 제출한다
  • 폴링 모드에서는 시스템 콜 없이도 진행된다
  • 파일·네트워크 I/O 를 같은 인터페이스로 다룬다

리눅스에서 "진짜 비동기 I/O" 를 처음으로 제대로 제공한 API 다 (기존 aio 는 제약이 많아 거의 쓰이지 않았다)

iostat -x 1
# %util  장치가 바쁜 비율 (SSD 에서는 100% 여도 여유가 있을 수 있다)
# await  요청당 평균 대기 시간 ← 이게 튀면 진짜 병목
# aqu-sz 평균 큐 길이

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — I/O — 폴링/인터럽트·DMA·blocking/async