首页 > 代码库 > Linux IO工具 iotop的替代品iopp

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 执行的命令