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
。これはlsof
Unixソケットの列の出力でもあります。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);
答え2
カーネル3.3以降、次のものが利用可能です。ss
またはlsof-4.89
それ以上 — 参照Stefan Chazerasの答え。
著者によると、以前のバージョンではlsof
これを見つけることは不可能でした。 Linuxカーネルはこの情報を公開していません。源泉:comp.unix.adminの2003スレッド。
に示されている数字は、/proc/$pid/fd/$fd
仮想ソケットファイルシステム内のソケットのinode番号です。パイプまたはソケットのペアを作成すると、各端は連続してinode番号を受け取ります。番号は順番に割り当てられるため、1つずつ差が出る可能性が高くなりますが、これは保証されません(最初のソケットは窒素そして窒素+1 はパッケージ化のためにすでに使用されているか、2 つの inode 割り当ての間に別のスレッドが予約されており、そのスレッドも一部の inode を生成するため使用中です。
確認しましたsocketpair
カーネル2.6.39の定義、ソケットの両端は特定のタイプを除いては関係ありません。socketpair
方法。 Unixソケットの場合unix_socketpair
存在するnet/unix/af_unix.c
。
答え3
カーネル3.3から開始
# ss -xp
これで、列の他のIDに対応するPeer
列のID(インデックスノード番号)が表示されます。Local
一致するIDはソケットの2つの端です。
注:UNIX_DIAG
このオプションはカーネルで有効にする必要があります。
カーネル3.3以前
Linuxはこの情報をユーザースペースに公開しません。
しかし、カーネルメモリの表示、私達はこの情報にアクセスできます。
gdb
注:この回答は次のように実装されています。@StéphaneChazelasからの返信この部分はもう少し詳しく説明されています。
# lsof | grep whatever
mysqld 14450 (...) unix 0xffff8801011e8280 (...) /var/run/mysqld/mysqld.sock
mysqld 14450 (...) unix 0xffff8801011e9600 (...) /var/run/mysqld/mysqld.sock
2つの異なるソケットがあり、1つはリスニング中で、1つは設定されます。 16進数はそのコアのアドレスです。unix_sock
構造、1つのpeer
属性はアドレスですソケットのもう一方の端(またunix_sock
構造体インスタンス)。
これで、次を使用してカーネルメモリをgdb
見つけることができます。peer
# gdb /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((struct unix_sock*)0xffff8801011e9600)->peer
$1 = (struct sock *) 0xffff880171f078c0
# lsof | grep 0xffff880171f078c0
mysql 14815 (...) unix 0xffff880171f078c0 (...) socket
ここでソケットのもう一方の端はPID 14815によって保持されますmysql
。
カーネルをKCORE_ELF
使用するにはコンパイルする必要があります/proc/kcore
。また、デバッグシンボルを含むカーネルイメージのバージョンも必要です。 Debian 7では、apt-get install linux-image-3.2.0-4-amd64-dbg
このファイルを使用できます。
デバッグ可能なカーネルイメージは必要ありません...
システムにデバッグカーネルイメージがない場合、または保持したくない場合は、「手動で」値にgdb
アクセスするためのメモリオフセットを提供できます。peer
このオフセットは通常カーネルのバージョンやアーキテクチャによって異なります。
私のカーネルでは、オフセットは680バイト、つまり64ビットの85倍であることがわかっています。だから私はこれを行うことができます:
# gdb /boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((void**)0xffff8801011e9600)[85]
$1 = (void *) 0xffff880171f078c0
チャジャン、結果は上記と同じです。
複数のシステムで同じカーネルを実行している場合は、デバッグイメージは必要なくオフセット値のみが必要なため、このバリエーションを使用する方が簡単です。
最初にこのオフセット値を(簡単に)見つけるには、実際に画像をデバッグする必要があります。
$ pahole -C unix_sock /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
struct unix_sock {
(...)
struct sock * peer; /* 680 8 */
(...)
}
680バイトは85 x 64ビット、つまり170 x 32ビットです。
この回答のほとんどのクレジットは次のとおりです。平均電圧。
答え4
このソリューションは有効ですが、関心は限られています。十分な新しいシステムタブがあると、次に使用できる十分に新しいカーネルがある可能性が高いからです。
ss
に基づいて 方法、古いカーネルを使用している場合その他のソリューション、もっとマグカット操作性が高く、追加のソフトウェアは必要ありません。
systemtap
この種のタスクの使用方法を示すのにまだ便利です。
systemtapを使用できる最新のLinuxシステム(1.8以降)を使用している場合は、次のスクリプトを使用して次の出力を後処理できますlsof
。
たとえば、
$ lsof -aUc nmアプレットsudoスクリプト| コマンド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]
(上記に0xffff ...の代わりに0x0000000000000000が表示されている場合、sysctlパラメータがシステムに設定され、カーネルポインタが許可されていないプロセスから非表示になるためです。この場合、意味のある結果を得るにはrootとして実行する必要がありkernel.kptr_restrict
ます)lsof
。
スクリプトは改行文字を含むソケットファイル名を処理しようとしませんが、そうではありませんlsof
(lsof
スペースやコロンも処理しません)。
systemtap
unix_sock
これは、カーネルのハッシュ内のすべての構造のアドレスとピアアドレスをダンプするために使用されます。unix_socket_table
systemtap 2.6を含むLinux 3.16 amd64および2.3を含む3.13でのみテストされました。
#! /usr/bin/perl
# meant to process lsof output to try and find the peer of a given
# unix domain socket. Needs a working systemtap, lsof, and superuser
# privileges. Copyright Stephane Chazelas 2015, public domain.
# Example: lsof -aUc X | sudo this-script
open STAP, '-|', 'stap', '-e', q{
probe begin {
offset = &@cast(0, "struct sock")->__sk_common->skc_node;
for (i = 0; i < 512; i++)
for (p = @var("unix_socket_table@net/unix/af_unix.c")[i]->first;
p;
p=@cast(p, "struct hlist_node")->next
) {
sock = p - offset;
printf("%p %p\n", sock, @cast(sock, "struct unix_sock")->peer);
}
exit()
}
};
my %peer;
while (<STAP>) {
chomp;
my ($a, $b) = split;
$peer{$a} = $b;
}
close STAP;
my %f, %addr;
open LSOF, '-|', 'lsof', '-nPUFpcfdn';
while (<LSOF>) {
if (/(.)(.*)/) {
$f{$1} = $2;
if ($1 eq 'n') {
$addr{$f{d}}->{"$f{c},$f{p}" . ($f{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
while (<>) {
chomp;
for my $addr (/0x[0-9a-f]+/g) {
my $peer = $peer{$addr};
if (defined($peer)) {
$_ .= $peer eq '0x0' ?
"[LISTENING]" :
" -> $peer\[" . join("|", keys%{$addr{$peer}}) . "]";
last;
}
}
print "$_\n";
}