Manuel de suivi dynamique Solaris

Chapitre 27 Fournisseur io

Le fournisseur io propose des sondes liées à l'entrée et la sortie de disque. Le fournisseur io permet d'étudier rapidement le comportement observé via des outils de contrôle d'E/S comme iostat(1M). Par exemple, grâce au fournisseur io, vous pouvez connaître les E/S par périphérique, type d'E/S, taille d'E/S, processus, nom d'application, nom de fichier ou par décalage de fichier.

Sondes

Les sondes io sont décrites dans Tableau 27–1.

Tableau 27–1 Sondes io

Sonder 

Description 

start

Sonde se déclenchant lorsqu'une demande d'E/S va être formulée sur un périphérique ou un serveur NFS. bufinfo_t correspondant à la demande d'E/S est indiqué par args[0]. devinfo_t du périphérique d'où les E/S proviennent est indiqué par args[1]. fileinfo_t du fichier correspondant à la demande d'E/S est indiqué par args[2]. Notez que la disponibilité des informations de fichier dépend du système de fichiers formulant la demande d'E/S. Pour plus d'informations, reportez-vous à la section fileinfo_t.

done

Sonde se déclenchant après la formulation d'une demande d'E/S. bufinfo_t correspondant à la demande d'E/S est indiqué par args[0]. La sonde done se déclenche une fois les E/S terminées, mais avant la fin du traitement sur le tampon. Par conséquent, B_DONE n'est pas défini dans b_flags au moment du déclenchement de la sonde done. devinfo_t du périphérique d'où les E/S proviennent est indiqué par args[1]. fileinfo_t du fichier correspondant à la demande d'E/S est indiqué par args[2].

wait-start

Sonde se déclenchant immédiatement avant qu'un thread commence à attendre la fin en attente d'une demande d'E/S particulière. La structure buf(9S) correspondant à la demande d'E/S pour laquelle le thread attend est indiquée par args[0]. devinfo_t du périphérique d'où les E/S proviennent est indiqué par args[1] . fileinfo_t du fichier correspondant à la demande d'E/S est indiqué par args[2]. Parfois, après le déclenchement de la sonde wait-start, la sonde wait-done est déclenchée dans le même thread.

wait-done

Sonde se déclenchant lorsqu'un thread doit attendre la fin d'une demande d'E/S donnée. bufinfo_t correspondant à la demande d'E/S pour laquelle le thread attend est indiqué par args[0]. devinfo_t du périphérique d'où les E/S proviennent est indiqué par args[1]. fileinfo_t du fichier correspondant à la demande d'E/S est indiqué par args[2]. La sonde wait-done ne se déclenche qu'après le déclenchement de la sonde wait-start dans le même thread.

Notez que les sondes io se déclenchent pour toutes les demandes d'E/S vers des périphériques, et pour toutes les demandes de lecture et d'écriture de fichier vers un serveur NFS. Des demandes de métadonnées à partir d'un serveur NFS, par exemple, ne déclenchent pas de sondes io en raison d'une demande readdir(3C).

Arguments

Les types d'argument des sondes io sont répertoriés dans Tableau 27–2. Les arguments sont décrits dans Tableau 27–1.

Tableau 27–2 Arguments de sonde io

Sonder 

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 *

Chaque sonde io comprend des arguments constitués d'un pointeur sur une structure buf(9S), d'un pointeur sur devinfo_t et d'un pointeur sur fileinfo_t. Ces structures sont décrites plus en détails dans cette section.

Structure bufinfo_t

La structure bufinfo_t représente l'abstraction décrivant une demande d'E/S. Le tampon correspondant à une demande d'E/S est indiqué par args[0] des sondes start, done, wait-start et wait-done. La définition de la structure bufinfo_t est la suivante :

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;

L'élément b_flags indique l'état du tampon d'E/S et comprend des valeurs d'état bitwise ou autres. Les valeurs d'état valides sont indiquées dans Tableau 27–3.

Tableau 27–3 Valeurs de b_flags

B_DONE

Indique que le transfert de données est terminé. 

B_ERROR

Indique une erreur de transfert d'E/S. Défini avec le champ b_error.

B_PAGEIO

Indique que le tampon est en cours d'utilisation dans une demande d'E/S chargée. Pour plus d'informations, reportez-vous à la description du champ b_addr.

B_PHYS

Indique que le tampon est en cours d'utilisation pour des E/S physiques (directes) d'une zone de données utilisateur. 

B_READ

Indique que les données doivent être lues du périphérique à la mémoire principale. 

B_WRITE

Indique que les données doivent être transférées de la mémoire principale au périphérique. 

B_ASYNC

La demande d'E/S est asynchrone et sera mise en attente. Les sondes wait-start et wait-done ne se déclenchent pas pour des demandes d'E/S asynchrones. Notez que B_ASYNC peut ne pas être défini pour certaines E/S conçues pour être asynchrones : le sous-système d'E/S asynchrone peut mettre en œuvre la demande asynchrone en disposant d'un thread distinct pour exécuter une opération d'E/S synchrone.

Le champ b_bcount représente le nombre d'octets à transférer dans la demande d'E/S.

Le champ b_addr représente l'adresse virtuelle de la demande d'E/S, à moins que B_PAGEIO soit défini. L'adresse est une adresse virtuelle de noyau à moins que B_PHYS soit défini, auquel cas il s'agit d'une adresse virtuelle d'utilisateur. Si B_PAGEIO est défini, le champ b_addr contient des données privées de noyau. Un de B_PHYS et de B_PAGEIO peut être défini, sinon aucun indicateur n'est défini.

Le champ b_lblkno identifie le bloc logique du périphérique auquel accéder. Le mappage d'un bloc logique avec un bloc physique (comme le cylindre, la piste, etc.) est défini par le périphérique.

Le champ b_resid est défini sur le nombre d'octets non transférés en raison d'une erreur.

Le champ b_bufsize contient la taille du tampon affecté.

Le champ b_iodone identifie une routine spécifique dans le noyau appelée à la fin de l'E/S.

Le champ b_error peut contenir un code d'erreur renvoyé par le périphérique en cas d'erreur d'E/S. b_error est défini avec le bit B_ERROR défini dans le membre b_flags.

Le champ b_edev contient les numéros supérieur et inférieur de périphériques du périphérique accessible. Les clients peuvent utiliser les sous-routines D getmajor() et getminor() pour extraire les numéros supérieur et inférieur de périphériques du champ b_edev.

devinfo_t

La structure devinfo_t fournit des informations sur un périphérique. La structure devinfo_t correspondant au périphérique de destination d'E/S est indiquée par args[1] des sondes start, done, wait-start et wait-done. Les éléments de devinfo_t sont les suivants :

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;

Le champ dev_major représente le numéro supérieur du périphérique. Pour plus d'informations, reportez-vous à getmajor(9F).

Le champ dev_minor représente le numéro inférieur du périphérique. Pour plus d'informations, reportez-vous à getminor(9F).

Le champ dev_instance représente le numéro d'instance du périphérique. L'instance d'un périphérique est différente du numéro inférieur. Le numéro inférieur est une abstraction gérée par le pilote du périphérique. Le numéro d'instance est une propriété du nœud de périphérique. Vous pouvez afficher des numéros d'instance de nœud de périphérique via prtconf(1M).

Le champ dev_name représente le nom du pilote de périphérique qui gère le périphérique. Vous pouvez afficher les noms des pilotes de périphérique via l'option -D pour prtconf(1M).

Le champ dev_statname représente le nom du périphérique tel qu'indiqué par iostat(1M). Ce nom correspond également au nom d'une statistique de noyau tel qu'indiqué par kstat(1M). Ce champ est conçu pour qu'une sortie iostat ou kstat anormale puisse être rapidement mise en corrélation avec l'activité d'E/S réelle.

Le champ dev_pathname représente le chemin d'accès complet au périphérique. Ce chemin peut être spécifié en tant qu'argument pour prtconf(1M) afin d'obtenir des informations détaillées sur le périphérique. Le chemin spécifié par dev_pathname inclut des composants indiquant le nœud du périphérique, le numéro d'instance et le nœud inférieur. Ces trois éléments ne sont cependant pas nécessairement indiqués dans le nom de statistique. Pour certains périphériques, le nom de statistique est composé du nom du périphérique et du numéro d'instance. Pour d'autres, il est composé du nom du périphérique et du numéro de nœud inférieur. Par conséquent, deux périphériques portant le même dev_statname peuvent avoir un dev_pathname différent .

fileinfo_t

La structure fileinfo_t fournit des informations sur un fichier. Le fichier auquel correspond des E/S est indiqué par args[0] des sondes start, done, wait-start et wait-done. La présence d'informations sur un fichier est liée au système de fichiers fournissant ces informations lors de la répartition de demandes d'E/S. Certains systèmes de fichiers, notamment de tiers, peuvent ne pas fournir ces informations. De même, des demandes d'E/S peuvent provenir d'un système de fichiers pour lequel il n'existe aucune information de fichier. Par exemple, les E/S pour des métadonnées de système de fichiers ne seront associées à aucun fichier. Enfin, certains systèmes de fichiers hautement optimisés peuvent regrouper des E/S de fichiers distincts dans une seule demande d'E/S. Dans ce cas, le système de fichiers peut fournir les informations relatives au fichier représentant la plupart des E/S ou au fichier représentant certaines E/S. Le système de fichiers peut également ne fournir aucune information de fichier.

La définition de la structure fileinfo_t est la suivante :

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;

Le champ fi_name contient le nom du fichier mais aucun composant de répertoire. Si aucune information de fichier n'est associée à une E/S, le champ fi_name sera défini sur la chaîne <none>. Dans quelques rares cas, le nom de chemin associé à un fichier peut être inconnu. Dans ce cas, le champ fi_name sera défini sur la chaîne <unknown>.

Le champ fi_dirname ne contient que le composant de répertoire du nom de fichier. Comme fi_name, cette chaîne peut être définie sur <none> s'il n'existe aucune information de fichier ou sur <unknown> si le nom du chemin associé au fichier est inconnu.

Le champ fi_pathname contient le nom du chemin complet au fichier. Comme fi_name, cette chaîne peut être définie sur <none> s'il n'existe aucune information de fichier ou sur <unknown> si le nom du chemin associé au fichier est inconnu.

Le champ fi_offset contient le décalage dans le fichier ou -1 s'il n'existe aucune information de fichier ou si le décalage n'est pas spécifié par le système de fichiers.

Exemples

L'exemple de script suivant indique des informations pertinentes sur chaque E/S telle que renvoyée :

#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");
}

La sortie de l'exemple lors d'un démarrage à froid d'Acrobat Reader sur un système portable x86 est similaire à l'exemple suivant :


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

Les entrées <none> de la sortie indiquent que l'E/S ne correspond pas aux données d'un fichier particulier : ces E/S sont dues à des métadonnées d'une quelconque forme. Les entrées <unknown> de la sortie indiquent que le nom du chemin d'accès au fichier est inconnu. Cette situation est relativement rare.

Vous pourriez appliquer un exemple de script un peu plus complexe en utilisant un ensemble associatif pour suivre la durée de chaque E/S, tel qu'illustré dans l'exemple suivant :

#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;
}

La sortie de l'exemple ci-dessus pendant la connexion à chaud d'un périphérique de stockage USB dans un système portable x86 au repos est illustré dans l'exemple suivant :


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

Plusieurs observations sur le mécanisme du système en fonction de cette sortie sont possibles. Premièrement, notez la durée prolongée pour exécuter les premières E/S, qui est d'environ 25 millisecondes pour chacune. Cette durée peut être due au périphérique cmdk0 dont l'alimentation est gérée sur le portable. Deuxièmement, observez l'E/S due au chargement du pilote scsa2usb(7D) pour traiter le périphérique de stockage de masse USB. Troisièmement, notez les messages dans /var/adm/messages renvoyés pour le périphérique. Enfin, observez la lecture des générateurs de liens de périphérique (fichiers se terminant par link.so) , qui traitent probablement le nouveau périphérique.

Le fournisseur io permet de comprendre de manière approfondie la sortie de iostat(1M). Supposons l'observation d'une sortie iostat similaire à l'exemple suivant :


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

Vous pouvez utiliser le script iotime.d pour afficher ces E/S lorsqu'elles se produisent, tel qu'illustré dans l'exemple suivant :


    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

Cette sortie semble indiquer que le fichier archives.tar est lu depuis cmdk0 (dans /export/archives), et écrit sur le périphérique sd2 (dans /mnt). La présence de deux fichiers nommés archives.tar en cours d'utilisation parallèle et distincte semble improbable. Pour en savoir plus, vous pouvez regrouper l'ID de périphérique, d'application, de processus et les octets transférés, tel qu'illustré dans l'exemple suivant :

#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", @);
}

L'exécution de ce script pendant quelques secondes entraîne une sortie similaire à l'exemple suivant :


# dtrace -s ./whoio.d
^C
    DEVICE                  APP        PID           BYTES
     cmdk0                   cp        790         1515520
       sd2                   cp        790         1527808

Cette sortie indique que cette activité est une copie du fichier archives.tar d'un périphérique à un autre. Cette conclusion pose une autre question : l'un de ces périphériques est-il plus rapide que l'autre ? Quel périphérique limite la copie ? Pour répondre à ces questions, vous devez connaître le débit effectif de chaque périphérique plutôt que le nombre d'octets par seconde transférés par chaque périphérique. Vous pouvez déterminer le débit à l'aide de l'exemple de script suivant :

#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", @);
}

L'exécution de l'exemple de script pendant plusieurs secondes produit la sortie suivante :


  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        

La sortie indique clairement que sd2 est le périphérique limiteur. Le débit sd2 est compris entre 256 Ko/s et 512 Ko/s, alors que cmdk0 fournit des E/S à un débit compris entre 8 Mo/s et plus de 64 Mo/s. Le script renvoie le nom tel qu'indiqué dans iostat et le chemin complet du périphérique. Pour en savoir plus sur le périphérique, vous pourriez spécifier le chemin du périphérique dans prtconf, tel qu'illustré dans l'exemple suivant :


# 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

Comme souligné, ce périphérique est un périphérique de stockage USB amovible.

Les exemples de cette section ont abordé toutes les demandes d'E/S. Cependant, un seul type de demande peut vous intéresser. L'exemple suivant permet de suivre les répertoires dans lesquels des écritures se produisent, ainsi que les applications à leur origine :

#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", @);
}

L'exécution de cet exemple de script sur une charge de travail de bureau sur une période donne des résultats intéressants, tel qu'illustré dans l'exemple suivant:


# 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

Comme l'indique la sortie, quasiment toutes les écritures sont associées au cache Mozilla Firebird. Les écritures intitulées <none> sont probablement dues à des écritures associées au journal UFS, écritures dues elles-mêmes à d'autres écritures du système de fichiers. Pour plus d'informations sur la journalisation, reportez-vous à ufs(7FS). Cet exemple illustre comment utiliser le fournisseur io pour relever un problème à un niveau logiciel supérieur. Dans ce cas, le script révèle un problème de configuration : le navigateur Web serait à l'origine de moins d'E/S (voire probablement aucune) si son cache était situé dans un répertoire d'un système de fichiers tmpfs(7FS).

Les exemples précédents n'ont utilisé que les sondes start et done. Vous pouvez utiliser les sondes wait-start et wait-done pour comprendre pourquoi les applications bloquent des E/S, et pendant combien de temps. L'exemple de script suivant utilise des sondes io et des sondes sched (voir le Chapitre26Fournisseur sched) pour dériver la durée CPU par rapport au délai d'attente d'E/S pour le logiciel 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);
}

L'exécution de l'exemple de script pendant un démarrage à froid du logiciel StarOffice donne la sortie suivante :


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

Comme le montre cette sortie, la lenteur du démarrage à froid de StarOffice est souvent due au délai d'attente d'E/S (13,1 secondes, par rapport à un délai de 3,6 secondes sur CPU). L'exécution du script sur un démarrage à chaud du logiciel StarOffice révèle que la mise en cache de page a supprimé la durée d'E/S, tel qu'illustré dans l'exemple suivant :


Time breakdown (milliseconds):
  <I/O wait>                                                       0
  <on cpu>                                                      2860

I/O wait breakdown (milliseconds):
  temp                                                             0
  soffice.tmp                                                      0
  <unknown>                                                        0
  Office                                                           0

La sortie du démarrage à froid illustre que le fichier applicat.rdb compte un délai d'attente d'E/S supérieur aux autres fichiers. Ce résultat est probablement dû au nombre important d'E/S pour ce fichier. Pour étudier les E/S pour ce fichier, vous pouvez utiliser le script D suivant :

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);
}

Ce script utilise le champ fi_offset de la structure fileinfo_t pour connaître les parties accessibles du fichier, de l'ordre du mégaoctet. L'exécution de ce script pendant un démarrage à froid du logiciel StarOffice donne une sortie similaire à l'exemple suivant :


# 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

Cette sortie indique que seuls les six premiers mégaoctets du fichier sont accessibles, probablement car la taille du fichier est de six mégaoctets. La sortie indique également que le fichier n'est pas accessible dans son intégralité. Si vous souhaitez améliorer la durée de démarrage à froid de StarOffice, vous pouvez souhaiter comprendre le modèle d'accès du fichier. Si les sections nécessaires du fichier pouvaient être contiguës, une méthode d'amélioration du démarrage à froid de StarOffice pourrait être d'exécuter un thread scout en amont de l'application, entraînant l'E/S du fichier plus tôt que requis (cette approche est tout particulièrement simple si le fichier est accessible via mmap(2)). Cependant, 1,6 seconde environ de démarrage à froid qui pourrait être économisée grâce à cette approche ne justifie pas la complexité et la charge de maintenance supplémentaires de l'application. D'autre part, les données collectées avec le fournisseur io permettent de comprendre précisément l'avantage qu'une telle tâche pourrait apporter.

Stabilité

Le fournisseur io utilise un mécanisme de stabilité DTrace pour décrire ses stabilités, tel qu'illustré dans le tableau suivant. Pour plus d'informations sur le mécanisme de stabilité, reportez-vous au Chapitre39Stabilité.

Élément 

Stabilité des noms 

Stabilité des données 

Classe de dépendance 

Fournisseur 

En cours d'évolution 

En cours d'évolution 

ISA

Module 

Privé 

Privé 

Inconnu 

Fonction 

Privé 

Privé 

Inconnu 

Nom 

En cours d'évolution 

En cours d'évolution 

ISA

Arguments 

En cours d'évolution 

En cours d'évolution 

ISA