![내 IO 요청 크기가 약 512K로 제한되는 이유는 무엇입니까?](https://linux55.com/image/156984/%EB%82%B4%20IO%20%EC%9A%94%EC%B2%AD%20%ED%81%AC%EA%B8%B0%EA%B0%80%20%EC%95%BD%20512K%EB%A1%9C%20%EC%A0%9C%ED%95%9C%EB%90%98%EB%8A%94%20%EC%9D%B4%EC%9C%A0%EB%8A%94%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F.png)
나는 /dev/sda
읽기에 1MiB 블록 크기를 사용합니다. Linux는 IO 요청을 제한하는 것 같습니다.512KiB평균 크기는 512KiB입니다. 여기서 무슨 일이 일어나고 있는 걸까요? 이 동작에 대한 구성 옵션이 있습니까?
$ sudo dd iflag=direct if=/dev/sda bs=1M of=/dev/null status=progress
1545601024 bytes (1.5 GB, 1.4 GiB) copied, 10 s, 155 MB/s
1521+0 records in
1520+0 records out
...
내 dd
명령이 실행 되면 rareq-sz
512입니다.
희귀 크기 장치에 대한 읽기 요청의 평균 크기(KB)입니다.
$ iostat -d -x 3
...
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
sda 309.00 0.00 158149.33 0.00 0.00 0.00 0.00 0.00 5.24 0.00 1.42 511.81 0.00 1.11 34.27
dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
dm-3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
...
커널 버전은 5.1.15-300.fc30.x86_64
.is max_sectors_kb
1280입니다.
$ cd /sys/class/block/sda/queue
$ grep -H . max_sectors_kb max_hw_sectors_kb max_segments max_segment_size optimal_io_size logical_block_size chunk_sectors
max_sectors_kb:1280
max_hw_sectors_kb:32767
max_segments:168
max_segment_size:65536
optimal_io_size:0
logical_block_size:512
chunk_sectors:0
기본적으로 BFQ I/O 스케줄러를 사용합니다. 나는 또한 나중에 테스트를 반복해 보았습니다 echo 0 | sudo tee wbt_lat_usec
. 그런 다음 나중에 테스트를 반복해 보았습니다 echo mq-deadline|sudo tee scheduler
. 결과는 여전히 동일합니다.
WBT 외에도 두 I/O 스케줄러 모두 기본 설정을 사용했습니다. 예를 들어 500 mq-deadline
은 iosched/read_expire
0.5초에 해당합니다.
마지막 테스트(mq-deadline, WBT 비활성화) 중에 다음을 실행했습니다 btrace /dev/sda
. 모든 요청이 서로 다른 두 부분으로 분할된 것으로 나타났습니다.
8,0 0 3090 5.516361551 15201 Q R 6496256 + 2048 [dd]
8,0 0 3091 5.516370559 15201 X R 6496256 / 6497600 [dd]
8,0 0 3092 5.516374414 15201 G R 6496256 + 1344 [dd]
8,0 0 3093 5.516376502 15201 I R 6496256 + 1344 [dd]
8,0 0 3094 5.516388293 15201 G R 6497600 + 704 [dd]
8,0 0 3095 5.516388891 15201 I R 6497600 + 704 [dd]
8,0 0 3096 5.516400193 733 D R 6496256 + 1344 [kworker/0:1H]
8,0 0 3097 5.516427886 733 D R 6497600 + 704 [kworker/0:1H]
8,0 0 3098 5.521033332 0 C R 6496256 + 1344 [0]
8,0 0 3099 5.523001591 0 C R 6497600 + 704 [0]
X——분할[소프트웨어] raid 또는 장치 매퍼 설정에서 들어오는 I/O는 장치 또는 내부 영역에 걸쳐 있을 수 있으며 서비스를 위해 더 작은 부분으로 분할되어야 합니다. 이는 부적절한 raid/dm 장치 설정으로 인한 성능 문제를 나타낼 수 있지만 정상적인 경계 조건의 일부일 수도 있습니다. dm은 특히 많은 I/O를 복제하는 데 좋지 않습니다.
무시해야 할 것들iostat
이 %util
번호는 무시하세요. 이번 버전에서는 깨졌습니다. (`dd`가 최고 속도로 실행되고 있지만 디스크 사용률은 20%에 불과합니다. 왜?)
나아이디어 aqu-sz
또한 영향을 받음%util을 기반으로 하기 때문에. 내 생각에는 이것이 여기 크기의 약 3배(100/34.27)라는 뜻이라고 생각합니다.
이 svtm
번호는 무시하세요. "경고! 이 필드를 더 이상 신뢰하지 마십시오. 이 필드는 향후 sysstat 릴리스에서 제거될 예정입니다."
답변1
내 IO 요청 크기가 약 512K로 제한되는 이유는 무엇입니까?
I/O는 커밋된 방식과 도달한 다양한 제한(이 경우)으로 인해 /sys/block/sda/queue/max_segments
"대략" 512KiB로 제한되어 있다고 생각합니다. 질문자는 blktrace
우리가 이 미스터리를 추측할 수 있도록 다양한 보조 정보(커널 버전 및 출력 등)를 제공하는 데 시간을 투자했으니, 제가 어떻게 이런 결론에 도달했는지 살펴보겠습니다.
왜 […]는 다음으로 제한됩니다.~에 대한512K?
핵심은 질문자가 제목에 "about"을 주의 깊게 언급했다는 점에 주목하는 것입니다. 출력 결과를 보면 iostat
512KiB의 값을 찾아야 한다고 생각하게 됩니다.
Device [...] aqu-sz rareq-sz wareq-sz svctm %util
sda [...] 1.42 511.81 0.00 1.11 34.27
blktrace
(경유)는 blkparse
몇 가지 정확한 값을 제공합니다.
8,0 0 3090 5.516361551 15201 Q R 6496256 + 2048 [dd]
8,0 0 3091 5.516370559 15201 X R 6496256 / 6497600 [dd]
8,0 0 3092 5.516374414 15201 G R 6496256 + 1344 [dd]
8,0 0 3093 5.516376502 15201 I R 6496256 + 1344 [dd]
8,0 0 3094 5.516388293 15201 G R 6497600 + 704 [dd]
8,0 0 3095 5.516388891 15201 I R 6497600 + 704 [dd]
(일반적으로 단일 섹터 크기는 512바이트일 것으로 예상합니다.) 따라서 dd
크기 2048 섹터(1MiByte)의 섹터 6496256의 읽기 I/O는 두 부분으로 분할됩니다. 하나는 섹터 6496256 1344 섹터에서 시작하고 다른 하나는 1344 섹터를 읽습니다. 섹터 6496256에서 시작하고 섹터 6497600에서 시작하는 704 섹터입니다. 그래서분할 전 요청된 최대 크기는 1024개 섹터(512KiB)보다 약간 큽니다....근데 왜?
질문자가 언급했습니다 5.1.15-300.fc30.x86_64
.Google 검색 Linux 분할 블록 I/O 커널나타나다Linux 장치 드라이버, 3판의 "16장. 블록 드라이버"그리고 언급
[...] 여러 장치에 제출하기 위해
bio_split
파일을 청크로 분할하는 데 사용할 수 있는 호출bio
bio
s를 다른 장치로 보내려고 하기 때문에(md나 장치 매퍼가 할 수 있는 방식으로) 분할하지는 않지만 여전히 탐색할 영역을 제공합니다. 수색LXR의 5.1.15 Linux 커널 소스 코드bio_split
파일에 대한 링크가 포함되어 있습니다.block/blk-merge.c
. 이 파일에는 다음이 포함되어 있습니다.blk_queue_split()
함수 호출을 위한 비특수 I/Oblk_bio_segment_split()
.
(휴식을 갖고 LXR을 탐색하고 싶다면 지금이 좋은 때입니다. 아래에서 계속 조사하고 더 간결하게 노력하겠습니다)
blk_bio_segment_split()
변수의 max_sectors
정렬에서 반환된 최종 값blk_max_size_offset()
보고 q->limits.chunk_sectors
0이면 반환합니다 q->limits.max_sectors
. 클릭하면 max_sectors
그것이 어떻게 max_sectors_kb
파생되는지 확인할 수 있습니다.queue_max_sectors_store()
이 시간은block/blk-sysfs.c
. blk_bio_segment_split()
로 돌아가서 max_segs
변수는 다음에서 옵니다.queue_max_segments()
Return q->limits.max_segments
계속해서 blk_bio_segment_split()
다음을 볼 수 있습니다.
bio_for_each_bvec(bv, bio, iter) {
~에 따르면block/biovecs.txt
우리는 다중 페이지 bvec을 반복하고 있습니다.
if (sectors + (bv.bv_len >> 9) > max_sectors) {
/*
* Consider this a new segment if we're splitting in
* the middle of this vector.
*/
if (nsegs < max_segs &&
sectors < max_sectors) {
/* split in the middle of bvec */
bv.bv_len = (max_sectors - sectors) << 9;
bvec_split_segs(q, &bv, &nsegs,
&seg_size,
&front_seg_size,
§ors, max_segs);
}
goto split;
}
따라서 I/O 크기가 (질문자의 경우 1280KiB)보다 크면 max_sectors_kb
분할됩니다(사용 가능한 세그먼트 및 섹터 공간이 있는 경우 분할하기 전에 현재 I/O를 최대한 많이 채웁니다). 부분으로 나누고 가능한 한 많이 추가하세요). 그러나 질문자의 경우 I/O는 "단지" 1MiB로 1280KiB 미만이므로 해당 경우에는 해당되지 않습니다. 더 아래에서 볼 수 있습니다.
if (bvprvp) {
if (seg_size + bv.bv_len > queue_max_segment_size(q))
goto new_segment;
[...]
queue_max_segment_size()
반품 q->limits.max_segment_size
. 이전에 본 것( if (sectors + (bv.bv_len >> 9) > max_sectors)
) bv.bv_len
이 바이트 단위라는 점을 고려하면(다른 이유는 무엇입니까? 512로 나누어야 할까요?) 질문자는 /sys/block/sda/queue/max_segment_size
그것이 65336이라고 말했습니다. 그 가치가 무엇인지 알면 bv.bv_len
...
[...]
new_segment:
if (nsegs == max_segs)
goto split;
bvprv = bv;
bvprvp = &bvprv;
if (bv.bv_offset + bv.bv_len <= PAGE_SIZE) {
nsegs++;
seg_size = bv.bv_len;
sectors += bv.bv_len >> 9;
if (nsegs == 1 && seg_size > front_seg_size)
front_seg_size = seg_size;
} else if (bvec_split_segs(q, &bv, &nsegs, &seg_size,
&front_seg_size, §ors, max_segs)) {
goto split;
}
}
do_split = false;
따라서 각각에 대해 bv
단일 페이지인지 다중 페이지 bvec인지 확인합니다(크기가 <=인지 확인하여 PAGE_SIZE
). 단일 페이지 bvec인 경우 세그먼트 수를 늘리고 일부 장부를 수행합니다. 다중 페이지 bvec인 경우 더 작은 세그먼트로 분할해야 하는지 확인합니다(코드는bvec_split_segs()
비교를 수행합니다 get_max_segment_size()
. 이 경우 세그먼트를 /sys/block/sda/queue/max_segment_size
64KiB(이전에는 65336에 대해 이야기했음)보다 크지 않지만 168( max_segs
)개 세그먼트를 넘지 않는 여러 세그먼트로 분할한다는 의미입니다. bvec_split_segs()
세그먼트 제한에 도달했지만 모든 길이가 포함되지 않은 경우 bv
로 이동합니다 . 그러나 1024/64 = 16개의 세그먼트만 생성한다고 split
가정하면 결국 1MiB 미만의 I/O를 커밋할 필요가 없습니다. goto split
질문자가 I/O를 통해 이동한 경로가 아닙니다.
거꾸로 외삽하여 "단일 페이지 크기의 세그먼트만" 가정하면 이는 bv.bv_offset + bv.bv_len
<= 4096을 추론할 수 있음을 의미합니다.bv_offset
는unsigned int
그러면 이는 0 <= bv.bv_len
<= 4096을 의미합니다. 따라서 우리는 goto new_segment
조건부 선도를 before로 취하지 않는다는 것을 추론할 수도 있습니다 . 그런 다음 원래 Biovec에는 1024/4 = 256개의 세그먼트가 있어야 한다는 결론을 내립니다. 256 > 168 그래서 우리는split
다음 으로 건너뛰기new_segment
결과적으로 하나의 168세그먼트 I/O와 또 다른 88세그먼트 I/O가 생성됩니다. 168 * 4096 = 688128바이트, 88 * 4096 = 360448바이트 그런데 그래서 어쩌죠? 훌륭한:
688128 / 512 = 1344
360448 / 512 = 704
출력에 표시되는 숫자는 다음과 같습니다 blktrace
.
[...] R 6496256 + 2048 [dd]
[...] R 6496256 / 6497600 [dd]
[...] R 6496256 + 1344 [dd]
[...] R 6496256 + 1344 [dd]
[...] R 6497600 + 704 [dd]
[...] R 6497600 + 704 [dd]
따라서 dd
제가 사용하도록 제안하는 명령줄은 I/O가 단일 페이지 bvec를 형성하도록 하고 최대 세그먼트 수에 도달했으므로 다음 경계에서 I/O 분할이 발생합니다.672KB모든 I/O에 대해.
다중 페이지 bvec을 생성하기 위해 I/O를 다르게(예: 버퍼링된 I/O를 통해) 제출하면 다른 분할 지점이 표시될 것이라고 생각됩니다.
이 동작에 대한 구성 옵션이 있습니까?
순서 지정 - /sys/block/<block device>/queue/max_sectors_kb
블록 계층을 통해 제출된 일반 I/O가 분할되기 전에 도달할 수 있는 최대 크기에 대한 제어이지만 이는 많은 기준 중 하나일 뿐입니다. 다른 제한(예: 최대 세그먼트)에 도달하면 블록 기반 I /O는 더 작은 크기로 분할할 수 있습니다. 또한 원시 SCSI 명령을 사용하는 경우 /sys/block/<block device>/queue/max_hw_sectors_kb
최대 크기까지 I/O를 제출할 수 있지만 그렇게 하면 블록 계층을 우회하게 되어 더 큰 I/O가 거부됩니다.
사실 넌 할 수 있어Ilya Dryomov는 이러한 max_segments
제한 사항을 설명합니다.2015년 6월 Ceph 사용자 스레드에서 "대형 IO를 작은 IO로 분할하는 krbd" 및나중에 rbd
기기 수리(어느자체는 나중에 복원되었습니다).
위 내용에 대한 추가 확인은 "2MB가 512KB가 되면커널 블록 계층 관리자 Jens Axboe가 작성한 "Device Limits" 섹션에는 최대 세그먼트 제한을 더 간결하게 다루는 섹션이 있습니다.