본문 바로가기

CS 과목(CS科目)/운영체제(OS)

block I/O vs non-block I/O with Socket I/O ( I/O Multiplexing도 설명함)

I/O: Input Output의 약자로서 , 데이터의 입출력을 의미.

I/0 종류

1. network(socket) I/O

-> 네트워크 통신은 Socket을 통해서 데이터가 입출력 된다. 

 

2. file I/O

3. pipe I/O( 프로세스 간의 통신 시 사용되는 개념)

4. device I/O

 

Socket I/0를 가지고 OS Level에서 block I/O와 non block I/O의 동작을 설명하겠다. 

Block I/O : I/O 작업을 요청한 프로세스/스레드는 요청이 완료될 때까지 BLOCK됨. ( 잘 사용되지 않고, non block이 일반적으로 사용됨)

( 대부분의 프로그래밍 언어는 non blocking I/O를 적극 사용)

 

일단 각 Socket에는 데이터를 받는 recv_buffer와 데이터를 보내는 send_buffer가 있다.

Thread A가 만약 소켓으로부터 데이터를 입력(I/O)한다고 하자.

근데 이 read 작업이 만약 blocking system call이고, recv_buffer에 read할 데이터(I/O)가 없으면, Thread A는 Block처리가

되어, recv_buffer에 데이터가 도착할 때까지 대기 상태가 된다. 

Thread B가 만약 소켓에 데이터를 출력(I/O)시킨다고 하자.

근데 이 write 작업이 blocking system call이고, send_buffer에 이미 데이터가 꽉 차있다면, Thread B는 block처리가 되어,

recv_buffer에 빈 공간이 생길 때까지 대기를 한다. 

 

non block I/O : 프로세스/스레드를 BLOCK시키지 않고, 요청에 대한 현재 상태를 즉시 리턴

(EAGAIN, EWOULDBLOCK : 리눅스의 에러 코드)

위와 같이 non block I/O는 프로세스/스레드가 BLOCK되지 않고 즉시 리턴하기 때문에 스레드가 다른 작업을 수행할 수가

있다. 

 

recv_buffer에 읽을 데이터가 없어도, 스레드는 block처리가 되지 않고 스레드에게 recv_buffer에 데이터가 없다는 것을 알

려주고 read system call을 종료하고 스레드의 다른 작업을 실행한다. 

write도 마찬가지로, send_buffer에 데이터가 꽉 차 있으면 스레는 block 처리가 되지 않고, 스레드에게 send_buffer가 다 찼

다고 알린 뒤, write system call을 종료하고 스레드의 다른 작업을 실행한다. 

 

non block I/O 관련 issue : I/O 작업 완료를 어떻게 확인할 것인가??

non block I/O를 처리하는 여러 방식

1. 완료됐는지 반복적으로 확인

thread run 중에 다시 read system call을 호출을 하여서 I/O 작업의 완료 여부를 알 수가 있다.

이 방식을 비추천하는 이유

a)완료된 시간과 확인하는 시간 사이의 GAP으로 인해 처리 속도가 느려질 수 있다.

b)반복적으로 확인을 해야하므로 CPU를 낭비!

만약 위와 같이 서버에 소켓이 많은 경우를 살펴 보자.

서버의 cpu는 저 많은 소켓들에 대해서 일일이 확인하는 작업을 거쳐야 하기 때문에 서버의 성능이 현저히 저하될 것이다.

Q. 그럼 걍 Block I/O 방식을 채택하면 되지 않아??

A. NOPE!!

예를 들어, 서버에는 단 1개의 스레드만이 있다고 하자.

서버의 스레드는 read 작업을 하는 와중에 읽을 데이터가 없어서 block된 상태로 아무런 작업을 못하고 기다리고 있다고

하자. 거기다가 해당 클라이언트가 계속해서 데이터를 보내고 있지 않다고 해보자. 

그때 마침, 다른 클라이언트들이 동시에 서버의 소켓에 데이터를 보냈다고 하자.

근데, 여기서 문제점이 발생을 한다.

서버의 스레드는 이미 block된 상태로 read 작업에서 멈춘 상태로 아무것도 하지를 못한다.

즉, 다른 클라이언트가 소켓으로 보낸 데이터를 read하지를 못하므로, 그 어떤 요청에 대해서도 응답을 주지 못한다.

해결책 : I/O Multiplexing

 

2. I/O Multiplexing ( 다중 입출력 ) : 관심있는 I/O 작업들을 동시에 모니터링하고 그 중에 완료된 I/O 작업들을 동시에 알려줌. ( 가장 많이 사용되는 non blocking I/O 기법)

monitor 2 sockets non blocking read : "2개의 socket에 대해 read할 건데, 2개 중 1개라도 read가 가능할 때 나한테 알려

줘"라고 OS 커널이 네트워크 디바이스에게 신호를 보낸다. 

thread blocked/run : 설정에 따라 socket의 recv_buffer에 읽을 데이터가 없어도, run 상태로 만들 수도 있다. 여기서는

block되는 걸로 설명을 하겠다.

2개의 read response가 thread에 도착을 하였고, 2개의 소켓에 대해서 read non blocking system call을 실행하여 소켓의 

순차적으로 데이터를 읽어 들인다. 

 

I/O multiplexing의 종류

1. select

2. poll

위 2개는 성능 면 때문에 잘 안 쓰이고, 아래의 3가지가 잘 사용됨. 

3. epoll ( 리눅스에서 사용됨 )

4.kqueue( 맥os에서 사용됨 )

5.IOCP(I/O Completion Port ( 윈도우, 솔라리스 계열에서 사용됨)

 

3. Callback/signal 사용 ( 잘 사용되지 않음)

 

Callback/signal 종류

1. POSIX AIO ( 여러 OS에서 표준으로서 사용될 수 있다.)

2. LINUX AIO

 

4. io_uring (최근 리눅스에서 나온 기법)

강사도 잘 모른다고 함. 

 

핵심 : non block I.O을 통해 I/O 요청 완료 전에도 다른 일을 할 수가 있다는 것!