📄 iostat.c
字号:
/* * iostat: report CPU and I/O statistics * (C) 1998-2006 by Sebastien GODARD (sysstat <at> wanadoo.fr) * *************************************************************************** * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License, or (at your * * option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY * * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * * for more details. * * * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *************************************************************************** */#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <signal.h>#include <fcntl.h>#include <errno.h>#include <time.h>#include <ctype.h>#include <dirent.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/utsname.h>#include <sys/param.h> /* for HZ */#include "iostat.h"#include "common.h"#include "ioconf.h"#ifdef USE_NLS#include <locale.h>#include <libintl.h>#define _(string) gettext(string)#else#define _(string) (string)#endifstruct comm_stats comm_stats[2];struct io_stats *st_iodev[2];struct io_hdr_stats *st_hdr_iodev;struct io_dlist *st_dev_list;/* Nb of devices and partitions found */int iodev_nr = 0;/* Nb of devices entered on the command line */int dlist_idx = 0;long interval = 0;unsigned char timestamp[64];/* * Nb of processors on the machine. * A value of 1 means two procs... */int cpu_nr = -1;/* *************************************************************************** * Print usage and exit *************************************************************************** */void usage(char *progname){ fprintf(stderr, _("Usage: %s [ options... ] [ <interval> [ <count> ] ]\n" "Options are:\n" "[ -c | -d ] [ -k | -m ] [ -t ] [ -V ] [ -x ]\n" "[ <device> [ ... ] | ALL ] [ -p [ <device> | ALL ] ]\n"), progname); exit(1);}/* *************************************************************************** * SIGALRM signal handler *************************************************************************** */void alarm_handler(int sig){ signal(SIGALRM, alarm_handler); alarm(interval);}/* *************************************************************************** * Initialize stats common structures *************************************************************************** */void init_stats(void){ memset(&comm_stats[0], 0, COMM_STATS_SIZE); memset(&comm_stats[1], 0, COMM_STATS_SIZE);}/* *************************************************************************** * Set every disk_io entry to inactive state *************************************************************************** */void set_entries_inactive(int iodev_nr){ int i; struct io_hdr_stats *shi = st_hdr_iodev; for (i = 0; i < iodev_nr; i++, shi++) shi->active = FALSE;}/* *************************************************************************** * Set structures's state to free for inactive entries *************************************************************************** */void free_inactive_entries(int iodev_nr){ int i; struct io_hdr_stats *shi = st_hdr_iodev; for (i = 0; i < iodev_nr; i++, shi++) { if (!shi->active) shi->used = FALSE; }}/* *************************************************************************** * Allocate and init I/O devices structures *************************************************************************** */void salloc_device(int iodev_nr){ int i; for (i = 0; i < 2; i++) { if ((st_iodev[i] = (struct io_stats *) malloc(IO_STATS_SIZE * iodev_nr)) == NULL) { perror("malloc"); exit(4); } memset(st_iodev[i], 0, IO_STATS_SIZE * iodev_nr); } if ((st_hdr_iodev = (struct io_hdr_stats *) malloc(IO_HDR_STATS_SIZE * iodev_nr)) == NULL) { perror("malloc"); exit(4); } memset(st_hdr_iodev, 0, IO_HDR_STATS_SIZE * iodev_nr);}/* *************************************************************************** * Allocate structures for devices entered on the command line *************************************************************************** */void salloc_dev_list(int list_len){ if ((st_dev_list = (struct io_dlist *) malloc(IO_DLIST_SIZE * list_len)) == NULL) { perror("malloc"); exit(4); } memset(st_dev_list, 0, IO_DLIST_SIZE * list_len);}/* *************************************************************************** * Look for the device in the device list and store it if necessary. * Returns the position of the device in the list. *************************************************************************** */int update_dev_list(int *dlist_idx, char *device_name){ int i; struct io_dlist *sdli = st_dev_list; for (i = 0; i < *dlist_idx; i++, sdli++) { if (!strcmp(sdli->dev_name, device_name)) break; } if (i == *dlist_idx) { /* Device not found: store it */ (*dlist_idx)++; strncpy(sdli->dev_name, device_name, MAX_NAME_LEN - 1); } return i;}/* *************************************************************************** * Allocate and init structures, according to system state *************************************************************************** */void io_sys_init(int *flags){ int i; /* Init stat common counters */ init_stats(); /* How many processors on this machine ? */ cpu_nr = get_cpu_nr(~0); /* Get number of block devices and partitions in /proc/diskstats */ if ((iodev_nr = get_diskstats_dev_nr(CNT_PART, CNT_ALL_DEV)) > 0) { *flags |= I_F_HAS_DISKSTATS; iodev_nr += NR_DEV_PREALLOC; } if (!HAS_DISKSTATS(*flags) || (DISPLAY_PARTITIONS(*flags) && !DISPLAY_PART_ALL(*flags))) { /* * If /proc/diskstats exists but we also want stats for the partitions * of a particular device, stats will have to be found in /sys. So we * need to know if /sys is mounted or not, and set *flags accordingly. */ /* Get number of block devices (and partitions) in sysfs */ if ((iodev_nr = get_sysfs_dev_nr(DISPLAY_PARTITIONS(*flags))) > 0) { *flags |= I_F_HAS_SYSFS; iodev_nr += NR_DEV_PREALLOC; } /* * Get number of block devices and partitions in /proc/partitions, * those with statistics... */ else if ((iodev_nr = get_ppartitions_dev_nr(CNT_PART)) > 0) { *flags |= I_F_HAS_PPARTITIONS; iodev_nr += NR_DEV_PREALLOC; } /* Get number of "disk_io:" entries in /proc/stat */ else if ((iodev_nr = get_disk_io_nr()) > 0) { *flags |= I_F_PLAIN_KERNEL24; iodev_nr += NR_DISK_PREALLOC; } else { /* Assume we have an old kernel: stats for 4 disks are in /proc/stat */ iodev_nr = 4; *flags |= I_F_OLD_KERNEL; } } /* Allocate structures for number of disks found */ salloc_device(iodev_nr); if (HAS_OLD_KERNEL(*flags)) { struct io_hdr_stats *shi = st_hdr_iodev; /* * If we have an old kernel with the stats for the first four disks * in /proc/stat, then set the devices names to hdisk[0..3]. */ for (i = 0; i < 4; i++, shi++) { shi->used = TRUE; sprintf(shi->name, "%s%d", K_HDISK, i); } }}/* *************************************************************************** * Save stats for current device or partition *************************************************************************** */void save_dev_stats(char *dev_name, int curr, struct io_stats *sdev){ int i; struct io_hdr_stats *st_hdr_iodev_i; struct io_stats *st_iodev_i; /* Look for device in data table */ for (i = 0; i < iodev_nr; i++) { st_hdr_iodev_i = st_hdr_iodev + i; if (!strcmp(st_hdr_iodev_i->name, dev_name)) { break; } } if (i == iodev_nr) { /* * This is a new device: look for an unused entry to store it. * Thus we are able to handle dynamically registered devices. */ for (i = 0; i < iodev_nr; i++) { st_hdr_iodev_i = st_hdr_iodev + i; if (!st_hdr_iodev_i->used) { /* Unused entry found... */ st_hdr_iodev_i->used = TRUE; /* Indicate it is now used */ strcpy(st_hdr_iodev_i->name, dev_name); st_iodev_i = st_iodev[!curr] + i; memset(st_iodev_i, 0, IO_STATS_SIZE); break; } } } if (i < iodev_nr) { st_hdr_iodev_i = st_hdr_iodev + i; st_hdr_iodev_i->active = TRUE; st_iodev_i = st_iodev[curr] + i; *st_iodev_i = *sdev; } /* else it was a new device but there was no free structure to store it */}/* *************************************************************************** * Read stats from /proc/stat file... * Useful at least for CPU utilization. * May be useful to get disk stats if /sys not available. *************************************************************************** */void read_proc_stat(int curr, int flags){ FILE *fp; char line[8192]; int pos, i; unsigned long v_tmp[4]; unsigned int v_major, v_index; struct io_stats *st_iodev_tmp[4]; unsigned long long cc_idle, cc_iowait, cc_steal; unsigned long long cc_user, cc_nice, cc_system, cc_hardirq, cc_softirq; /* * Prepare pointers on the 4 disk structures in case we have a * /proc/stat file with "disk_rblk", etc. entries. */ for (i = 0; i < 4; i++) st_iodev_tmp[i] = st_iodev[curr] + i; if ((fp = fopen(STAT, "r")) == NULL) { perror("fopen"); exit(2); } while (fgets(line, 8192, fp) != NULL) { if (!strncmp(line, "cpu ", 4)) { /* * Read the number of jiffies spent in the different modes, * and compute system uptime in jiffies (1/100ths of a second * if HZ=100). * Some fields are only presnt in 2.6 kernels. */ comm_stats[curr].cpu_iowait = 0; /* For pre 2.6 kernels */ comm_stats[curr].cpu_steal = 0; cc_hardirq = cc_softirq = 0; /* CPU counters became unsigned long long with kernel 2.6.5 */ sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu", &(comm_stats[curr].cpu_user), &(comm_stats[curr].cpu_nice), &(comm_stats[curr].cpu_system), &(comm_stats[curr].cpu_idle), &(comm_stats[curr].cpu_iowait), &cc_hardirq, &cc_softirq, &(comm_stats[curr].cpu_steal)); /* * Time spent in system mode also includes time spent servicing * hard interrupts and softirqs. */ comm_stats[curr].cpu_system += cc_hardirq + cc_softirq; /* * Compute system uptime in jiffies. * Uptime is multiplied by the number of processors. */ comm_stats[curr].uptime = comm_stats[curr].cpu_user + comm_stats[curr].cpu_nice + comm_stats[curr].cpu_system + comm_stats[curr].cpu_idle + comm_stats[curr].cpu_iowait + comm_stats[curr].cpu_steal; } else if ((!strncmp(line, "cpu0", 4)) && cpu_nr) { /* * Read CPU line for proc#0 (if available). * Useful to compute uptime reduced to one processor on SMP machines, * with fewer risks to get an overflow... */ cc_iowait = cc_hardirq = cc_softirq = cc_steal = 0; sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu", &cc_user, &cc_nice, &cc_system, &cc_idle, &cc_iowait, &cc_hardirq, &cc_softirq, &cc_steal); comm_stats[curr].uptime0 = cc_user + cc_nice + cc_system + cc_idle + cc_iowait + cc_hardirq + cc_softirq + cc_steal; } else if (DISPLAY_EXTENDED(flags) || HAS_DISKSTATS(flags) || HAS_PPARTITIONS(flags) || HAS_SYSFS(flags)) /* * When displaying extended statistics, or if /proc/diskstats or * /proc/partitions exists, or /sys is mounted, * we just need to get CPU info from /proc/stat. */ continue; else if (!strncmp(line, "disk_rblk ", 10)) { /* * Read the number of blocks read from disk. * A block is of indeterminate size. * The size may vary depending on the device type. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -