AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 529529
Accepted
sourcejedi
sourcejedi
Asked: 2019-07-12 02:51:19 +0800 CST2019-07-12 02:51:19 +0800 CST 2019-07-12 02:51:19 +0800 CST

Por que o tamanho das minhas solicitações de E/S está sendo limitado a cerca de 512K?

  • 772

Eu li /dev/sdausando um tamanho de bloco de 1MiB. O Linux parece limitar as solicitações de IO a512 KiBum tamanho médio de 512KiB. O que esta acontecendo aqui? Existe uma opção de configuração para este comportamento?

$ 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
...

Enquanto meu ddcomando está em execução, rareq-szé 512.

rareq-sz O tamanho médio (em kilobytes) das solicitações de leitura que foram emitidas para o dispositivo.

--man iostat

$ 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
...

A versão do kernel é 5.1.15-300.fc30.x86_64. 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

Por padrão eu uso o agendador de E/S BFQ. Eu também tentei repetir o teste depois echo 0 | sudo tee wbt_lat_usec. Eu também tentei repetir o teste depois echo mq-deadline|sudo tee scheduler. Os resultados permaneceram os mesmos.

Além do WBT, usei as configurações padrão para ambos os agendadores de E/S. Por exemplo, para mq-deadline, iosched/read_expireé 500, o que equivale a meio segundo.

Durante o último teste (mq-deadline, WBT desativado), executei btrace /dev/sda. Ele mostra que todas as solicitações foram divididas em duas metades desiguais:

  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 -- split Em [software] raid ou configurações de mapeador de dispositivo, um i/o de entrada pode abranger um dispositivo ou zona interna e precisa ser dividido em pedaços menores para serviço. Isso pode indicar um problema de desempenho devido a uma configuração ruim desse dispositivo raid/dm, mas também pode ser apenas parte das condições normais de limite. dm é notavelmente ruim nisso e clonará muitas E/S.

--man blkparse

Coisas para ignorar emiostat

Ignore o %utilnúmero. Está quebrado nesta versão. ( `dd` está rodando a toda velocidade, mas só vejo 20% de utilização do disco. Por quê? )

Eu pensei que aqu-sz também é afetado por ser baseado em %util . Embora eu achasse que isso significava que seria cerca de três vezes maior aqui (100/34,27).

Ignore o svtmnúmero. "Aviso! Não confie mais neste campo. Este campo será removido em uma versão futura do sysstat."

linux iostat
  • 1 1 respostas
  • 6010 Views

1 respostas

  • Voted
  1. Best Answer
    Anon
    2019-08-05T09:54:35+08:002019-08-05T09:54:35+08:00

    Por que o tamanho das minhas solicitações de E/S está sendo limitado a cerca de 512K?

    Suponho que a E/S está sendo limitada a "cerca de" 512 KiB devido à maneira como está sendo enviada e vários limites sendo atingidos (neste caso /sys/block/sda/queue/max_segments). O questionador teve tempo para incluir várias informações secundárias (como a versão do kernel e a blktracesaída) que nos permite adivinhar esse mistério, então vamos ver como cheguei a essa conclusão.

    Por que [...] limitado, a cerca de 512K?

    É fundamental observar que o questionador disse cuidadosamente "sobre" no título. Enquanto a iostatsaída nos faz pensar que devemos procurar valores de 512 KiB:

    Device         [...] aqu-sz rareq-sz wareq-sz  svctm  %util
    sda            [...]   1.42   511.81     0.00   1.11  34.27
    

    o blktrace(via blkparse) nos dá alguns valores exatos:

      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]
    

    (Normalmente, esperamos que um único setor tenha 512 bytes de tamanho) Portanto, a E/S de leitura do ddsetor 6496256 que foi dimensionado para 2048 setores (1 MiByte) foi dividida em duas partes - uma lida começando no setor 6496256 para 1344 setores e outra leia começando no setor 6497600 para 704 setores. Portanto, o tamanho máximo de uma solicitação antes de ser dividida é um pouco mais de 1024 setores (512 KiB) ... mas por quê?

    O questionador menciona uma versão do kernel do 5.1.15-300.fc30.x86_64. Fazer uma pesquisa no Google por linux split block i/o kernel mostra o "Capítulo 16. Block Drivers" de Linux Device Drivers, 3rd Edition e que menciona

    uma bio_splitchamada que pode ser usada para dividir um bioem vários pedaços para envio a mais de um dispositivo

    Embora não estejamos dividindo bios porque pretendemos enviá-los para dispositivos diferentes (da forma como md ou mapeador de dispositivos), isso ainda nos dá uma área para explorar. A pesquisa na fonte do kernel Linux 5.1.15 do LXRbio_split inclui um link para o arquivo block/blk-merge.c. Dentro desse arquivo existe blk_queue_split()e para I/Os não especiais que a função chama blk_bio_segment_split().

    (Se você quiser fazer uma pausa e explorar o LXR, agora é um bom momento. Continuarei a investigação abaixo e tentarei ser mais conciso daqui para frente)

    Na blk_bio_segment_split()variável max_sectors, em última análise, vem alinhando o valor retornado blk_max_size_offset()e que olha q->limits.chunk_sectorse se for zero, então apenas retorna q->limits.max_sectors. Clicando ao redor, vemos como max_sectorsé derivado de max_sectors_kbem queue_max_sectors_store()que está emblock/blk-sysfs.c . De volta blk_bio_segment_split(), a max_segsvariável vem de queue_max_segments()onde retorna q->limits.max_segments. Continuando para baixo blk_bio_segment_split(), vemos o seguinte:

        bio_for_each_bvec(bv, bio, iter) {
    

    De acordo com block/biovecs.txtestamos iterando sobre bvec de várias páginas.

            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,
                            &sectors, max_segs);
                }
                goto split;
            }
    

    Portanto, se o tamanho de E/S for maior que max_sectors_kb(que é 1280 KiB no caso do questionador), ele será dividido (se houver segmentos sobressalentes e espaço de setor, preencheremos a E/S atual o máximo possível antes de dividir por dividindo-o em segmentos e somando o máximo possível). Mas no caso do questionador a E/S é "apenas" 1 MiB que é menor que 1280 KiB então não estamos neste caso... Mais abaixo vemos:

            if (bvprvp) {
                if (seg_size + bv.bv_len > queue_max_segment_size(q))
                    goto new_segment;
            [...]
    

    queue_max_segment_size()retorna q->limits.max_segment_size. Dado o que vimos anteriormente ( if (sectors + (bv.bv_len >> 9) > max_sectors)) bv.bv_lenserá em termos de bytes (caso contrário, por que temos que dividi-lo por 512?) e o questionador disse que /sys/block/sda/queue/max_segment_sizeera 65336. Se soubéssemos qual era o valor 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, &sectors, max_segs)) {
                goto split;
            }
        }
    
        do_split = false;
    

    Portanto, para cada um bv, verificamos se é um bvec de página única ou de várias páginas (verificando se seu tamanho é <= PAGE_SIZE). Se for um bvec de página única, adicionamos um à contagem de segmentos e fazemos alguma contabilidade. Se for um bvec de várias páginas, verificamos se ele precisa ser dividido em segmentos menores (o código em bvec_split_segs()faz comparações com o get_max_segment_size()que, neste caso, significa que ele dividirá o segmento em vários segmentos não maiores que 64 KiB (anteriormente dissemos que /sys/block/sda/queue/max_segment_sizeera 65336), mas há não deve ter mais de 168 ( max_segs) segmentos. Se bvec_split_segs()atingiu o limite de segmento e não cobriu todo o bvcomprimento do , então pularemos para split. No entanto, SE assumirmos que tomamos ogoto splitcaso só geremos 1024 / 64 = 16 segmentos, então, no final das contas, não teríamos que enviar menos de 1 MiB de E/S, então esse não é o caminho pelo qual a E/S do questionador passou ...

    Trabalhando para trás, se assumirmos que havia "apenas segmentos de tamanho de página única", isso significa que podemos deduzir bv.bv_offset + bv.bv_len<= 4096 e como bv_offseté umunsigned int então significa 0 <= bv.bv_len<= 4096. Assim, também podemos deduzir que nunca pegamos o corpo de condição que levou goto new_segmentantes. Em seguida, concluímos que o biovec original deve ter tido 1024 / 4 = 256 segmentos. 256 > 168 então teríamos causado um salto para splitlogo apósnew_segment gerando uma E/S de 168 segmentos e outra de 88 segmentos. 168 * 4096 = 688128 bytes, 88 * 4096 = 360448 bytes, mas e daí? Nós iremos:

    688128 / 512 = 1344

    360448 / 512 = 704

    Quais são os números que vimos na blktracesaída:

    [...]   R 6496256 + 2048 [dd]
    [...]   R 6496256 / 6497600 [dd]
    [...]   R 6496256 + 1344 [dd]
    [...]   R 6496256 + 1344 [dd]
    [...]   R 6497600 + 704 [dd]
    [...]   R 6497600 + 704 [dd]
    

    Então, proponho que a ddlinha de comando que você está usando está fazendo com que a E/S seja formada em bvecs de página única e, como o número máximo de segmentos está sendo alcançado, a divisão de E/S acontece em um limite de 672 KiB para cada I /O.

    Suspeito que se tivéssemos enviado E/S de uma maneira diferente (por exemplo, via E/S em buffer) de modo que bvecs de várias páginas fossem gerados, teríamos visto um ponto de divisão diferente.

    Existe uma opção de configuração para este comportamento?

    Sort of - /sys/block/<block device>/queue/max_sectors_kbé um controle sobre o tamanho máximo que uma E/S normal submetida através da camada de bloco pode ter antes de ser dividida, mas é apenas um dos muitos critérios - se outros limites forem atingidos (como o máximo de segmentos), então um A E/S baseada em bloco pode ser dividida em um tamanho menor. Além disso, se você usar comandos SCSI brutos, é possível enviar uma E/S de /sys/block/<block device>/queue/max_hw_sectors_kbtamanho maior, mas você estará ignorando a camada de bloco e E/S maiores serão rejeitadas.

    Na verdade, você pode Ilya Dryomov descrevendo essa max_segmentslimitação em um tópico Ceph Users de junho de 2015 "krbd dividindo E/S grandes em E/S menores" e uma correção foi feita posteriormente para rbddispositivos (que foi corrigida posteriormente ).

    A validação adicional do acima vem através de um documento intitulado " Quando 2 MB se transforma em 512 KB " pelo mantenedor da camada de bloco do kernel Jens Axboe, que tem uma seção intitulada "Limitações do dispositivo" cobrindo a limitação máxima de segmentos de forma mais sucinta.

    • 7

relate perguntas

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • Inicie/pare o serviço systemd usando o atalho de teclado [fechado]

  • Necessidade de algumas chamadas de sistema

  • astyle não altera a formatação do arquivo de origem

  • Passe o sistema de arquivos raiz por rótulo para o kernel do Linux

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve