컴퓨터 구조 - I/O 구조
I/O 구조
DB에서 어떤 데이터에 대해서 읽고 쓴다고 해보자.
이 DB는 C, C++로 개발되었다고 할 때 Data I/O의 구조는 아래와 같다.
DB에서 데이터를 읽는 요청을 한다고하면 먼저 대상 데이터가 있는 파일에 대해서 정보를 알아내야한다. DB에서 Table 혹은 Collection(Document DB 기준)은 모두 파일로 저장되기 때문인데 알아낸 파일 위치는 Standard C Library에서 정의된 fread()를 이용하여 읽게 된다.
이 C Library는 내부적으로 본다면 SystemCall 함수로 구성되어있다.
대표적으로 fread()를 구성하는 시스템 함수인 read()는 open() 함수에서 리턴받은 파일 디스크립터값을 매개변수로 받는다.
이 파일 디스크립터는 파일의 맨처음을 가리키고 있기 때문에 read() 함수는 이 부분부터 EOF까지 읽는다면 해당 파일을 다 읽을 수 있다. (물론 파일 길이를 인자로 받고 그 길이의 끝에 EOF가 없다면 에러로 간주하고 -1을 반환하긴 한다)
이렇게 읽은 파일을 힙 영역의 포인터부터 할당받은 곳 만큼 읽어와서 메모리에 넣어주는 것이다.
이 read() 함수는 FileSystem에 접근하여 해당 File에 대한 정보를 가져오는 것이다. 따지고 보자면 file은 block의 집합이다. 때문에 FileSystem 입장에서는 추상화를 위해서 File이라는 단위가 필요한것이고 사실 read()에서 파일을 읽는다는 것은 지정된 block 만큼 읽어온다면 파일을 읽어올 수 있다.
하지만 우리가 보는 file이라는 것은 추상화된 가상 block 집합이기에 실제로 block device(HDD,SSD,M.2)등 에서 물리적으로 어디에 존재하는지 알아야한다. 이 것을 알고 있는것은 FileSystem의 inode 이기에 실상 FileSystem이 안다고 할 수 있다.
요컨대 block에 대한 인터페이스들을 지원해주는게 FileSystem인거고, 종류들은 그림에 나와있는 바와 같다.
(Linux의 경우 F2FS는 잘 못봤던 것일텐데 이는 Log기반 파일 시스템이다. 모바일에서 많이 사용한다고 한다. 아무래도 ext 계열에 비해서 성능을 예측하기 어려워서 잘 쓰지 않는다고 한다)
block Layer는 그냥 OS 입장에서 바라본 block device이기 때문에 block Layer라고 적혀있는 것이고 그 밑에는 block device를 지원하는 driver가 있다고 생각하면 된다.
그 아래에는 실제적인 Hardware가 있는 것이다.
아까 read()에서 실질적인 File에 대한 위치를 FileSystem에서 받아왔으니 driver를 통해서 Device에 요청을 한다. NVMe command의 경우에는 아래와 같은 형태라고 한다.
1
(Read, Block No : 128, Count :4)
Device에서는 위와 같은 command를 받으면 해당 위치의 블럭만큼 데이터를 읽어들인 뒤에
인자로 받은 RAM의 위치에 그만큼은 복사해넣는다. 이를 실행하는 주체는 DMA(Direct Memory Access) Engine이다. CPU를 거치지 않고 Block에서 RAM으로 바로 보내는 것이다(이를 DMA라고 한다).
명령어 자체에 RAM의 물리 주소가 있기 때문에 DMA를 바로 보낼 수 있다.
이후 RAM에 데이터가 모두 복사되었다면 인터럽트를 통해 CPU에 알리게 되고 CPU은 인터럽트 핸들러를 통해서 해당 데이터를 처리하게 된다.
특정 Device의 경우 DMA engine이 두 개라서 명령어 큐에서 device 명령어를 갖고오는 엔진이 있고 데이터를 주고받는 DMA engine이 있다고 한다.
참고자료
- 서강대학교 김영재 교수님 고급데이터 베이스 강의 자료
- DB 그림들은 각 DB 공식 홈페이지에서 발췌
- M.2 SSD 그림 - 삼성전자 공식 홈페이지
- SATA SSD 그림 - 시게이트 공식 홈페이지
- HDD 그림 - 시게이트 공식 홈페이지