O provedor io disponibiliza os testes relacionados à entrada e saída em disco. O provedor io possibilita a rápida exploração do comportamento observado através de ferramentas de monitoração de E/S como iostat(1M). Por exemplo, ao usar o provedor io, você pode compreender a E/S por dispositivo, por tipo de E/S, por tamanho de E/S, por processo, por nome de aplicativo, por nome de arquivo ou por deslocamento de arquivo.
Os testes io estão descritos na Tabela 27–1.
Tabela 27–1 Testes io
Teste |
Descrição |
---|---|
start |
Teste que é acionado quando uma solicitação de E/S está para ser feita seja em um dispositivo periférico ou em um servidor NFS. A bufinfo_t correspondente à solicitação de E/S é apontada por args[0]. A devinfo_t do dispositivo para o qual a E/S está sendo emitida é apontada por args[1]. A fileinfo_t do arquivo que corresponde à solicitação de E/S é apontada por args[2]. Observe que a disponibilidade das informações do arquivo depende do sistema de arquivos fazer a solicitação de E/S. Consulte fileinfo_t para obter mais informações. |
done |
Teste que é acionado depois que uma solicitação de E/S tiver sido atendida. A bufinfo_t correspondente à solicitação de E/S é apontada por args[0]. O teste done é acionado depois da conclusão da E/S, mas antes do processamento da conclusão ser realizado no buffer. Como resultado, B_DONE não é definido em b_flags quando o teste done é acionado. A devinfo_t do dispositivo para o qual a E/S foi emitida é apontada por args[1]. A fileinfo_t do arquivo que corresponde à solicitação de E/S é apontada por args[2]. |
wait-start |
Teste que é acionado imediatamente antes de um segmento começar a aguardar a conclusão pendente de uma determinada solicitação de E/S. A estrutura buf(9S) correspondente à solicitação de E/S pela qual o segmento irá aguardar é apontada por args[0]. A devinfo_t do dispositivo para o qual a E/S foi emitida é apontada por args[1] . A fileinfo_t do arquivo que corresponde à solicitação de E/S é apontada por args[2]. Algum tempo depois do teste wait-start ser acionado, o teste wait-done será acionado no mesmo segmento. |
wait-done |
Teste que é acionado quando um segmento for concluído aguardando a conclusão de uma determinada solicitação de E/S. A bufinfo_t correspondente à solicitação de E/S pela qual o segmento irá aguardar é apontada por args[0]. A devinfo_t do dispositivo para o qual a E/S foi emitida é apontada por args[1]. A fileinfo_t do arquivo que corresponde à solicitação de E/S é apontada por args[2]. O teste wait-done é acionado somente depois que o teste wait-start tiver sido acionado no mesmo segmento. |
Observe que os testes io são acionados para todas as solicitações de E/S em dispositivos periféricos, e para todas as solicitações de leitura e gravação de arquivo em um servidor NFS. As solicitações por metadados de um servidor NFS, por exemplo, não acionam testes io devido a uma solicitação readdir(3C).
Os tipos de argumento dos testes io são listados na Tabela 27–2. Os argumentos são descritos na Tabela 27–1.
Tabela 27–2 Argumentos do teste io.
Teste |
args[0] |
args[1] |
args[2] |
---|---|---|---|
start |
struct buf * |
devinfo_t * |
fileinfo_t * |
done |
struct buf * |
devinfo_t * |
fileinfo_t * |
wait-start |
struct buf * |
devinfo_t * |
fileinfo_t * |
wait-done |
struct buf * |
devinfo_t * |
fileinfo_t * |
Cada teste io possui argumentos que consistem em um ponteiro para uma estrutura buf(9S), um ponteiro para devinfo_t e um ponteiro para fileinfo_t . Essas estruturas são descritas com mais detalhes nesta seção.
A estrutura bufinfo_t é a abstração que descreve uma solicitação de E/S. O buffer que corresponde a uma solicitação de E/S é apontado por args[0] nos testes start, done, wait-start e wait-done. A definição da estrutura bufinfo_t é a seguinte:
typedef struct bufinfo { int b_flags; /* flags */ size_t b_bcount; /* number of bytes */ caddr_t b_addr; /* buffer address */ uint64_t b_blkno; /* expanded block # on device */ uint64_t b_lblkno; /* block # on device */ size_t b_resid; /* # of bytes not transferred */ size_t b_bufsize; /* size of allocated buffer */ caddr_t b_iodone; /* I/O completion routine */ dev_t b_edev; /* extended device */ } bufinfo_t;
O membro b_flags indica o estado do buffer de E/S e consiste em um operador bit a bit de diferentes valores de estado. Os valores de estado válidos estão na Tabela 27–3.
Tabela 27–3 Valores de b_flags
B_DONE |
Indica que a transferência de dados foi concluída. |
B_ERROR |
Indica um erro de transferência de E/S. É definido junto com o campo b_error. |
B_PAGEIO |
Indica que o buffer está sendo usado em uma solicitação de E/S paginada. Veja a descrição do campo b_addr para obter mais informações. |
B_PHYS |
Indica que o buffer está sendo usado para E/S física (direta) para uma área de dados do usuário. |
B_READ |
Indica que os dados devem ser lidos a partir do dispositivo periférico para a memória principal. |
B_WRITE |
Indica que os dados devem ser transferidos a partir da memória principal para o dispositivo periférico. |
B_ASYNC |
A solicitação de E/S é assíncrona, e não será aguardada. Os testes wait-start e wait-done não são acionados em solicitações de E/S assíncronas. Observe que algumas E/Ss direcionadas para ser assíncronas podem não ter B_ASYNC definida: o subsistema de E/S assíncrona pode implementar a solicitação assíncrona fazendo com que um segmento de trabalho separado realize uma operação de E/S síncrona. |
O campo b_bcount é o número de bytes a ser transferido como parte da solicitação de E/S.
O campo b_addr é o endereço virtual da solicitação de E/S, a menos que B_PAGEIO esteja definida. O endereço é um endereço virtual do kernel a menos que B_PHYS esteja definida. Nesse caso, é um endereço virtual do usuário. Se B_PAGEIO estiver definida, o campo b_addr contém dados privados do kernel. Exatamente um de B_PHYS e B_PAGEIO pode ser definido, ou nenhum dos sinalizadores será definido.
O campo b_lblkno identifica qual bloco lógico no dispositivo será acessado. O mapeamento de um bloco lógico para um bloco físico (como o cilindro, a trilha e assim por diante) é definido pelo dispositivo.
O campo b_resid é definido como o número de bytes não transferidos devido a um erro.
O campo b_bufsize contém o tamanho do buffer alocado.
O campo b_iodone identifica uma rotina específica no kernel que é chamada quando a E/S está concluída.
O campo b_error pode conter um código de erro retornado do driver no caso de um erro de E/S. b_error é definido com o conjunto de bits B_ERROR no membro b_flags.
O campo b_edev contém os números principal e secundário do dispositivo acessado. Os consumidores podem usar as sub-rotinas de D getmajor() e getminor() para extrair os números principal e secundário do dispositivo do campo b_edev.
A estrutura devinfo_t fornece informações sobre um dispositivo. A estrutura devinfo_t correspondente ao dispositivo de destino de uma E/S é apontada por args[1] nos testes start, done, wait-start e wait-done. Os membros de devinfo_t são os seguintes:
typedef struct devinfo { int dev_major; /* major number */ int dev_minor; /* minor number */ int dev_instance; /* instance number */ string dev_name; /* name of device */ string dev_statname; /* name of device + instance/minor */ string dev_pathname; /* pathname of device */ } devinfo_t;
O campo dev_major é o número principal do dispositivo. Consulte getmajor(9F) para obter mais informações.
O campo dev_minor é o número secundário do dispositivo. Consulte getminor(9F) para obter mais informações.
O campo dev_instance é o número da instância do dispositivo. A instância de um dispositivo é diferente do número secundário. O número secundário é uma abstração gerenciada pelo driver do dispositivo. O número da instância é uma propriedade do nó do dispositivo. Você pode exibir números de instância de um nó de dispositivo com prtconf(1M).
O campo dev_name é o nome do driver de dispositivo que gerencia o dispositivo. Você pode exibir nomes de driver de dispositivo com a opção -D para prtconf(1M).
O campo dev_statname é o nome do dispositivo conforme informado por iostat(1M). Esse nome também corresponde ao nome de uma estatística do kernel conforme informado por kstat(1M). Esse campo é fornecido para que um resultado iostat ou kstat anormal possa rapidamente ser correlacionado a uma atividade de E/S verdadeira.
O campo dev_pathname é o caminho total do dispositivo. Esse caminho pode ser especificado como um argumento para prtconf(1M) a fim de obter informações detalhadas sobre o dispositivo. O caminho especificado por dev_pathname inclui componentes que expressam o nó do dispositivo, o número de instância e o nó menor. Entretanto, todos esses três elementos não são necessariamente expressados no nome da estatística. Para alguns dispositivos, o nome da estatística consiste no nome do dispositivo e no número da instância. Para outros dispositivos, o nome consiste no nome do dispositivo e no número do nó secundário. Como resultado, dois dispositivos que possuam o mesmo dev_statname podem diferir no dev_pathname .
A estrutura fileinfo_t fornece informações sobre um arquivo. O arquivo ao qual uma E/S corresponde é apontado por args[2] nos testes start, done, wait-start e wait-done. A presença de informações do arquivo depende do sistema de arquivos fornecer essas informações ao distribuir solicitações de E/S. Alguns sistemas de arquivos, especialmente de terceiros, podem não fornecer essas informações. Além disso, solicitações de E/S podem surgir de um sistema de arquivos para o qual não existam informações do arquivo. Por exemplo, uma E/S para os metadados do sistema de arquivos não será associada a um arquivo. Finalmente, alguns sistemas de arquivos altamente otimizados podem agregar E/S de arquivos separados em uma única solicitação de E/S. Nesse caso, o sistema de arquivos pode fornecer as informações de arquivo seja para o arquivo que representa a maioria da E/S ou para o arquivo que representa uma parte da E/S. De forma alternativa, o sistema de arquivos pode não fornecer as informações de arquivo nesse caso.
A definição da estrutura fileinfo_t é a seguinte:
typedef struct fileinfo { string fi_name; /* name (basename of fi_pathname) */ string fi_dirname; /* directory (dirname of fi_pathname) */ string fi_pathname; /* full pathname */ offset_t fi_offset; /* offset within file */ string fi_fs; /* filesystem */ string fi_mount; /* mount point of file system */ } fileinfo_t;
O campo fi_name contém o nome do arquivo mas não inclui componentes do diretório. Se nenhuma informação do arquivo for associada a uma E/S, o campo fi_name será definido como a seqüência <none>. Em alguns casos raros, o nome de caminho associado a um arquivo pode ser desconhecido. Nesse caso, o campo fi_name será definido como a seqüência <unknown>.
O campo fi_dirname contém somente o componente de diretório do nome do arquivo. Assim como com fi_name, essa seqüência pode ser definida como <none> se nenhuma informação de arquivo estiver presente, ou <unknown> se o nome de caminho associado ao arquivo não for conhecido.
O campo fi_pathname contém o nome de caminho completo para o arquivo. Assim como com fi_name, essa seqüência pode ser definida como <none> se nenhuma informação de arquivo estiver presente, ou <unknown> se o nome de caminho associado ao arquivo não for conhecido.
O campo fi_offset contém o deslocamento no arquivo, ou -1 se a informação do arquivo não estiver presente ou se o deslocamento não estiver especificado pelo sistema de arquivos.
O script de exemplo a seguir exibe informações pertinentes a cada E/S conforme é emitida:
#pragma D option quiet BEGIN { printf("%10s %58s %2s\n", "DEVICE", "FILE", "RW"); } io:::start { printf("%10s %58s %2s\n", args[1]->dev_statname, args[2]->fi_pathname, args[0]->b_flags & B_READ ? "R" : "W"); }
O resultado da partida a frio do Acrobat Reader em um sistema de laptop x86 lembra o seguinte exemplo:
# dtrace -s ./iosnoop.d DEVICE FILE RW cmdk0 /opt/Acrobat4/bin/acroread R cmdk0 /opt/Acrobat4/bin/acroread R cmdk0 <unknown> R cmdk0 /opt/Acrobat4/Reader/AcroVersion R cmdk0 <unknown> R cmdk0 <unknown> R cmdk0 <none> R cmdk0 <unknown> R cmdk0 <none> R cmdk0 /usr/lib/locale/iso_8859_1/iso_8859_1.so.3 R cmdk0 /usr/lib/locale/iso_8859_1/iso_8859_1.so.3 R cmdk0 /usr/lib/locale/iso_8859_1/iso_8859_1.so.3 R cmdk0 <none> R cmdk0 <unknown> R cmdk0 <unknown> R cmdk0 <unknown> R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 <none> R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 <unknown> R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 <none> R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libreadcore.so.4.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 /opt/Acrobat4/Reader/intelsolaris/bin/acroread R cmdk0 <unknown> R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libAGM.so.3.0 R cmdk0 <none> R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libAGM.so.3.0 R cmdk0 /opt/Acrobat4/Reader/intelsolaris/lib/libAGM.so.3.0 R ... |
As entradas <none> no resultado indicam que a E/S não corresponde aos dados em nenhum arquivo em particular: essas E/Ss são devido a metadados de um formato ou outro. As entradas <unknown> no resultado indicam que o nome de caminho do arquivo não é conhecido. Essa situação é relativamente rara.
Você poderia tornar o script de exemplo um pouco mais sofisticado usando uma matriz de associação para controlar o tempo gasto em cada E/S, conforme mostrado no exemplo a seguir:
#pragma D option quiet BEGIN { printf("%10s %58s %2s %7s\n", "DEVICE", "FILE", "RW", "MS"); } io:::start { start[args[0]->b_edev, args[0]->b_blkno] = timestamp; } io:::done /start[args[0]->b_edev, args[0]->b_blkno]/ { this->elapsed = timestamp - start[args[0]->b_edev, args[0]->b_blkno]; printf("%10s %58s %2s %3d.%03d\n", args[1]->dev_statname, args[2]->fi_pathname, args[0]->b_flags & B_READ ? "R" : "W", this->elapsed / 10000000, (this->elapsed / 1000) % 1000); start[args[0]->b_edev, args[0]->b_blkno] = 0; }
O resultado do exemplo acima durante a conexão automática de um dispositivo de armazenamento USB em um sistema de laptop x86 inativo é mostrado no exemplo a seguir:
# dtrace -s ./iotime.d DEVICE FILE RW MS cmdk0 /kernel/drv/scsa2usb R 24.781 cmdk0 /kernel/drv/scsa2usb R 25.208 cmdk0 /var/adm/messages W 25.981 cmdk0 /kernel/drv/scsa2usb R 5.448 cmdk0 <none> W 4.172 cmdk0 /kernel/drv/scsa2usb R 2.620 cmdk0 /var/adm/messages W 0.252 cmdk0 <unknown> R 3.213 cmdk0 <none> W 3.011 cmdk0 <unknown> R 2.197 cmdk0 /var/adm/messages W 2.680 cmdk0 <none> W 0.436 cmdk0 /var/adm/messages W 0.542 cmdk0 <none> W 0.339 cmdk0 /var/adm/messages W 0.414 cmdk0 <none> W 0.344 cmdk0 /var/adm/messages W 0.361 cmdk0 <none> W 0.315 cmdk0 /var/adm/messages W 0.421 cmdk0 <none> W 0.349 cmdk0 <none> R 1.524 cmdk0 <unknown> R 3.648 cmdk0 /usr/lib/librcm.so.1 R 2.553 cmdk0 /usr/lib/librcm.so.1 R 1.332 cmdk0 /usr/lib/librcm.so.1 R 0.222 cmdk0 /usr/lib/librcm.so.1 R 0.228 cmdk0 /usr/lib/librcm.so.1 R 0.927 cmdk0 <none> R 1.189 ... cmdk0 /usr/lib/devfsadm/linkmod R 1.110 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_audio_link.so R 1.763 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_audio_link.so R 0.161 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_cfg_link.so R 0.819 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_cfg_link.so R 0.168 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_disk_link.so R 0.886 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_disk_link.so R 0.185 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_fssnap_link.so R 0.778 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_fssnap_link.so R 0.166 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_lofi_link.so R 1.634 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_lofi_link.so R 0.163 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_md_link.so R 0.477 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_md_link.so R 0.161 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_misc_link.so R 0.198 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_misc_link.so R 0.168 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_misc_link.so R 0.247 cmdk0 /usr/lib/devfsadm/linkmod/SUNW_misc_link_i386.so R 1.735 ... |
Você pode fazer várias observações sobre o mecanismo do sistema com base neste resultado. Primeiro, observe que o tempo de realização das primeiras E/Ss foi longo: aproximadamente 25 milissegundos cada. A causa dessa demora pode ter sido que o dispositivo cmdk0 teve a energia gerenciada no laptop. Segundo, observe a E/S devido ao driver scsa2usb(7D) que está sendo carregado para lidar com o dispositivo de armazenamento em massa USB. Terceiro, observe as gravações em /var/adm/messages à medida que o dispositivo é informado. Finalmente, observe a leitura dos geradores de link do dispositivo (os arquivos que terminam em link.so) , que supostamente lidam com o novo dispositivo.
O provedor io possibilita uma compreensão abrangente do resultado de iostat(1M). Suponha que você observe o resultado de iostat de forma similar ao exemplo a seguir:
extended device statistics device r/s w/s kr/s kw/s wait actv svc_t %w %b cmdk0 8.0 0.0 399.8 0.0 0.0 0.0 0.8 0 1 sd0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 sd2 0.0 109.0 0.0 435.9 0.0 1.0 8.9 0 97 nfs1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 nfs2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 0 |
Você pode usar o script iotime.d para ver estas E/Ss à medida que acontecem, conforme mostrado no exemplo a seguir:
DEVICE FILE RW MS sd2 /mnt/archives.tar W 0.856 sd2 /mnt/archives.tar W 0.729 sd2 /mnt/archives.tar W 0.890 sd2 /mnt/archives.tar W 0.759 sd2 /mnt/archives.tar W 0.884 sd2 /mnt/archives.tar W 0.746 sd2 /mnt/archives.tar W 0.891 sd2 /mnt/archives.tar W 0.760 sd2 /mnt/archives.tar W 0.889 cmdk0 /export/archives/archives.tar R 0.827 sd2 /mnt/archives.tar W 0.537 sd2 /mnt/archives.tar W 0.887 sd2 /mnt/archives.tar W 0.763 sd2 /mnt/archives.tar W 0.878 sd2 /mnt/archives.tar W 0.751 sd2 /mnt/archives.tar W 0.884 sd2 /mnt/archives.tar W 0.760 sd2 /mnt/archives.tar W 3.994 sd2 /mnt/archives.tar W 0.653 sd2 /mnt/archives.tar W 0.896 sd2 /mnt/archives.tar W 0.975 sd2 /mnt/archives.tar W 1.405 sd2 /mnt/archives.tar W 0.724 sd2 /mnt/archives.tar W 1.841 cmdk0 /export/archives/archives.tar R 0.549 sd2 /mnt/archives.tar W 0.543 sd2 /mnt/archives.tar W 0.863 sd2 /mnt/archives.tar W 0.734 sd2 /mnt/archives.tar W 0.859 sd2 /mnt/archives.tar W 0.754 sd2 /mnt/archives.tar W 0.914 sd2 /mnt/archives.tar W 0.751 sd2 /mnt/archives.tar W 0.902 sd2 /mnt/archives.tar W 0.735 sd2 /mnt/archives.tar W 0.908 sd2 /mnt/archives.tar W 0.753 |
Este resultado parece mostrar que o arquivo archives.tar está sendo lido a partir de cmdk0 (em /export/archives ) e sendo gravado no dispositivo sd2 (em /mnt). Essa existência de dois arquivos chamados archives.tar operados separadamente em paralelo parece pouco provável. Para investigar melhor, você pode agregar dispositivo, aplicativo, ID de processo e bytes transferidos, conforme mostrado no exemplo a seguir:
#pragma D option quiet io:::start { @[args[1]->dev_statname, execname, pid] = sum(args[0]->b_bcount); } END { printf("%10s %20s %10s %15s\n", "DEVICE", "APP", "PID", "BYTES"); printa("%10s %20s %10d %15@d\n", @); }
A execução deste script por alguns segundos produz um resultado similar ao exemplo a seguir:
# dtrace -s ./whoio.d ^C DEVICE APP PID BYTES cmdk0 cp 790 1515520 sd2 cp 790 1527808 |
Este resultado mostra que esta atividade é uma cópia do arquivo archives.tar de um dispositivo para outro. Essa conclusão leva a uma outra pergunta natural: um desses dispositivos é mais rápido que o outro? Qual dispositivo atua como limitador na cópia? Para responder a estas perguntas, você precisa conhecer o throughput efetivo de cada dispositivo em vez do número de bytes por segundo que cada dispositivo está transferindo. Você pode determinar o throughput com o seguinte script de exemplo:
#pragma D option quiet io:::start { start[args[0]->b_edev, args[0]->b_blkno] = timestamp; } io:::done /start[args[0]->b_edev, args[0]->b_blkno]/ { /* * We want to get an idea of our throughput to this device in KB/sec. * What we have, however, is nanoseconds and bytes. That is we want * to calculate: * * bytes / 1024 * ------------------------ * nanoseconds / 1000000000 * * But we can't calculate this using integer arithmetic without losing * precision (the denomenator, for one, is between 0 and 1 for nearly * all I/Os). So we restate the fraction, and cancel: * * bytes 1000000000 bytes 976562 * --------- * ------------- = --------- * ------------- * 1024 nanoseconds 1 nanoseconds * * This is easy to calculate using integer arithmetic; this is what * we do below. */ this->elapsed = timestamp - start[args[0]->b_edev, args[0]->b_blkno]; @[args[1]->dev_statname, args[1]->dev_pathname] = quantize((args[0]->b_bcount * 976562) / this->elapsed); start[args[0]->b_edev, args[0]->b_blkno] = 0; } END { printa(" %s (%s)\n%@d\n", @); }
A execução do script de exemplo por vários segundos produz o seguinte resultado:
sd2 (/devices/pci@0,0/pci1179,1@1d/storage@2/disk@0,0:r) value ------------- Distribution ------------- count 32 | 0 64 | 3 128 | 1 256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2257 512 | 1 1024 | 0 cmdk0 (/devices/pci@0,0/pci-ide@1f,1/ide@0/cmdk@0,0:a) value ------------- Distribution ------------- count 128 | 0 256 | 1 512 | 0 1024 | 2 2048 | 0 4096 | 2 8192 |@@@@@@@@@@@@@@@@@@ 172 16384 |@@@@@ 52 32768 |@@@@@@@@@@@ 108 65536 |@@@ 34 131072 | 0 |
O resultado mostra que sd2 é claramente o dispositivo limitador. O throughput sd2 está entre 256K/seg. e 512K/seg., enquanto cmdk0 está entregando E/S em qualquer ponto de 8 MB/segundo a mais de 64 MB/segundo. O script imprime o nome conforme visto em iostat e o caminho completo do dispositivo. Para descobrir mais sobre o dispositivo, você pode especificar o caminho do dispositivo para prtconf, conforme mostrado no exemplo a seguir:
# prtconf -v /devices/pci@0,0/pci1179,1@1d/storage@2/disk@0,0 disk, instance #2 (driver name: sd) Driver properties: name='lba-access-ok' type=boolean dev=(29,128) name='removable-media' type=boolean dev=none name='pm-components' type=string items=3 dev=none value='NAME=spindle-motor' + '0=off' + '1=on' name='pm-hardware-state' type=string items=1 dev=none value='needs-suspend-resume' name='ddi-failfast-supported' type=boolean dev=none name='ddi-kernel-ioctl' type=boolean dev=none Hardware properties: name='inquiry-revision-id' type=string items=1 value='1.04' name='inquiry-product-id' type=string items=1 value='STORAGE DEVICE' name='inquiry-vendor-id' type=string items=1 value='Generic' name='inquiry-device-type' type=int items=1 value=00000000 name='usb' type=boolean name='compatible' type=string items=1 value='sd' name='lun' type=int items=1 value=00000000 name='target' type=int items=1 value=00000000 |
Conforme os termos enfatizados indicam, este dispositivo é um dispositivo de armazenamento USB removível.
Os exemplos nesta seção exploraram todas as solicitações de E/S. Entretanto, você pode estar interessado em somente um tipo de solicitação. O exemplo a seguir controla os diretórios nos quais as gravações estão ocorrendo, junto com os aplicativos que estão realizando as gravações:
#pragma D option quiet io:::start /args[0]->b_flags & B_WRITE/ { @[execname, args[2]->fi_dirname] = count(); } END { printf("%20s %51s %5s\n", "WHO", "WHERE", "COUNT"); printa("%20s %51s %5@d\n", @); }
A execução deste script de exemplo em uma carga de área de trabalho por um período de tempo produz alguns resultados interessantes, conforme mostrado no seguinte resultado de exemplo:
# dtrace -s ./whowrite.d ^C WHO WHERE COUNT su /var/adm 1 fsflush /etc 1 fsflush / 1 fsflush /var/log 1 fsflush /export/bmc/lisa 1 esd /export/bmc/.phoenix/default/78cxczuy.slt/Cache 1 fsflush /export/bmc/.phoenix 1 esd /export/bmc/.phoenix/default/78cxczuy.slt 1 vi /var/tmp 2 vi /etc 2 cat <none> 2 bash / 2 vi <none> 3 xterm /var/adm 3 fsflush /export/bmc 7 MozillaFirebird <none> 8 vim /export/bmc 9 MozillaFirebird /export/bmc 10 fsflush /var/adm 11 devfsadm /dev 14 ksh <none> 71 ksh /export/bmc 71 fsflush /export/bmc/.phoenix/default/78cxczuy.slt 119 MozillaFirebird /export/bmc/.phoenix/default/78cxczuy.slt 119 fsflush <none> 211 MozillaFirebird /export/bmc/.phoenix/default/78cxczuy.slt/Cache 591 fsflush /export/bmc/.phoenix/default/78cxczuy.slt/Cache 666 sched <none> 2385 |
Como o resultado indica, virtualmente todas as gravações são associadas ao cache do Mozilla Firebird. As gravações rotuladas <none> são provavelmente devido a gravações associadas ao log UFS, gravações que são, elas mesmas, induzidas por outras gravações no sistema de arquivos. Consulte ufs(7FS) para obter detalhes sobre log. Este exemplo mostra como usar o provedor io para descobrir um problema em uma camada mais elevada de software. Neste caso, o script revelou um problema de configuração: o navegador da Web induziria muito menos E/S (e provavelmente nenhuma) se o seu cache estivesse em um diretório em um sistema de arquivos tmpfs(7FS).
Os exemplos anteriores usaram somente os testes start e done. Você pode usar os testes wait-start e wait-done para compreender por que os aplicativos são bloqueados para E/S e por quanto tempo. O seguinte script de exemplo usa os testes de io e de sched (consulte o Capítulo 26Provedor sched) para derivar tempo de CPU em comparação ao tempo de espera de E/S do software StarOffice:
#pragma D option quiet sched:::on-cpu /execname == "soffice.bin"/ { self->on = vtimestamp; } sched:::off-cpu /self->on/ { @time["<on cpu>"] = sum(vtimestamp - self->on); self->on = 0; } io:::wait-start /execname == "soffice.bin"/ { self->wait = timestamp; } io:::wait-done /self->wait/ { @io[args[2]->fi_name] = sum(timestamp - self->wait); @time["<I/O wait>"] = sum(timestamp - self->wait); self->wait = 0; } END { printf("Time breakdown (milliseconds):\n"); normalize(@time, 1000000); printa(" %-50s %15@d\n", @time); printf("\nI/O wait breakdown (milliseconds):\n"); normalize(@io, 1000000); printa(" %-50s %15@d\n", @io); }
A execução do script de exemplo durante uma partida a frio do software StarOffice produz o seguinte resultado:
Time breakdown (milliseconds): <on cpu> 3634 <I/O wait> 13114 I/O wait breakdown (milliseconds): soffice.tmp 0 Office 0 unorc 0 sbasic.cfg 0 en 0 smath.cfg 0 toolboxlayout.xml 0 sdraw.cfg 0 swriter.cfg 0 Linguistic.dat 0 scalc.cfg 0 Views.dat 0 Store.dat 0 META-INF 0 Common.xml.tmp 0 afm 0 libsimreg.so 1 xiiimp.so.2 3 outline 4 Inet.dat 6 fontmetric 6 ... libucb1.so 44 libj641si_g.so 46 libX11.so.4 46 liblng641si.so 48 swriter.db 53 libwrp641si.so 53 liblocaledata_ascii.so 56 libi18npool641si.so 65 libdbtools2.so 69 ofa64101.res 74 libxcr641si.so 82 libucpchelp1.so 83 libsot641si.so 86 libcppuhelper3C52.so 98 libfwl641si.so 100 libsb641si.so 104 libcomphelp2.so 105 libxo641si.so 106 libucpfile1.so 110 libcppu.so.3 111 sw64101.res 114 libdb-3.2.so 119 libtk641si.so 126 libdtransX11641si.so 127 libgo641si.so 132 libfwe641si.so 150 libi18n641si.so 152 libfwi641si.so 154 libso641si.so 173 libpsp641si.so 186 libtl641si.so 189 <unknown> 189 libucbhelper1C52.so 195 libutl641si.so 213 libofa641si.so 216 libfwk641si.so 229 libsvl641si.so 261 libcfgmgr2.so 368 libsvt641si.so 373 libvcl641si.so 741 libsvx641si.so 885 libsfx641si.so 993 <none> 1096 libsw641si.so 1365 applicat.rdb 1580 |
Como este resultado mostra, grande parte do tempo de partida a frio do StarOffice é devido à espera por E/S. (13,1 segundos de espera por E/S em comparação a 3,6 segundos na CPU.) A execução do script em uma partida a quente do software StarOffice revela que o armazenamento em cache da página eliminou o tempo de E/S, conforme mostrado no seguinte resultado de exemplo:
Time breakdown (milliseconds): <I/O wait> 0 <on cpu> 2860 I/O wait breakdown (milliseconds): temp 0 soffice.tmp 0 <unknown> 0 Office 0 |
O resultado da partida a frio mostra que o arquivo applicat.rdb contabiliza mais tempo de espera por E/S que qualquer outro arquivo. Esse resultado provavelmente é devido à grande quantidade de E/Ss no arquivo. Para explorar as E/Ss realizadas nesse arquivo, você pode usar o seguinte script de D:
io:::start /execname == "soffice.bin" && args[2]->fi_name == "applicat.rdb"/ { @ = lquantize(args[2]->fi_offset != -1 ? args[2]->fi_offset / (1000 * 1024) : -1, 0, 1000); }
Este script usa o campo fi_offset da estrutura fileinfo_t para compreender quais partes do arquivo estão sendo acessadas, na granularidade de um megabyte. A execução desse script durante uma partida a frio do software StarOffice produz um resultado similar ao seguinte exemplo:
# dtrace -s ./applicat.d dtrace: script './applicat.d' matched 4 probes ^C value ------------- Distribution ------------ count < 0 | 0 0 |@@@ 28 1 |@@ 17 2 |@@@@ 35 3 |@@@@@@@@@ 72 4 |@@@@@@@@@@ 78 5 |@@@@@@@@ 65 6 | 0 |
Este resultado indica que somente os primeiros seis megabytes do arquivo são acessados, talvez porque o tamanho do arquivo seja de seis megabytes. O resultado também indica que o arquivo completo não é acessado. Se você desejasse aumentar o tempo da partida a frio do StarOffice, talvez desejasse compreender o padrão de acesso do arquivo. Se as seções necessárias do arquivo pudessem ser totalmente contíguas, uma forma de aumentar o tempo de partida a frio do StarOffice seria executar um segmento verificador antes do aplicativo, induzindo a E/S no arquivo antes do necessário. (Essa abordagem é particularmente fácil se o arquivo for acessado usando-se mmap(2).) Entretanto, o tempo de aproximadamente 1,6 segundos que essa estratégia ganharia na partida a frio não compensa a complexidade adicional e o aumento de manutenção no aplicativo. De qualquer forma, os dados coletados com o provedor io permitem uma compreensão precisa do benefício que tal trabalho poderia oferecer.
O provedor io usa o mecanismo de estabilidade do DTrace para descrever suas estabilidades, conforme mostrado na tabela a seguir. Para obter mais informações sobre o mecanismo de estabilidade, consulte o Capítulo 39Estabilidade.
Elemento |
Estabilidade de nome |
Estabilidade de dados |
Classe de dependência |
---|---|---|---|
Provedor |
Desenvolvendo |
Desenvolvendo |
ISA |
Módulo |
Privada |
Privada |
Desconhecida |
Função |
Privada |
Privada |
Desconhecida |
Nome |
Desenvolvendo |
Desenvolvendo |
ISA |
Argumentos |
Desenvolvendo |
Desenvolvendo |
ISA |