Linux IO工具 iotop的替代品iopp

iotop无疑在Linux IO检测上是个不错的工具,但苦于要求内核版本和Python版本,不少朋友放弃了,我也是。偶然间找到了iopp,用c写的,这个跟iotop是一个作用,nice!给大家分享下

安装方法很简单,首先复制下面源代码保存为iopp.c文件

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
 
#define PROC "/proc"
 
#define GET_VALUE(v) \
  p = strchr(p, ':'); \
  ++p; \
  ++p; \
  q = strchr(p, '\n'); \
  length = q - p; \
  if (length >= BUFFERLEN) \
  { \
   printf("ERROR - value is larger than the buffer: %d\n", __LINE__); \
   exit(1); \
  } \
  strncpy(value, p, length); \
  value[length] = '\0'; \
  v = atoll(value);
 
#define BTOKB(b) b >> 10
#define BTOMB(b) b >> 20
 
#define BUFFERLEN 255
#define COMMANDLEN 1024
#define VALUELEN 63
 
#define NUM_STRINGS 8
 
struct io_node
{
 int pid;
 long long rchar;
 long long wchar;
 long long syscr;
 long long syscw;
 long long read_bytes;
 long long write_bytes;
 long long cancelled_write_bytes;
 char command[COMMANDLEN + 1];
 struct io_node *next;
};
 
struct io_node *head = NULL;
int command_flag = 0;
int idle_flag = 0;
int mb_flag = 0;
int kb_flag = 0;
int hr_flag = 0;
 
/* Prototypes */
char *format_b(long long);
struct io_node *get_ion(int);
struct io_node *new_ion(char *);
void upsert_data(struct io_node *);
 
char *
format_b(long long amt)
{
 static char retarray[NUM_STRINGS][16];
 static int index = 0;
 register char *ret;
 register char tag = 'B';
 
 ret = retarray[index];
 index = (index + 1) % NUM_STRINGS;
 
 if (amt >= 10000) {
  amt = (amt + 512) / 1024;
  tag = 'K';
  if (amt >= 10000) {
   amt = (amt + 512) / 1024;
   tag = 'B';
   if (amt >= 10000) {
    amt = (amt + 512) / 1024;
    tag = 'G';
   }
  }
 }
 
 snprintf(ret, sizeof(retarray[index]) - 1, "%lld%c", amt, tag);
 
 return (ret);
}
 
int
get_cmdline(struct io_node *ion)
{
 int fd;
 int length;
 char filename[BUFFERLEN + 1];
 char buffer[COMMANDLEN + 1];
 char *p;
 char *q;
 
 
 length = snprintf(filename, BUFFERLEN, "%s/%d/cmdline", PROC, ion->pid);
 if (length == BUFFERLEN)
  printf("WARNING - filename length may be too big for buffer: %d\n",
    __LINE__);
 fd = open(filename, O_RDONLY);
 if (fd == -1)
  return 1;
 length = read(fd, buffer, sizeof(buffer) - 1);
 close(fd);
 buffer[length] = '\0';
 if (length == 0)
  return 2;
 if (command_flag == 0)
 {
  /*
  * The command is near the beginning; we don't need to be able to
  * the entire stat file.
  */
  p = strchr(buffer, '(');
  ++p;
  q = strchr(p, ')');
  length = q - p;
 }
 else
  p = buffer;
 length = length < COMMANDLEN ? length : COMMANDLEN;
 strncpy(ion->command, p, length);
 ion->command[length] = '\0';
 return 0;
}
 
struct io_node *
get_ion(int pid)
{
 struct io_node *c = head;
 
 while (c != NULL)
 {
  if (c->pid == pid)
   break;
  c = c->next;
 }
 return c;
}
 
int
get_tcomm(struct io_node *ion)
{
 int fd;
 int length;
 char filename[BUFFERLEN + 1];
 char buffer[BUFFERLEN + 1];
 char *p;
 char *q;
 
 length = snprintf(filename, BUFFERLEN, "%s/%d/stat", PROC, ion->pid);
 if (length == BUFFERLEN)
  printf("WARNING - filename length may be too big for buffer: %d\n",
    __LINE__);
 fd = open(filename, O_RDONLY);
 if (fd == -1)
  return 1;
 length = read(fd, buffer, sizeof(buffer) - 1);
 close(fd);
 /*
  * The command is near the beginning; we don't need to be able to
  * the entire stat file.
  */
 p = strchr(buffer, '(');
 ++p;
 q = strchr(p, ')');
 length = q - p;
 length = length < BUFFERLEN ? length : BUFFERLEN;
 
 strncpy(ion->command, p, length);
 ion->command[length] = '\0';
 return 0;
}
 
struct io_node *
insert_ion(struct io_node *ion)
{
 struct io_node *c;
 struct io_node *p;
 
 /* Check the head of the list as a special case. */
 if (ion->pid < head->pid)
 {
  ion->next = head;
  head = ion;
  return head;
 }
 
 c = head->next;
 p = head;
 while (c != NULL)
 {
  if (ion->pid < c->pid)
  {
   ion->next = c;
   p->next = ion;
   return head;
  }
  p = c;
  c = c->next;
 }
 
 /* Append to the end of the list. */
 if (c == NULL)
  p->next = ion;
 
 return head;
}
 
void
get_stats()
{
 DIR *dir = opendir(PROC);
 struct dirent *ent;
 char filename[BUFFERLEN + 1];
 char buffer[BUFFERLEN + 1];
 
 char value[BUFFERLEN + 1];
 
 /* Display column headers. */
 if (hr_flag == 1)
  printf("%5s %5s %5s %8s %8s %5s %6s %7s %s\n", "pid", "rchar", "wchar",
    "syscr", "syscw", "reads", "writes", "cwrites", "command");
 else if (kb_flag == 1)
  printf("%5s %8s %8s %8s %8s %8s %8s %8s %s\n", "pid", "rchar", "wchar",
    "syscr", "syscw", "rkb", "wkb", "cwkb", "command");
 else if (mb_flag == 1)
  printf("%5s %8s %8s %8s %8s %8s %8s %8s %s\n", "pid", "rchar", "wchar",
    "syscr", "syscw", "rmb", "wmb", "cwmb", "command");
 else
  printf("%5s %8s %8s %8s %8s %8s %8s %8s %s\n", "pid", "rchar", "wchar",
    "syscr", "syscw", "rbytes", "wbytes", "cwbytes", "command");
 
 /* Loop through the process table and display a line per pid. */
 while ((ent = readdir(dir)) != NULL)
 {
  int rc;
  int fd;
  int length;
 
  char *p;
  char *q;
 
  struct io_node *ion;
  struct io_node *old_ion;
 
  long long rchar;
  long long wchar;
  long long syscr;
  long long syscw;
  long long read_bytes;
  long long write_bytes;
  long long cancelled_write_bytes;
 
  if (!isdigit(ent->d_name[0]))
   continue;
 
  ion = new_ion(ent->d_name);
 
  if (command_flag == 1)
   rc = get_cmdline(ion);
  if (command_flag == 0 || rc != 0)
   /* If the full command line is not asked for or is empty... */
   rc = get_tcomm(ion);
 
  if (rc != 0)
  {
   free(ion);
   continue;
  }
 
  /* Read 'io' file. */
  length = snprintf(filename, BUFFERLEN, "%s/%s/io", PROC, ent->d_name);
  if (length == BUFFERLEN)
   printf("WARNING - filename length may be too big for buffer: %d\n",
     __LINE__);
  fd = open(filename, O_RDONLY);
  if (fd == -1)
  {
   free(ion);
   continue;
  }
  length = read(fd, buffer, sizeof(buffer) - 1);
  close(fd);
  buffer[length] = '\0';
 
  /* Parsing the io file data. */
  p = buffer;
  GET_VALUE(ion->rchar);
  GET_VALUE(ion->wchar);
  GET_VALUE(ion->syscr);
  GET_VALUE(ion->syscw);
  GET_VALUE(ion->read_bytes);
  GET_VALUE(ion->write_bytes);
  GET_VALUE(ion->cancelled_write_bytes);
 
  old_ion = get_ion(ion->pid);
 
  /* Display the pid's io data. */
  if (old_ion != NULL)
  {
   rchar = ion->rchar - old_ion->rchar;
   wchar = ion->wchar - old_ion->wchar;
   syscr = ion->syscr - old_ion->syscr;
   syscw = ion->syscw - old_ion->syscw;
   read_bytes = ion->read_bytes - old_ion->read_bytes;
   write_bytes = ion->write_bytes - old_ion->write_bytes;
   cancelled_write_bytes = ion->cancelled_write_bytes -
     old_ion->cancelled_write_bytes;
 
   if (kb_flag == 1 && hr_flag == 0)
   {
    rchar = BTOKB(rchar);
    wchar = BTOKB(wchar);
    syscr = BTOKB(syscr);
    syscw = BTOKB(syscw);
    read_bytes = BTOKB(read_bytes);
    write_bytes = BTOKB(write_bytes);
    cancelled_write_bytes = BTOKB(cancelled_write_bytes);
   }
   else if (mb_flag == 1 && hr_flag == 0)
   {
    rchar = BTOMB(rchar);
    wchar = BTOMB(wchar);
    syscr = BTOMB(syscr);
    syscw = BTOMB(syscw);
    read_bytes = BTOMB(read_bytes);
    write_bytes = BTOMB(write_bytes);
    cancelled_write_bytes = BTOMB(cancelled_write_bytes);
   }
 
   if (!(idle_flag == 1 && rchar == 0 && wchar == 0 && syscr == 0 &&
     syscw == 0 && read_bytes == 0 && write_bytes == 0 &&
     cancelled_write_bytes == 0)) {
    if (hr_flag == 0)
     printf("%5d %8lld %8lld %8lld %8lld %8lld %8lld %8lld %s\n",
       ion->pid,
       rchar,
       wchar,
       syscr,
       syscw,
       read_bytes,
       write_bytes,
       cancelled_write_bytes,
       ion->command);
    else
     printf("%5d %5s %5s %8lld %8lld %5s %6s %7s %s\n",
       ion->pid,
       format_b(rchar),
       format_b(wchar),
       syscr,
       syscw,
       format_b(read_bytes),
       format_b(write_bytes),
       format_b(cancelled_write_bytes),
       ion->command);
   }
  }
  else if (idle_flag != 1)
   /*
    * No previous data, show 0's instead of calculating negatives
    * only if we are shoring idle processes.
    */
   printf("%5d %8d %8d %8d %8d %8d %8d %8d %s\n",
     ion->pid, 0, 0, 0, 0, 0, 0, 0, ion->command);
 
  upsert_data(ion);
 }
 closedir(dir);
 return;
}
 
struct io_node *
new_ion(char *pid)
{
 struct io_node *ion;
 
 ion = (struct io_node *) malloc(sizeof(struct io_node));
 bzero(ion, sizeof(struct io_node));
 ion->pid = atoi(pid);
 
 return ion;
}
 
void
upsert_data(struct io_node *ion)
{
 struct io_node *n;
 
 /* List is empty. */
 if (head == NULL)
 {
  head = ion;
  return;
 }
 
 /* Check if we have seen this pid before. */
 n = head;
 while (n != NULL)
 {
  if (n->pid == ion->pid)
  {
   n->rchar = ion->rchar;
   n->wchar = ion->wchar;
   n->syscr = ion->syscr;
   n->syscw = ion->syscw;
   n->read_bytes = ion->read_bytes;
   n->write_bytes = ion->write_bytes;
   n->cancelled_write_bytes = ion->cancelled_write_bytes;
   /*
    * If the pids wrap, then the command may be different then before.
    */
   strcpy(n->command, ion->command);
   free(ion);
   return;
  }
  n = n->next;
 }
 
 /* Add this pid to the list. */
 head = insert_ion(ion);
 return;
}
 
void
usage()
{
 printf("usage: iopp -h|--help\n");
 printf("usage: iopp [-ci] [-k|-m] [delay [count]]\n");
 printf("            -c, --command display full command line\n");
 printf("            -h, --help display help\n");
 printf("            -i, --idle hides idle processes\n");
 printf("            -k, --kilobytes display data in kilobytes\n");
 printf("            -m, --megabytes display data in megabytes\n");
 printf("            -u, --human-readable display data in kilo-, mega-, or giga-bytes\n");
}
 
int
main(int argc, char *argv[])
{
 int c;
 
 int delay = 0;
 int count = 0;
 int max_count = 1;
 
 while (1)
 {
  int option_index = 0;
  static struct option long_options[] = {
    { "command", no_argument, 0, 'c' },
    { "help", no_argument, 0, 'h' },
    { "human-readable", no_argument, 0, 'u' },
    { "idle", no_argument, 0, 'i' },
    { "kilobytes", no_argument, 0, 'k' },
    { "megabytes", no_argument, 0, 'm' },
    { 0, 0, 0, 0 }
  };
 
  c = getopt_long(argc, argv, "chikmu", long_options, &option_index);
  if (c == -1)
  {
   /* Handle delay and count arguments. */
 
   if (argc == optind)
    break; /* No additional arguments. */
   else if ((argc - optind) == 1)
   {
    delay = atoi(argv[optind]);
    max_count = -1;
   }
   else if ((argc - optind) == 2)
   {
    delay = atoi(argv[optind]);
    max_count = atoi(argv[optind + 1]);
   }
   else
   {
    /* Too many additional arguments. */
    usage();
    return 3;
   }
   break;
  }
 
  switch (c)
  {
  case 'c':
   command_flag = 1;
   break;
  case 'h':
   usage();
   return 0;
  case 'i':
   idle_flag = 1;
   break;
  case 'k':
   kb_flag = 1;
   break;
  case 'm':
   mb_flag = 1;
   break;
  case 'u':
   hr_flag = 1;
   break;
  default:
   usage();
   return 2;
  }
 }
 
 while (max_count == -1 || count++ < max_count)
 {
  get_stats();
  if (count != max_count)
   sleep(delay);
 }
 return 0;
}

然后放到服务器上,gcc -o iopp iopp.c 编译一下

运行  ./iopp -i -k -c 1 > io.log 这个命令就可以把实时的io信息打印出来啦

打印出来的各项含义:

pid 进程ID

rchar 将要从磁盘读取的字节数

wchar 已经写入或应该要写入磁盘的字节数

syscr 读I/O数

syscw 写I/O数

rbytes 真正从磁盘读取的字节数

wbytes 真正写入到磁盘的字节数

cwbytes 因为清空页面缓存而导致没有发生操作的字节数

command 执行的命令

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/17867.html