lsof:测量套接字 fd 的 I/O 速率

lsof:测量套接字 fd 的 I/O 速率

我正在编写一个脚本,该脚本解析 的输出lsof以显示哪些进程打开了套接字的列表(类似于netstat显示的内容)。lsof还给了我一个文件描述符。我现在想做的是让我的脚本也告诉通过该套接字发送/接收了多少数据(以KB/秒为单位)。

到目前为止我已经看过:

  1. nethogs:告诉我一个进程的网络 I/O,但仅限于每个进程,而不是每个套接字。
  2. iotop:告诉我每个进程的磁盘 I/O;似乎无法区分网络 I/O 或每个套接字的 I/O。
  3. /proc/pid/fd/: 看起来这并不能告诉我太多。
  4. fatrace:告诉我进程访问哪些文件(不是套接字)。
  5. iostat:告诉我每个磁盘的平均 I/O 统计信息。
  6. tcpdump:为我提供每个 IP 的所有流量的转储;似乎无法判断流量属于哪个套接字。
  7. strace -p pid -e trace=network -s 0:告诉我每次给定进程调用某些套接字函数,这看起来很有用,但实际上只给了我很多recvfrom(13, 0x7feed8fb3074, 4096, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable)
  8. strace -p pid -e trace=read,write -sread:告诉我每个/调用的结果write

strace看起来很有希望,但我不确定我是否正确工作(read,write足够了吗?),而且似乎会有很多开销。 (对于每个具有打开套接字的进程,我必须运行一个实例strace并解析输出。)

Linux 内核是否提供了更好的方法来测量每个 fd/socket 进行了多少 I/O?可能可以设置iptables或破解某些东西nethogs

答案1

考虑使用系统点击。它是 DTrace 克隆,但针对 Linux - 它编译动态修补内核的内核模块,并具有对其数据的完全访问权限(因此lsof在这种情况下可能不需要)。

然而,您询问的信息越多,脚本就会变得越棘手并且特定于内核版本。

例如,用于套接字的简单的类似于统计的实用程序将如下所示:

global stats;

probe begin {
    printf("%14s %6s %12s %5s %5s %8s\n", "NAME", "PID", "EXECNAME",
                "INO", "OPS/S", "BYTES");
}

function file_ino:long (file:long)
{
    if(file == 0) return -1;
    d_inode = @cast(file, "file", "kernel")->f_inode;
    if (d_inode == 0) return -1;
    return @cast(d_inode, "inode", "kernel")->i_ino;
}

probe socket.send, socket.receive {
    if(success == 0) next;

    /* Get inode number for a socket. Depending on 
       operation, struct file is contained in different fields. 
       Determine that field and get inode number */
    ino = -1;
    if(@defined($sock)) {
        ino = file_ino($sock->file);
    }
    else if(@defined($iocb)) {
        ino = file_ino($iocb->ki_filp);
    }

    stats[pid(), execname(), ino, name] <<< size;
}

probe timer.s(1) {
    /* Every 1 second print statistics */
    foreach([pid+, ename, ino, name] in stats) {
        printf("%14s %6d %12s %5d %5d %8d\n", name, pid, ename, ino, 
                    @count(stats[pid, ename, ino, name]), 
                    @sum(stats[pid, ename, ino, name]));
    }
    delete stats;
}

我在 vanilla Linux 3.12 上测试了它,但是正如你所看到的,获取 inode 编号的逻辑依赖于内部内核结构。

正如您所看到的,大多数时候,它会跟踪自己写入 SSH 会话:

       NAME    PID     EXECNAME   INO OPS/S    BYTES
socket.send   2655         sshd  7480     1       96
socket.send   2655         sshd  7480     1       96
socket.send   2655         sshd  7480     1       96
...

示例中有更复杂的脚本:https://sourceware.org/systemtap/examples/network/socktop

警告

SystemTap 是正在开发的内核级软件,因此存在内核崩溃或冻结的可能性。不过,这种情况很少见,但要小心。

参考

相关内容