Block I/O vs Non-Block I/O
I/O 종류
I/O란 Input/Output인 입출력을 말한다.
- network(socket)
- file
- pipe (process 간에 통신할 때 사용되는 개념)
- device (모니터, 키보드, 등): 네트워크 통신은 소켓을 통해 데이터가 입출력 된다.
- socket
소켓
: 네트워크 통신은 소켓을 통해 데이터가 입출력 된다.
2개의 컴퓨터의 프로세스가 데이터를 주고 받기 위해서는 소켓을 열고 소켓을 통해서 데이터를 주고받을 수 있다.
backend server
: 네트워크 상의 요청자들과 각각 소켓을 열고 통신한다.
밖에 있는 클라이언트들이 소켓을 열고 요청을 한다.
백엔드도 소켓을 열고 받은 요청의 클라이언트와 상호작용한다.
OS 레벌에서 Block I/O와 Non-Block I/O가 어떻게 동작하나
Block I/O
: I/O 작업을 요청한 프로세스/스레드는 요청이 완료될 때까지 블락됨
- 스레드에서 작성된 코드가 쭉 실행
- 1번을 실행하다 blocking sysetmcall이 입력되면 스레드는 블락됨 => 스레드가 동작을 멈춤
- 시스템 콜에 의해 커널로 감
- 커널에서는 read I/O 실행
- 해당 디바이스에서 읽을 준비 됐다고 응답 줌
- 커널이 응답 받고 준비된 데이터를 커널 스페이스에서 유저 스페이스로 옮김
- 그때 블락되었던 스레드가 풀림
- 나머지 코드 실행
소켓에서 Block I/O
- Socket S에서 A로 데이터를 보내려고 한다면 A는 기다려야 함
- 여기서 기다린다는 의미란 !?!
- 소켓에는 2가지 버퍼가 있다.
- READ SYSTEM CALL이 오면 recv_buffer에 데이터가 들어올 때까지 read system call을 요청한 스레드는 블락됨
- Socket S입장
- write 하면 send_buffer에 전송하려는 데이터를 씀
- 이 때 send_buffer에 데이터가 차는 데 가득 차면 비워질 때까지 블락됨
- 정리
- 각각의 send_buffer, recv_buffer의 상태에 따라 블락이 된다.
Non-Block I/O
: 프로세스/스레드를 블락시키지 않고 요청에 대한 현재 상태를 즉시 리턴
1. 스레드에서 non-bolcking으로 read systemcall 호출
2. system call하는 순간 커널모드로 스위칭
3. 커널에서 read I/O 실행
4. 커널에서 바로 리턴 때림 (리눅스 기준 -1, EAGAIN, EWOULDBLOCK)의 에러와 같이
5. 블락이 아니라 바로 스레드는 다음 코드를 실행
6. 그러던 중 I/O 디바이스로부터 데이터 읽을 준비 됐다는 응답이 커널로 옴
7. 커널은 데이터 준비 함
8. 스레드는 non-bolcking으로 다시 시스템 요청
9. 이때 전에 요청했던 데이터가 리턴되면서 유저 스페이스로 옮겨감
10. 받은 데이터 읽고 나머지 작업 실행
정리
non-block I/O는 블락되지 않고 즉시 리턴하기 때문에 스레드가 다른 작업을 수행할 수 있다.
소켓에서 Non-Block I/O
- S에서 A로 데이터 보냄
- recv_buffer에 데이터가 있는 지 read로 확인
- 데이터가 없으면 없다고 알려주고 read system call은 바로 종료 ( 블락 안됨 )
- send_buffer에 데이터가 가득 쳤어도 write system call이 종료되는 게 아니라 해당하는 에러코드와 함께 반환된다.
Non-block I/O 결과 처리 방식
non-block I/O 이슈
그렇다면 I/O 작업 완료는 어떻게 확인하는 것인가?
1. 완료되었는 지 반복적으로 확인
- 완료된 시간과 완료를 확인한 시간 사이의 갭으로 인해 처리 속도가 느려질 수 있음
- 반복적으로 확인하며 cpu 낭비가 발생
2. I/O multiplexing (다중 입출력) 사용
: 관심있는 I/O 작업들을 동시에 모니터링하고 그 중 완료된 I/O 작업들을 한 번에 알려줌
- multiplexing 으로 system call을 하면 상태에 따라 스레드를 블락시킬 수도 있고 안시킬 수도 있다.
I/O 멀티 플렉싱 종류
- select
- poll
- epoll
- kqueue
- IOCP(I/O completion port)
- => 이 3개가 많이 쓰임
3. callback/singal 사용
- aio_read를 non-bolcking으로 호출
- 커널에서 read i/o실행
- 스레드는 독립적으로 실행
- 이때 데이터 응답이 오면 callback이나 singal 형태로 실행
callback/signal 종류
- POSIX AIO (여러 운영체제에서 표준화된 것 )
- LINUX AIO
=> 하지만 많이 사용되지는 않음
결론
Non-Block I/O를 통해 I/O요청의 완료 전에도 다른 일을 할 수 있다!!!
참조
쉬운 코딩님의 강의를 들으면서 정리한 겁니다.