-
[Java NIO] NIO 는 어떻게 동작할까 ? (DMA, Kernel Space)Language/Java 2024. 1. 31. 06:11728x90반응형
- 목차
들어가며.
이번 글에서는 Java NIO 가 어떻게 동작하는지 깊이있게 다루어보려고 합니다.
앞으로 Java NIO 와 관련된 글들을 이어서 작성할 예정입니다.
DMA Controller.
먼저 DMA Controller 에 대해서 알아보도록 하겠습니다.
컴퓨터를 아주 단순히 표현하면 아래 그림과 같이 표현될 수 있습니다.
CPU 와 Memory 그리고 Disk 로 표현될 수 있습니다.
좀 더 나아가서 NIC 같은 네트워크 인터페이스가 추가될 수 있죠.
그래서 Disk 에 존재하는 실행파일은 메모리로 옮겨지고,
CPU 는 Memory 로 적재된 실행파일의 코드를 한줄 한줄 실행하며 컴퓨터는 동작합니다.
또한 Network Card 를 통해서 유입되는 데이터를 읽어들이고, 다시 데이터를 전송하며 Network 통신 또한 가능합니다.
이러한 구조에서 Disk 와 Network Card 는 Peripheral Device 라고 부릅니다.
흔히 주변 장치라고 하죠.
CPU 는 메모리에 적재된 데이터를 한줄 한줄 읽고 실행한다고 말씀드렸습니다.
그럼 Memory 로 데이터를 옮기는 주체는 누구일까요 ??
그리고 어떻게 CPU 가 NIC 로 유입된 데이터를 알 수 있을까요 ?
이러한 동작을 도와주는 대상이 Device Controller 입니다.
위 그림을 다시 그려보겠습니다.
주변 장치와 CPU 또는 Memory 사이에 Controller 가 존재하며, Controller 는 이들 사이를 중재합니다.
세상에는 수 많은 벤더사들이 하드디스크, SSD, Network Card 를 만들고, 컴퓨터의 CPU 도 그 종류가 너무 다양합니다.
이 모든 조합을 만족하는 만능의 기계어는 존재하지 않겠죠 ?
Controller 는 그 사이에서 CPU 와 Peripheral Device 가 소통할 수 있도록 중재를 합니다.
마치 CPU 가 미국사람이고, Disk 가 러시아 사람이면 Controller 는 영어와 러시아어를 할 줄 아는 통역사와 같습니다.
즉, CPU 는 Controller 를 통해서 Peripheral Device 를 통제하게 됩니다.
여기서 문제가 발생합니다.
CPU 가 너무 많은 일을 수행하게 된다는 점입니다.
Disk 에 존재하는 파일을 읽어들이기 위해서 CPU 는 계속 명령을 내리고,
그 외 마우스, 키보드, 모니터 등등의 주변장치를 컨트롤하는데에 CPU 의 낭비되는 리소스가 커집니다.
그래서 DMA Controller 가 등장합니다.
DMA Controller 는 CPU 의 개입없이 주변장치의 데이터를 Memory 로 로드할 수 있습니다.
알아서 Disk 의 파일을 Memory 로 로드할 수 있고,
Network 통신을 위한 Socket 으로 유입된 네트워크 트래픽을 알아서 Memory 에 로드합니다.
Kernel Space Buffer.
이제 DMA Controller 를 통해서 CPU 의 개입없이 Disk 의 데이터가 메모리로 로드되는 원리를 알게 되었습니다.
지금부터는 커널 영역의 버퍼에 대한 개념에 대해서 알아보려고 하는데요.
User Space, Kernel Space 그리고 운영체제에 대해서 간단히 알아보도록 하겠습니다.
운영체제라는 것이 보편화되고 의무화되면서 모든 리소스 관련 요청은 운영체제를 통해서만 수행됩니다.
예를 들어, 어떤 프로그램을 실행하기 위해서 2GB 메모리가 필요하면 OS 에게 2GB 의 메모리를 요청해야합니다.
어떤 파일을 읽어들여야된다면 OS 에게 파일 읽기를 위한 요청을 보내야합니다.
이러한 요청들을 모두 System Call 이라고 부릅니다.
Java NIO 역시 File Read 와 관련된 System Call 을 OS 에게 요청합니다.
OS 는 Disk 로부터 File 을 읽어들이고, Kernel Space 의 버퍼에 해당 데이터를 저장합니다.
( 참고로 운영체제가 동작하는 메모리 영역을 Kernel Space 라고 합니다. )
그리고 Java NIO 의 File Read 를 수행한 JVM Process 는 자신의 User Space 로 Kernel Space Buffer 를 복사합니다.
만약 Non Direct Buffer 를 생성하게 된다면, Non Direct Buffer 는 JVM Heap 메모리로 Kernel Buffer 를 복사합니다.
그리고 Direct Buffer 는 Off-heap 메모리로 복사되죠.
Zero-Copy.
위에서 설명한 내용들을 토대로 File 또는 Network Socket 의 내용을 읽고 쓸 수 있습니다.
Disk -> Kernel Buffer -> Heap or Off-Heap Buffer
NIC -> Socket -> Kernel Buffer -> Heap or Off-Heap Buffer
와 같은 형식으로 말이죠.
그리고 Java NIO 는 Zero-Copy 이라는 중요한 기능이 추가됩니다.
이는 데이터의 전달과정에서 Application 의 Processing 이 필요없는 상황에서만 쓸 수 있습니다.
예를 들어, "File 하나를 Network 를 통해서 외부로 전달한다던지"
"Source File 을 Copy 하여 Dest File 로 복사한다던지 " 와 같은 경우입니다.
Application Level 에서의 Processing 이 필요없기 때문에 굳이 User Space 의 Heap 에 Buffer 를 생성할 필요가 없겠죠?
그래서 Java NIO 에서는 TransferTo, MMap 등의 System Call 을 JNI 로 구현하여 이를 가능케 합니다.
Kafka 나 Flink 와 같은 Data Stream 영역에서 Java NIO, Jetty 를 활용하여 Zero-Copy 방식을 사용합니다.
마치며.
Java NIO 는 위와 같은 방식으로 Disk 에서 RAM 으로 데이터를 로드하게 됩니다.
그리고 Direct Buffer 방식을 통해서 Off-Heap 영역을 사용할 수 있게 됩니다.
Channel, Buffer, Selector 등의 추상화 개념이 더 존재하며 앞으로 이어지는 글들에서 해당 내용을 상세히 다루어보도록 하겠습니다.
반응형'Language > Java' 카테고리의 다른 글
Jackson 으로 JSON 다루기 (0) 2024.01.22 Java off-heap 메모리 알아보기 (0) 2023.12.22 Java Thread Pool 알아보기 (0) 2023.09.30 Java Future 알아보기 (0) 2023.09.30 java Annotation 이해하기 (0) 2023.09.25