So lesen Sie einen offenen Dateideskriptor von außerhalb des Schreibvorgangs

So lesen Sie einen offenen Dateideskriptor von außerhalb des Schreibvorgangs

Wie öffnen Sie einen Dateideskriptor und geben ihn an ein Terminal zurück, während er von einem Prozess beschrieben wird?

Ich habe ein Backup-Programm, Duplicity, das seine Protokolle in einen durch den Parameter angegebenen Dateideskriptor schreibt --log-fd=16.

Und tatsächlich, wenn ich laufe, lsof -p <duplicity PID>sehe ich:

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

Wenn ich jedoch versuche, den Dateideskriptor in Python zu öffnen, erhalte ich eine Fehlermeldung:

>>> import os
>>> os.fdopen(16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 9] Bad file descriptor

Warum ist das so? Wie lese ich den Dateideskriptor?

Antwort1

Verwenden Sie strace(Systemaufrufe und Signale verfolgen).

Verwendung:

sudo strace -p <PID of writing process> -s 9999 -e write=<corresponding FD>

Aus der Handbuchseite:

       -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.

Referenz:https://man7.org/linux/man-pages/man1/strace.1.html

Antwort2

Ich glaube, die Option „Duplicity“ ist für komplexe Pipelines gedacht, bei denen Sie Ihr Protokoll trennen --log=fdmöchten .stderrstdout

Diese Antwort aufdiese Fragegibt ein Beispiel. Hier ist ein einfaches Beispiel:

#!/bin/sh
# Generate output on three different fds
echo hello >&3
echo world >&2
echo today >&1

Und wenn es so ausgeführt wird,

./foo 2> 2.log 3> 3.log 1> 1.log

Ergebnisse in

$ cat 1.log 2.log 3.log
today
world
hello

Antwort3

Linux hat vor kurzem Systemaufrufe für genau diese Dinge erhalten:

  1. Verwendenpidfd_openum ein „PID FD“ von einem PID zu erhalten.

  2. Verwendenpidfd_getfdum einen Dateideskriptor von einem anderen Prozess über seinen PID FD zu erhalten.

Ab Python 3.9 pidfd_openist verfügbar alsos.pidfd_open.

pidfd_getfdist noch nicht in der Python-Standardbibliothek verfügbar, aber zum Glückctypeslass uns anrufensyscall, Linux-Systemaufrufnummern ändern sich nie, und die Linux-Systemaufruf-API und ABI ändern sich nur auf abwärtskompatible Weise.

Also!

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

In Ihrem Beispiel, bei dem die betreffende PID 9224 ist, os.fdopen(16)würden Sie also statt aufrufen Folgendes tun os.fdopen(pidfd_getfd(os.pidfd_open(9224), 16)): .

Beachten Sie, dass dies nur funktioniert, wenn Sie über die erforderlichen Berechtigungen verfügen, um auf den Zielprozess zuzugreifen. Daher müssen Sie diesen Code möglicherweise mit erhöhten Berechtigungen (z. B. sudo) ausführen, je nachdem, wie der Prozess gestartet wurde und wie Ihr System konfiguriert ist.

verwandte Informationen