カーネル3.3から開始

カーネル3.3から開始

UNIXソケットのもう一方の端を所有しているプロセスを確認したいと思います。

socketpair()特に、質問はすべてのUNIXソケットに対して同じですが、生成されたソケットを使用することについて尋ねられています。

aとsをparent生成するプログラムがあります。親プロセスが終了し、通信が維持されます。子供たちはその反対だ。それから子供のためのもう一つのプログラムです。両方のソケットはこのソケットペアを介して前後に通信できます。socketpair(AF_UNIX, SOCK_STREAM, 0, fds)fork()fds[1]fds[0]close(fds[0]); s=fds[1]exec()child1

今、私はそれが誰であるかを知っていますが、それが誰であるかをparent知りたいとしましょう。child1どうすればいいですか?

利用可能なツールはいくつかありますが、そのうちどのプロセスがソケットの反対側にあるかを知らせるツールはありません。私は試した:

  • lsof -c progname
  • lsof -c parent -c child1
  • ls -l /proc/$(pidof server)/fd
  • cat /proc/net/unix

デフォルトではソケットとその内容の両方を見ることができますが、接続されているかどうかはわかりません。親プロセスのどのFDがどの子プロセスと通信しているかを確認しようとしています。

ベストアンサー1

ノートlsofここで説明した2つのアプローチを組み合わせて、ループバックTCP接続ピアに関する情報を次の場所に追加するラッパーを維持します。https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc

Linux-3.3以降。

Linuxでは、カーネルバージョン3.3から始まります(およびUNIX_DIAG機能がカーネルに組み込まれています)、特定のUNIXドメインソケット(ソケットペアを含む)のピアは新しいインターネット接続APIに基づいています。

lsofこのAPIはバージョン4.89以降で利用可能です。

lsof +E -aUc Xorg

Xorg名前の両端で始まるプロセスを持つすべてのUnixドメインソケットを次のような形式で一覧表示します。

Xorg       2777       root   56u  unix 0xffff8802419a7c00      0t0   34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u

バージョンがlsof古すぎる場合は、より多くのオプションがあります。

ssこのリソースユーティリティiproute2は、同じAPIを使用してピア情報を含む、システムのUNIXドメインソケットリストに関する情報を取得して表示します。

ソケットは次のように識別されます。アイノード番号。ソケットファイルのファイルシステムinodeとは何の関係もありません。

たとえば、

$ ss -x
[...]
u_str  ESTAB    0    0   @/tmp/.X11-unix/X0 3435997     * 3435996

これは、ソケット3435997(ABSTRACTソケットにバインド/tmp/.X11-unix/X0)がソケット3435996に接続されていることを示します。この-pオプションは、ソケットが開いているプロセスを通知します。readlinkこれを行うことでこれを行うので、あなたが/proc/$pid/fd/*所有するプロセスでのみこれを行うことができます(あなたが所有している場合を除くroot)。たとえば、次のようになります。

$ sudo ss -xp
[...]
u_str  ESTAB  0  0  @/tmp/.X11-unix/X0 3435997 * 3435996 users:(("Xorg",pid=3080,fd=83))
[...]
$ sudo ls -l /proc/3080/fd/23
lrwx------ 1 root root 64 Mar 12 16:34 /proc/3080/fd/83 -> socket:[3435997]

どのプロセスに3435996があるかを確認するには、出力で該当する項目を見つけますss -xp

$ ss -xp | awk '$6 == 3435996'
u_str  ESTAB  0  0  * 3435996  * 3435997 users:(("xterm",pid=29215,fd=3))

また、このスクリプトをラッパーとして使用してlsof関連情報を簡単に表示することもできます。

#! /usr/bin/perl
# lsof wrapper to add peer information for unix domain socket.
# Needs Linux 3.3 or above and CONFIG_UNIX_DIAG enabled.

# retrieve peer and direction information from ss
my (%peer, %dir);
open SS, '-|', 'ss', '-nexa';
while (<SS>) {
  if (/\s(\d+)\s+\*\s+(\d+) ([<-]-[->])$/) {
    $peer{$1} = $2;
    $dir{$1} = $3;
  }
}
close SS;

# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfin';
while (<LSOF>) {
  if (/(.)(.*)/) {
    $fields{$1} = $2;
    if ($1 eq 'n') {
      $proc{$fields{i}}->{"$fields{c},$fields{p}" .
      ($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
    }
  }
}
close LSOF;

# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
  chomp;
  if (/\sunix\s+\S+\s+\S+\s+(\d+)\s/) {
    my $peer = $peer{$1};
    if (defined($peer)) {
      $_ .= $peer ?
            " ${dir{$1}} $peer\[" . (join("|", keys%{$proc{$peer}})||"?") . "]" :
            "[LISTENING]";
    }
  }
  print "$_\n";
}
close LSOF or exit(1);

たとえば、

$ sudo that-lsof-wrapper -ad3 -p 29215
コマンドPIDユーザーFDタイプデバイスサイズ/終了ノード名
xterm 29215 Steven 3u Unix 0xffff8800a07da4c0 0t0 3435996タイプ=ストリーム<-> 3435997[Xorg,3080,@/tmp/.X11-unix/X0]

Linux-3.3以前

以前のLinux APIは、テキストファイルからUnixソケット情報を取得しました/proc/net/unix。これには、すべてのUnixドメインソケット(ソケットペアを含む)が一覧表示されます。最初のフィールド(sysctlパラメーターが使用されていない場合は、スーパーユーザー以外のユーザーには隠されますkernel.kptr_restrict)は次のとおりです。@Totorはすでに説明しています。そのエントリを指すフィールドをunix_sock含む構造体のカーネルアドレスを含みます。peer仲間 unix_sock。これはlsofUnixソケットの列の出力でもあります。DEVICE

これでこのフィールドの値を取得することは、peerカーネルメモリを読み込み、アドレスpeerに対するこのフィールドunix_sockのオフセットを知ることができることを意味します。

一部gdb- ベースそしてsystemtap- ベース解決策が提供されていますが、実行中のカーネルをインストールするには、gdb/systemtapおよびLinuxカーネルのデバッグシンボルが必要です。これは通常、本番システムには該当しません。

オフセットのハードコーディングはカーネルバージョンからカーネルバージョンに変わるため、実際にはオプションではありません。

これで、経験的な方法を使用してオフセットを決定できます。ツールでダミーオブジェクトを作成しsocketpair(これにより2つのピアのアドレスがわかります)、検索できます。仲間反対側の端にあるメモリの周りのオフセットを決定します。

これはこれを行う概念証明スクリプトですperl(カーネル2.4.27と2.6.32を使用するi386と3.13と3.16を使用してamd64で正常にテストされました)。上記と同様に、以下を包み込む役割をしますlsof

たとえば、

$that-lsof-wrapper -aUc nm-applet
コマンドPIDユーザーFDタイプデバイスサイズ/終了ノード名
nm-applet 4183 Stephane 4u unix 0xffff8800a055eb40 0t0 36888タイプ=STREAM-> 0xffff8800a055e7c0[dbus-daemon,4190,@/tmp/dbus-AiBCXOnuP6]
nm-applet 4183 Stephane 7u unix 0xffff8800a055e440 0t0 36890 タイプ=STREAM-> 0xffff8800a055e0c0[Xorg,3080,@/tmp/.X11-unix/X0]
nm-applet 4183 ステファン 8u unix 0xffff8800a05c1040 0t0 36201 タイプ=STREAM-> 0xffff8800a05c13c0[dbus-daemon,4118,@/tmp/dbus-yxxNr1NkYC]
nm-applet 4183 ステファン 11u unix 0xffff8800a055d080 0t0 36219 タイプ=STREAM-> 0xffff8800a055d400[dbus-daemon,4118,@/tmp/dbus-yxxNr1NkYC]
nm-applet 4183 ステファン 12u unix 0xffff88022e0dfb80 0t0 36221 タイプ=ストリーム-> 0xffff88022e0df800[dbus-daemon, 2268, /var/run/dbus/system_bus_socket]
nm-applet 4183 ステファン 13u unix 0xffff88022e0f80c0 0t0 37025 タイプ=STREAM-> 0xffff88022e29ec00 [dbus-デーモン、2268、/var/run/dbus/system_bus_socket]

スクリプトは次のとおりです。

#! /usr/bin/perl
# wrapper around lsof to add peer information for Unix
# domain sockets. needs lsof, and superuser privileges.
# Copyright Stephane Chazelas 2015, public domain.
# example: sudo this-lsof-wrapper -aUc Xorg
use Socket;

open K, "<", "/proc/kcore" or die "open kcore: $!";
read K, $h, 8192 # should be more than enough
 or die "read kcore: $!";

# parse ELF header
my ($t,$o,$n) = unpack("x4Cx[C19L!]L!x[L!C8]S", $h);
$t = $t == 1 ? "L3x4Lx12" : "Lx4QQx8Qx16"; # program header ELF32 or ELF64
my @headers = unpack("x$o($t)$n",$h);

# read data from kcore at given address (obtaining file offset from ELF
# @headers)
sub readaddr {
  my @h = @headers;
  my ($addr, $length) = @_;
  my $offset;
  while (my ($t, $o, $v, $s) = splice @h, 0, 4) {
    if ($addr >= $v && $addr < $v + $s) {
      $offset = $o + $addr - $v;
      if ($addr + $length - $v > $s) {
        $length = $s - ($addr - $v);
      }
      last;
    }
  }
  return undef unless defined($offset);
  seek K, $offset, 0 or die "seek kcore: $!";
  my $ret;
  read K, $ret, $length or die "read($length) kcore \@$offset: $!";
  return $ret;
}

# create a dummy socketpair to try find the offset in the
# kernel structure
socketpair(Rdr, Wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
 or die "socketpair: $!";
$r = readlink("/proc/self/fd/" . fileno(Rdr)) or die "readlink Rdr: $!";
$r =~ /\[(\d+)/; $r = $1;
$w = readlink("/proc/self/fd/" . fileno(Wtr)) or die "readlink Wtr: $!";
$w =~ /\[(\d+)/; $w = $1;
# now $r and $w contain the socket inodes of both ends of the socketpair
die "Can't determine peer offset" unless $r && $w;

# get the inode->address mapping
open U, "<", "/proc/net/unix" or die "open unix: $!";
while (<U>) {
  if (/^([0-9a-f]+):(?:\s+\S+){5}\s+(\d+)/) {
    $addr{$2} = hex $1;
  }
}
close U;

die "Can't determine peer offset" unless $addr{$r} && $addr{$w};

# read 2048 bytes starting at the address of Rdr and hope to find
# the address of Wtr referenced somewhere in there.
$around = readaddr $addr{$r}, 2048;
my $offset = 0;
my $ptr_size = length(pack("L!",0));
my $found;
for (unpack("L!*", $around)) {
  if ($_ == $addr{$w}) {
    $found = 1;
    last;
  }
  $offset += $ptr_size;
}
die "Can't determine peer offset" unless $found;

my %peer;
# now retrieve peer for each socket
for my $inode (keys %addr) {
  $peer{$addr{$inode}} = unpack("L!", readaddr($addr{$inode}+$offset,$ptr_size));
}
close K;

# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfdn';
while (<LSOF>) {
  if (/(.)(.*)/) {
    $fields{$1} = $2;
    if ($1 eq 'n') {
      $proc{hex($fields{d})}->{"$fields{c},$fields{p}" .
      ($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
    }
  }
}
close LSOF;

# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
  chomp;
  for my $addr (/0x[0-9a-f]+/g) {
    $addr = hex $addr;
    my $peer = $peer{$addr};
    if (defined($peer)) {
      $_ .= $peer ?
            sprintf(" -> 0x%x[", $peer) . join("|", keys%{$proc{$peer}}) . "]" :
            "[LISTENING]";
      last;
    }
  }
  print "$_\n";
}
close LSOF or exit(1);

おすすめ記事