쓰기 프로세스 외부에서 열린 파일 설명자를 읽는 방법

쓰기 프로세스 외부에서 열린 파일 설명자를 읽는 방법

프로세스에서 파일 설명자를 쓸 때 어떻게 파일 설명자를 열고 터미널에 에코합니까?

인수로 지정된 파일 설명자에 로그를 기록하는 백업 프로그램 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.

인용하다:https://man7.org/linux/man-pages/man1/strace.1.html

답변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는 최근 이런 종류의 시스템 호출을 얻었습니다.

  1. 사용pidfd_openPID에서 "PID FD"를 가져옵니다.

  2. 사용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이는 대상 프로세스에 액세스하는 데 필요한 권한이 있는 경우에만 작동하므로 프로세스가 시작된 방법 및 시스템 구성 방법에 따라 상승된 권한(예:)으로 이 코드를 실행 해야 할 수도 있습니다 .

관련 정보