當從進程寫入檔案描述符時,如何開啟檔案描述符並將其回顯到終端?
我有備份程式 Duplicity,它將其日誌寫入參數指定的檔案描述符--log-fd=16
。
果然,如果我運行lsof -p <duplicity PID>
我會看到:
python2 9224 myuser 0r CHR 1,3 0t0 6 /dev/null
python2 9224 myuser 1w CHR 1,3 0t0 6 /dev/null
python2 9224 myuser 2w CHR 1,3 0t0 6 /dev/null
python2 9224 myuser 3u a_inode 0,11 0 7005 [eventfd]
python2 9224 myuser 4u unix 0x0000000000000000 0t0 158199 type=STREAM
python2 9224 myuser 5u a_inode 0,11 0 7005 [eventfd]
python2 9224 myuser 6u a_inode 0,11 0 7005 [eventfd]
python2 9224 myuser 7r DIR 8,3 4096 22414346 <some random file being accessed during the backup>
python2 9224 myuser 8r CHR 1,9 0t0 11 /dev/urandom
python2 9224 myuser 15r FIFO 0,10 0t0 157054 pipe
python2 9224 myuser 16w FIFO 0,10 0t0 157054 pipe
但是,如果我嘗試在 Python 中開啟檔案描述符,則會收到錯誤:
>>> import os
>>> os.fdopen(16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 9] Bad file descriptor
為什麼是這樣?如何讀取檔案描述符?
答案1
使用strace
(追蹤系統調用和信號)。
用法:
sudo strace -p <PID of writing process> -s 9999 -e write=<corresponding FD>
從手冊頁:
-p pid Attach to the process with the process ID pid and begin tracing. The trace may be terminated
at any time by a keyboard interrupt signal (CTRL-C). strace will respond by detaching itself
from the traced process(es) leaving it (them) to continue running. Multiple -p options can
be used to attach to many processes in addition to command (which is optional if at least one
-p option is given). -p "`pidof PROG`" syntax is supported.
-s strsize Specify the maximum string size to print (the default is 32). Note that filenames are not
considered strings and are always printed in full.
-e read=set
Perform a full hexadecimal and ASCII dump of all the data read from file descriptors listed in the
specified set. For example, to see all input activity on file descriptors 3 and 5 use
-e read=3,5. Note that this is independent from the normal tracing of the read(2) system call
which is controlled by the option -e trace=read.
-e write=set
Perform a full hexadecimal and ASCII dump of all the data written to file descriptors listed in
the specified set. For example, to see all output activity on file descriptors 3 and 5 use
-e write=3,5. Note that this is independent from the normal tracing of the write(2) system call
which is controlled by the option -e trace=write.
答案2
我相信口是心非的--log=fd
選項適用於您想要與日誌分離的複雜stderr
管道stdout
。
這個答案對這個問題舉個例子。這是一個簡單的例子:
#!/bin/sh
# Generate output on three different fds
echo hello >&3
echo world >&2
echo today >&1
像這樣執行時,
./foo 2> 2.log 3> 3.log 1> 1.log
結果是
$ cat 1.log 2.log 3.log
today
world
hello
答案3
Linux 最近獲得了針對此類事情的系統呼叫:
使用
pidfd_open
從 PID 取得“PID FD”。使用
pidfd_getfd
透過另一個程序的 PID FD 取得檔案描述符。
從 Python 3.9 開始,pidfd_open
可用作os.pidfd_open
。
pidfd_getfd
尚未透過Python標準庫公開,但幸運的是ctypes
讓我們打電話syscall
,Linux系統呼叫號碼永遠不會改變,Linux系統呼叫API和ABI只是以向後相容的方式改變。
所以!
from ctypes import CDLL, c_int, c_long, c_uint, get_errno
from functools import partial
from os import strerror
_syscall = CDLL(None, use_errno=True).syscall
# Non-variadic system call number argument:
_syscall.argtypes = [c_long]
def pidfd_getfd(pidfd, targetfd):
fd = _syscall(
438, # system call number of pidfd_getfd
c_int(pidfd),
c_int(targetfd),
c_uint(0), # unused "flags" argument
)
if fd == -1:
errno = get_errno()
raise OSError(errno, strerror(errno))
return fd
因此,在您的範例中,感興趣的 PID 為 9224,os.fdopen(16)
您無需呼叫 ,而是執行os.fdopen(pidfd_getfd(os.pidfd_open(9224), 16))
。
請注意,只有當您擁有存取目標進程所需的權限時,這才有效,因此您可能需要使用提升的權限(例如sudo
)執行此程式碼,具體取決於進程的啟動方式以及系統的配置方式。