Linux 커널 2.6 이상에서는 File System 사용을 모니터링 하기 위해 block_dump라는 파라미터를 사용할 수 있다.
용도는 File System 사용 모니터링이지만, 조금 응용해보면 프로세스 별 Disk I/O 양을 확인할 수 있다. ^^
우선 이 기능을 사용하기 위해서는 아래와 같이 block_dump 커널 파라미터를 1로 수정한다.
기능을 끄려면 동일한 방법으로 block_dump 값을 0으로 바꾸기만 하면 된다.
# sysctl vm.block_dump=1 or # echo 1 > /proc/sys/vm/block_dump
|
block_dump 값이 활성화 되면 Linux 커널은 아래와 같이 File System 이벤트를 시스템 로그에 기록한다.
kjournald(587): WRITE block 248048 on sda1 kjournald(587): WRITE block 248056 on sda1
...
|
이 값을 잘 활용하면 프로세스 별로 Disk I/O가 얼마나 일어나는지 알 수 있다.
친절하게도 "High Performance MySQL"의 저자이기도 한 Baron Schwartz가 이 데이터를 잘 분석해서 보여주는 Perl Script를 짜 놓았다.
(기특하기도 하지~ ㅎ)
스크립트 파일을 열어보면 간단한 사용법이 있는데, IO를 확인하고 싶은 시간동안 돌려 두고, Ctrl + C로 인터럽트를 걸면 된다.
while 루프가 돌면서 dmesg로 계속 로그 내용을 수집하고 인터럽트가 걸리면 iodump Perl Script가 잘 정리해서 보여주는 구조다.
출력은 Block 단위로 나오는데, Linux 시스템에서는 4KB의 Block을 사용하므로 프로세스가 얼마나 많은 데이터를 Read Write 했는지 알 수 있다.
아래 예제에서는 MySQL 프로세스가 21.6MB (5549 * 4KB) 정도의 Write 작업을 한 것을 확인할 수 있다. ^^
# while true; do sleep 1; dmesg -c; done | perl iodump # Caught SIGINT. TASK PID TOTAL READ WRITE DIRTY DEVICES mysqld 3928 5549 0 5549 0 sda3 kjournald 1823 24 0 24 0 sda3
|
결과가 조금 아쉬워서 원하는 프로세스만 vmstat 형태로 나오게 Script 처리해 본 결과~
이제 좀 쓸만해진 것 같다. ^^
TIME TASK PID TOTAL READ(Blk) WRITE(Blk) DIRTY DEVICES 10:25:00 mysqld 4124 5 0 5 0 sda3 10:25:01 mysqld 9980 11 7 4 0 sda3 10:25:01 mysqld 3896 1 1 0 0 sda3 10:25:02 mysqld 3899 144 0 144 0 sda3 10:25:02 mysqld 3835 1 0 1 0 sda3 10:25:03 - - - - - - - 10:25:04 mysqld 9980 6 2 4 0 sda3 10:25:04 mysqld 3835 1 0 1 0 sda3 10:25:05 mysqld 9980 1 1 0 0 sda3 10:25:06 - - - - - - - 10:25:07 - - - - - - - 10:25:08 mysqld 3832 6 6 0 0 sda3 10:25:08 mysqld 9980 2 2 0 0 sda3 10:25:09 mysqld 10342 22 22 0 0 sda3 10:25:09 mysqld 10092 17 17 0 0 sda1 10:25:09 mysqld 10116 1 1 0 0 sda3
TIME TASK PID TOTAL READ(Blk) WRITE(Blk) DIRTY DEVICES 10:25:10 mysqld 10342 41 34 7 0 sda3 10:25:10 mysqld 10370 4 0 4 0 sda3 10:25:10 mysqld 10371 3 0 3 0 sda3 10:25:10 mysqld 10350 2 0 2 0 sda3 10:25:11 - - - - - - - 10:25:12 mysqld 10388 1872 0 1872 0 sda3 10:25:12 mysqld 10368 803 0 803 0 sda3 10:25:13 mysqld 10388 1729 0 1729 0 sda3 10:25:13 mysqld 10368 965 0 965 0 sda3 10:25:14 mysqld 10388 2037 0 2037 0 sda3 10:25:14 mysqld 10368 670 0 670 0 sda3 10:25:15 mysqld 10388 1756 0 1756 0 sda3 10:25:16 mysqld 10368 530 0 530 0 sda3 10:25:17 mysqld 10388 1845 0 1845 0 sda3 10:25:17 mysqld 10368 849 0 849 0 sda3 10:25:19 mysqld 10388 2717 0 2717 0 sda3 10:25:20 - - - - - - -
|
#!/usr/bin/env perl
# This program is part of Aspersa (http://code.google.com/p/aspersa/)
=pod
=head1 NAME
iodump - Compute per-PID I/O stats for Linux when iotop/pidstat/iopp are not available.
=head1 SYNOPSIS
Prepare the system:
dmesg -c
/etc/init.d/klogd stop
echo 1 > /proc/sys/vm/block_dump
Start the reporting:
while true; do sleep 1; dmesg -c; done | perl iodump
CTRL-C
Stop the system from dumping these messages:
echo 0 > /proc/sys/vm/block_dump
/etc/init.d/klogd start
=head1 AUTHOR
Baron Schwartz
=cut
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use sigtrap qw(handler finish untrapped normal-signals);
my %tasks;
my $oktorun = 1;
my $line;
while ( $oktorun && (defined ($line = <>)) ) {
my ( $task, $pid, $activity, $where, $device );
( $task, $pid, $activity, $where, $device )
= $line =~ m/(\S+)\((\d+)\): (READ|WRITE) block (\d+) on (\S+)/;
if ( !$task ) {
( $task, $pid, $activity, $where, $device )
= $line =~ m/(\S+)\((\d+)\): (dirtied) inode \(.*?\) (\d+) on (\S+)/;
}
if ( $task ) {
my $s = $tasks{$pid} ||= { pid => $pid, task => $task };
++$s->{lc $activity};
++$s->{activity};
++$s->{devices}->{$device};
}
}
printf("%-15s %10s %10s %10s %10s %10s %s\n",
qw(TASK PID TOTAL READ WRITE DIRTY DEVICES));
foreach my $task (
reverse sort { $a->{activity} <=> $b->{activity} } values %tasks
) {
printf("%-15s %10d %10d %10d %10d %10d %s\n",
$task->{task}, $task->{pid},
($task->{'activity'} || 0),
($task->{'read'} || 0),
($task->{'write'} || 0),
($task->{'dirty'} || 0),
join(', ', keys %{$task->{devices}}));
}
sub finish {
my ( $signal ) = @_;
if ( $oktorun ) {
print STDERR "# Caught SIG$signal.\n";
$oktorun = 0;
}
else {
print STDERR "# Exiting on SIG$signal.\n";
exit(1);
}
}