6월 7일-Blocking I/O & Non-Blocking I/O

Yullgiii·2024년 6월 7일
0
post-thumbnail

Blocking I/O & Non-Blocking I/O

I/O 작업은 Input/Output의 약자로, 주로 파일 입출력을 다룰 때 사용된다. 예를 들어, 두 대 이상의 컴퓨터끼리 네트워크를 통해 통신할 때, 한 컴퓨터에서 출력(send)을 하고 다른 컴퓨터에서 입력(receive)을 받는 과정을 통해 통신할 수 있다. I/O 작업은 User Level에서 직접 수행할 수 없고, 실제 I/O 작업을 수행하는 것은 Kernel Level에서만 가능하다. User Process나 Thread는 커널에게 요청하고, 작업 완료 후 커널이 반환하는 결과를 기다릴 뿐이다.

Blocking Model

가장 기본적인 I/O 모델로, 리눅스에서 모든 소켓 통신은 기본적으로 blocking으로 동작한다. I/O 작업이 진행되는 동안 유저 프로세스는 자신의 작업을 중단한 채 대기하는 방식이다.

동작 과정

  1. 유저는 커널에게 read 작업을 요청한다.
  2. 데이터가 입력될 때까지 대기한다.
  3. 데이터가 입력되면 커널이 유저에게 결과를 전달하고, 유저는 자신의 작업에 복귀한다.

이 모델에서는 작업이 진행되는 동안 어플리케이션이 다른 작업을 수행하지 못하고 대기하게 되므로 자원이 낭비될 수 있다.

Non-Blocking Model

위와 같은 blocking 방식의 비효율성을 극복하고자 도입된 방식이다. I/O 작업이 진행되는 동안 유저 프로세스의 작업을 중단시키지 않는 방식이다.

동작 과정

  1. 유저가 커널에게 read 작업을 요청한다.
  2. 데이터가 입력되었든 입력되지 않았든 요청하는 순간 바로 결과가 반환된다.
    • 입력 데이터가 없으면, 입력 데이터가 없다는 결과 메시지를 반환한다.
  3. 입력 데이터가 있을 때까지 1~2번을 반복한다.
  4. 입력 데이터가 있으면 커널이 유저에게 결과를 전달한다.

이 모델에서는 I/O의 진행 시간과 관계없이 어플리케이션이 작업을 중지하지 않고도 I/O 작업을 진행할 수 있다. 하지만 반복적으로 시스템 호출이 발생하므로 자원이 낭비될 수 있다.

예제 코드

Blocking I/O 예제

import java.io.*;
import java.net.*;

public class BlockingIOExample {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(6666)) {
            Socket socket = serverSocket.accept();
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                System.out.println(inputLine);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Non-Blocking I/O 예제

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class NonBlockingIOExample {
    public static void main(String[] args) {
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(6666));
            serverSocketChannel.configureBlocking(false);
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            while (true) {
                selector.select();
                for (SelectionKey key : selector.selectedKeys()) {
                    if (key.isAcceptable()) {
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(256);
                        socketChannel.read(buffer);
                        String result = new String(buffer.array()).trim();
                        System.out.println(result);
                        buffer.clear();
                    }
                }
                selector.selectedKeys().clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

So...

Blocking I/O와 Non-Blocking I/O는 각각의 장단점을 가지고 있다. Blocking I/O는 구현이 간단하고 이해하기 쉬운 반면, Non-Blocking I/O는 자원을 더 효율적으로 사용할 수 있다. 작업의 특성과 요구 사항에 따라 적절한 I/O 모델을 선택하는 것이 중요하다.

profile
개발이란 무엇인가..를 공부하는 거북이의 성장일기 🐢

0개의 댓글