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
여기에 설명된 두 가지 접근 방식을 결합하고 루프백 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
Resources의 이 유틸리티 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 스티븐 3u 유닉스 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
(그러면 두 피어의 주소를 알 수 있음) 검색할 수 있습니다.동료반대쪽 끝에 있는 메모리 주위의 오프셋을 결정합니다.
이것은 바로 이를 수행하는 개념 증명 스크립트입니다 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은 패키징으로 인해 이미 사용 중이거나 두 inode 할당 사이에 다른 스레드가 예약되어 있고 해당 스레드도 일부 inode를 생성하기 때문에 사용 중입니다.
나는 확인했다socketpair
커널 2.6.39의 정의, 소켓의 두 끝은 특정 유형을 제외하고는 관련이 없습니다.socketpair
방법. 유닉스 소켓의 경우unix_socketpair
존재하다net/unix/af_unix.c
.
답변3
커널 3.3부터 시작
# ss -xp
이제 열의 다른 ID에 해당하는 Peer
열의 ID(인덱스 노드 번호)를 볼 수 있습니다. Local
일치하는 ID는 소켓의 두 끝입니다.
참고: 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
구조, 하나의 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 매개변수가 시스템에 설정되어 커널 포인터가 권한이 없는 프로세스에서 숨겨지기 때문입니다 . 이 경우 의미 있는 결과를 얻으려면 루트로 실행 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";
}