욱'S 노트

IO 모델 (Linux, Java) 본문

Methdology/IO

IO 모델 (Linux, Java)

devsun 2024. 12. 20. 09:54

IO 모델이란?

어플리케이션에서 디바이스간 데이터 전송 및 수신을 위해서는 OS의 시스템콜이 발생한다.

write system call의 경우, OS 커널은 전송하고자하는 데이터를 소켓 전송 버퍼에 복사하고, read system call의 경우 OS 커널은 수신 받은 데이터를 소켓 수신 버퍼에 복사한다.

Linux IO 기본개념

File Descriptor

리눅스 혹은 유닉스 계열의 시스템에서 프로세스가 파일을 다룰 때 사용하는 개념이다. 프로세스에서 특정 파일에 접근할 때 사용하는 추상적인 값이며 프로세스에서 열린 파일의 목록을 관리하는 테이블의 인덱스의 인덱스이다. 리눅스(유닉스) 에서는 모든것을 파일로 취급한다.(파일, 소켓 등) 각각의 프로세스는 File desciptors의 테이블을 가지고 있다.

IO Multiplexing

하나의 채널에서 둘 이상의 데이터르 전송하는 기술. 멀티플렉싱을 통해 여러 개의 파일을 다루기 위해 FD를 배열로 관리한다. 즉 개발자는 FD 배열을 통해, 여러개의 파일을 감시한다.

System Call

응용 프로그램에서 운영 체제에게 시스템 자원을 요청하는 수단. 시스템콜을 요청하면 제어가 커널로 넘어간다.

Select vs Epoll

Select는 싱글스레드에서 여러개의 파일을 작업 하고자 할 때 사용하는 방식이다. FD_SET 구조체는 1024 크기를 가진 배열이다. 변경된 데이터가 있으면 해당 비트값이 1이 되고, 이 비트값을 검사하여 파일에 변경된 데이터가 있는지 확인해서 읽기/쓰기를 수행한다. 기본적으로 polling 방식으로 O(n)의 계산량이 필요하다.

while(1){
       FD_SET(fd,&readfds);
 
       ret = select(fd+1, &readfds, NULL, NULL, NULL);
 
       if(ret == -1){
               perror("select error ");
               exit(0);
       }
 
       if(FD_ISSET(fd, &readfds)){
               while(( n = read(fd, buf, 128)) > 0)
                       printf("%s",buf);
       }
 
       memset(buf, 0x00, 128);
       usleep(1000);
 }

 

Poll은 소캣셋을 활용하여 소캣의 변화를 확인하는 방식이다. 1024 사이즈 제한이 없다.

 

Epoll은 poll과 select의 단점을 개선한 시스템 콜이다. 파일 디스크립터를 사용자가 아닌 커널이 관리한다. 이벤트가 발생하면 I/O 이벤트가 발생하고 통지된다. 즉, select처럼 어느 파일 디스크립터에 이벤트가 발생하였는지 찾기 위해 전체 파일디스크립터에 대해서 순차검색을 위한 FD_SET 루프를 돌려야 하지만, Epoll은 그렇지 않다. 코드는 폴링처럼 보이지만 내부동작이 시스템 콜이 발생하지 않는다는 걸 명심하자.

for(;;){

  nfds = epoll_wait(fd_epoll, events, MAX_EVENTS, 10);
  if(nfds < 0) {
    // critical error
    fprintf(stderr, "epoll_wait() error : %s\n", strerror(errno));
    exit(-1);
  }
  
  // no event
  if(nfds == 0){
    // idle
    continue;
  }
  for(n=0; n < nfds; ++n)
    OnEvent(&events[n]);
}

Blocking IO vs Non-Blocking IO

블록킹 IO는 스레드가 SystemCall의 응답을 기다리는 것, 논블록킹 IO는 스레드가 SystemCall의 응답을 기다리지 않고 바로 return하고 다른 일을 하는 것을 의미한다.

 

블록킹 IO는 시스템 콜 이후 대기시 스레드가 아무일도 하지 않을때도 스레드가 점유되는 형태를 말한다. 자바에서는 FileInputStream, FileOutputStream이 이에 해당한다.

논블럭킹 IO는 시스템 콜 이후 데이터그램이 준비되지 않으면, EWOULDBLOCK(블락될 수 있다)을 리턴한다. 즉 블락킹 될 수 있다는 메시지를 리턴함으로써 스레드는 점유하지 않고 다른 일을 할 수 있다. 자바에서는 ServerSocketChannel, SocketChannel, DatagramChannel등이 이에 해당한다.

I/O Multiplexing

논블락킹 IO에서 더 나아가 여러 채널을 한꺼번에 관리하는 개념이다. 자바에서는 Selector가 여러개의 채널의 입출력을 담당한다. 즉 소켓 켓 채널등은 Selector에 등록되며, 데이터 전송이 가능한 소켓에서 이벤트가 발생하면 실제 IO가 발생한다.

\

Asynchronous I/O

추가적으로 Asynchronous는 핸들러를 이용한 리액티브 형태의 방식을 지원한다. 스레드는 대기하지 않고 IO가 가능할 때 이벤트를 받아서 해당 IO 작업을 별도의 스레드에서 수행하게 된다. 자바에서는 NIO2에서 AsynchronousSocketChannel, AsynchronousServerSocketChannel, AsynchronousFileChannel 에서 지원한다.

`

AsynchronousFileChannel fileChannel 
      = AsynchronousFileChannel.open(path, StandardOpenOption.READ);

fileChannel.read(
      buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {

        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            // result is number of bytes read
            // attachment is the buffer containing content
        }
        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {

        }
    });

 

출처 :

https://itnext.io/optimizing-large-file-transfers-in-linux-with-go-an-exploration-of-tcp-and-syscall-ebe1b93fb72f

https://stackoverflow.com/questions/17615272/java-selector-is-asynchronous-or-non-blocking-architecture

Comments