Post

리눅스 - 블록 입출력 계층

블록 입출력 계층

1. 개요

블록 장치는 고정된 크기의 데이터 덩어리를 임의 접근한다는 특징이 있는 하드웨어 장치이다. 고정된 크기의 데이터 덩어리를 블록이라고 부르는데 이 블록에 접근하기위한 장치인 것이다. 가장 대표적인 블록장치는 하드디스크가 있고, SSD 또한 이 블록 장치라고 할 수 있다. 그 외 플로피 드라이브나 플래시 메모리 등 많은 블록 장치가 있으며 이 모든 장치는 파일시스템을 통해 마운트하여 엑세스 할 수 있다.

커널에서 블록 장치를 관리하려면 많은 준비와 작업이 필요한데 이러한 블록 장치 관리를 위하여 전용 서브 시스템을 제공한다. 이 전용 서브 시스템을 운용하기 위한 부분이 커널에서 블록 입출력 계층이다.

2. 블록 장치 구조

블록 장치에서 접근 가능한 가장 작은 단위는 섹터이다. 여러가지 2의 거듭제곱값을 섹터로 사용하지만 전통적으로 사용하기에는 512 바이트를 사용한다. (HDD의 한 섹터가 512 바이트이기 때문, 물론 CD-ROM 같은 것들은 2KB 크기의 섹터를 사용하여 다른 크기인 경우도 있다 저장 장치에 대한 포스팅은 아래의 링크를 참고하면 좋다)

파일 시스템에서는 블록의 배수 단위로만 접근 할 수 있는데 장치에 접근 가능한 최소 단위가 섹터이므로 블록 크기는 섹터 크기보다 작을 수 없으며 섹터 크기의 배수가 된다. 또한 커널에서 블록의 크기도 2의 거듭제곱 형태가 되어야하며 페이지 크기보다 클 수 없다.

3. 버퍼와 버퍼헤드

블록을 읽고 난 후, 또는 블록 쓰기를 준비할 때처럼 블록이 메모리상에 존재할 때 블록은 버퍼에 저장된다. 각 버퍼는 하나의 블록에 대응되며 이 버퍼는 디스크상의 블록을 메모리상에 표현하는 객체 역할을 한다. 블록은 하나 이상의 섹터로 구성되며 페이지 크기를 넘지 않는다. 커널은 데이터와 함께 관련 제어 정보를 필요로 하기 때문에 각 버퍼에는 서술자가 붙어있다. 이 서술자를 버퍼헤드라고 하며 “include/linux/buffer_head.h” 파일에 정의된 buffer_head 구조체를 사용해 표현한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct buffer_head {
	unsigned long b_state;		/* 버퍼 상태 비트맵 */
	struct buffer_head *b_this_page;/* 페이지의 버퍼들의 순환 리스트 */
	union {
		struct page *b_page;	/* 이 버퍼 헤드가 매핑된 페이지 */
		struct folio *b_folio;	/* 이 버퍼 헤드가 매핑된 folio */
	};

	sector_t b_blocknr;		/* 시작 블록 번호 */
	size_t b_size;			/* 매핑된 크기 */
	char *b_data;			/* 페이지 내 데이터에 대한 포인터 */

	struct block_device *b_bdev; /* 블록 디바이스 */
	bh_end_io_t *b_end_io;		/* I/O 완료 처리 함수 */
	void *b_private;		/* b_end_io를 위한 예약 공간 */
	struct list_head b_assoc_buffers; /* 다른 매핑과 연관된 버퍼들 */
	struct address_space *b_assoc_map;	/* 이 버퍼가 연관된 매핑 */

	atomic_t b_count;		/* 이 buffer_head를 사용하는 사용자 수 */
	spinlock_t b_uptodate_lock;	/* 페이지의 첫 번째 버퍼 헤드에서 사용됨.
					 * 페이지 내 다른 버퍼들의 I/O 완료를
					 * 직렬화하기 위해 사용 */
};

제일 위에 b_state는 파일의 가장 위에 있는 항목인 bh_state_bits로 정의되는데 내용은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum bh_state_bits {
	BH_Uptodate,	/* 유효한 데이터를 포함함 */
	BH_Dirty,	/* 변경됨 */
	BH_Lock,	/* 버퍼에 입출력 작업 중이니까 동시 접근이 금지중이다 */
	BH_Req,		/* I/O 작업 중이다.*/

	BH_Mapped,	/* 디스크 매핑이 존재함 */
	BH_New,		/* 디스크 매핑이 get_block에 의해 새로 생성됨 */
	BH_Async_Read,	/* end_buffer_async_read I/O 중 */
	BH_Async_Write,	/* end_buffer_async_write I/O 중 */
	BH_Delay,	/* 버퍼가 아직 디스크에 할당되지 않음 */
	BH_Boundary,	/* 블록 뒤에 불연속성이 이어짐 */
	BH_Write_EIO,	/* 쓰기 중 I/O 오류 발생 */
	BH_Unwritten,	/* 버퍼가 디스크에 할당되었지만 기록되지 않음 */
	BH_Quiet,	/* 버퍼 오류 메시지를 출력하지 않음 */
	BH_Meta,	/* 버퍼가 메타데이터를 포함함 */
	BH_Prio,	/* 버퍼가 REQ_PRIO로 제출되어야 함 */
	BH_Defer_Completion, /* AIO 완료를 workqueue로 지연 처리 */

	BH_PrivateStart,/* 상태 비트가 아니며, 다른 엔터티에서
			 * 사적으로 할당할 수 있는 첫 번째 비트
			 */
};

추가 업데이트 예정

4. 입출력 스케줄러

책에 기재된 2.6버전 커널에서는 총 4개의 입출력 스케줄러에 대해 나와있으나 6.6.7버전 커널에서의 입출력 스케줄러는 아래와 같다.

1) MQ deadline I/O scheduler

멀티큐(MQ) 환경에서 사용 가능한 데드라인 I/O 스케줄러입니다.

2) Kyber I/O scheduler

낮은 오버헤드와 고속 장치에 적합하며, 읽기와 동기화된 쓰기의 목표 대기 시간을 설정하고 큐의 깊이를 자동 조정합니다.

3) BFQ(Budget Fair Queueing) I/O scheduler

프로세스별로 가중치를 기준으로 디바이스 대역폭을 배분합니다. 대화형 애플리케이션과 소프트 실시간 애플리케이션에 낮은 대기 시간을 보장합니다.

추가 업데이트 예정

참고문헌

  • 리눅스 커널 심층분석 (에이콘 임베디드 시스템프로그래밍 시리즈 33, 로버트 러브 저자(글) · 황정동 번역)
  • 리눅스 커널 6.6.7 버전
This post is licensed under CC BY 4.0 by the author.