[Linux] 找出執行的程式中,哪邊在建立 Unix domain socket

[Linux] 找出執行的程式中,哪邊在建立 Unix domain socket

今天遇到客戶回報一個奇怪的問題,

專案的 Python 程序似乎開了許多檔案,把 fd 都用光了,

導致程序再也不能開啟新的檔案…

 

看一下 /proc/<PID>/fd,發現有許多 socket 檔案,

下面只列出其中一個:

# ll /proc/40931/fd/

lrwx------ 1 root root 64 Jan 17 16:42 12 -> socket:[383853277]

 

但這 socket 檔是連到哪去呢?

用  lsof 去找那個 socket fd 話,可以知道它是一個 unix domain socket,

但不知道它的路徑:

 # lsof -n | egrep 383853277

COMMAND     PID   TID           USER   FD      TYPE             DEVICE    SIZE/OFF       NODE NAME
python3   40931                 root   12u     unix 0xffff8e346aee5500         0t0  383853277 socket

 

用  netstat 去找那個 socket fd 的話,得到的資訊也差不多:

# netstat -anp -x

Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
unix  3      [ ]         STREAM     CONNECTED     383853277 40931/python3

 

這要怎麼才能找到,是哪邊產生了這個 unix domain socket 呢?

研究了一下,想到的有下面幾個:

 

1. 用單步執行

如果這是在程式一跑起來時,就會產生的 unix domain socket,

可以試著單步執行程式 (像是使用 python 的 pdb),來找出是哪一段程式造成的。

但如果是程式跑了好一陣子才出來的,這個方法就不太適用…

 

2. 用 strace 指令

假設那個 unix domain socket 的 fd 都蠻固定的,

比如說,都出現在 /proc/<PID>/fd/12 的話,

那我們可以用  strace 指令監看 open 和 socket 這兩個 API,

然後找出 fd = 12 的前後文,來推敲出可能的程式區段。

 

以下面為例,fd = 12 的出現了好幾次,

這是因為可能開檔 fd = 12 之後又關檔、再次開檔時就還是使用了 12 這個空位。

可以看到 socket() API 被呼叫後也產生出了 fd = 12,

接著 fd 在載入 libf.so 和 libg.so 後穩定變成 13 ,

因此可以從程式裡面,去找出載入 libf.so 和 libg.so 的地方,

往前推看看哪邊可能會建立 unix domain socket:

# strace -f -e open,socket python3 test.py

......
[pid  3906] open("/project/log/u.log.lock", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 12
[pid  3906] socket(PF_LOCAL, SOCK_STREAM, 0) = 12
[pid  3906] socket(PF_LOCAL, SOCK_STREAM, 0) = 13
[pid  3906] socket(PF_LOCAL, SOCK_STREAM, 0) = 13
[pid  3906] open("/project/libf.so", O_RDONLY|O_CLOEXEC) = 13
[pid  3906] open("/project/libg.so", O_RDONLY|O_CLOEXEC) = 13

 

3. 使用 ss 指令

這個  ss 就是我這次學到的新指令,

它可以將系統中所有的 socket 都列出來,非常的好用~

先看一下它的參數說明:

# ss -h

Usage: ss [ OPTIONS ]
       ss [ OPTIONS ] [ FILTER ]
   -h, --help		this message
   -V, --version	output version information
   -n, --numeric	don't resolve service names
   -r, --resolve       resolve host names
   -a, --all		display all sockets
   -l, --listening	display listening sockets
   -o, --options       show timer information
   -e, --extended      show detailed socket information
   -m, --memory        show socket memory usage
   -p, --processes	show process using socket
   -i, --info		show internal TCP information
   -s, --summary	show socket usage summary
   -b, --bpf           show bpf filter socket information

   -4, --ipv4          display only IP version 4 sockets
   -6, --ipv6          display only IP version 6 sockets
   -0, --packet	display PACKET sockets
   -t, --tcp		display only TCP sockets
   -u, --udp		display only UDP sockets
   -d, --dccp		display only DCCP sockets
   -w, --raw		display only RAW sockets
   -x, --unix		display only Unix domain sockets
   -f, --family=FAMILY display sockets of type FAMILY

   -A, --query=QUERY, --socket=QUERY
       QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]

   -D, --diag=FILE     Dump raw information about TCP sockets to FILE
   -F, --filter=FILE   read filter information from FILE
       FILTER := [ state TCP-STATE ] [ EXPRESSION ]

 

像我們要找 unix domain socket,可以下 -x 參數,

加上 -e 參數可以顯示出更多資訊,

如果想把 listen port 這種的也列出來,還可以加上 -a 或 -l 參數。

不過像我們這次是要找的是已連線的 unix domain socket,因此下 -x 和 -e 參數就好了。

 

如下所示,我們本來從 /proc/<PID>/fd 裡看到 fd = 12 時,內容是 socket:[383853277]。

用 383853277 在 ss 指令結果裡搜尋,

可以看到跟它對口 domain socket,是綁在 /tmp/.s.PGSQL.5432 這個路徑上。

而這個是 Python 的 psycopg2 模組建立出來的 domain socket:

# ss -x -e 

Netid State   Recv-Q Send-Q       Local Address:Port        Peer Address:Port
u_str ESTAB   0      0                        * 383853277              * 383854296 <->
u_str ESTAB   0      0       /tmp/.s.PGSQL.5432 383854296              * 383853277 <->

 

用以上方式,可能就可以找出來是哪邊在建立 unix domain socket,

這樣如果有沒關閉的 socket,也比較有機會找出來囉~

 

參考資訊:Find out details of a socket by the socket’s path name?

(本頁面已被瀏覽過 491 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料