
Was sieht ein Programmierer, wenn er anfängt, mit der Sprache C zu arbeiten? Er sieht fopen
, printf
, scanf
und viele weitere Funktionen. Er sieht alle Arten von open
und mmap
- es scheint, warum markieren Sie sie? Aber im Gegensatz zur ersten Gruppe sind diese beiden Funktionen, wenn sie auf dem Linux-Kernel ausgeführt werden, Systemaufrufe ( tatsächlich kann fast nie ein Systemaufruf einfach als Funktion aufgerufen werden, und daher enthält libc
Wrapper, die Argumente neu packen und manchmal, wie im Fall von open
, wobei alte Systemaufrufe durch allgemeinere ersetzt werden). Im Gegensatz zu den Tausenden von Bibliotheksfunktionen, die auf einem typischen GNU / Linux-System verfügbar sind, verfügt die Kernel-Schnittstelle im Allgemeinen über eine relativ begrenzte Anzahl von Einstiegspunkten - in der Größenordnung von mehreren Hundert. Für den Benutzerbereich ist dies jedoch ein Absturz (z. B. Zugriff auf eine fehlende Seite) für den Kernel - Standardbetriebsart.
In diesem Artikel werde ich Ihnen meiner Meinung nach einige interessante Fakten erzählen. Es wird (wahrscheinlich) keine Details zur Implementierung von futex
und anderen langweiligen futex
. Es wird hauptsächlich das sein, was mich zur Reaktion gebracht hat "Und was, könnte es so sein?!?".
Erstens einige Kommentare zum Text vor kat: Einige Systemaufrufe haben eine optionale Schnittstelle in Form einer Funktion aus einem gemeinsam genutzten Objekt namens vDSO , die der Kernel in den Prozess einfügt . Es gibt nur wenige solcher Funktionen (ungefähr vier, aber die genaue Anzahl hängt anscheinend von der Kernelversion und gettimeofday
) - dies sind alle Arten von time
und gettimeofday
, die zum einen häufig verwendet werden und zum anderen wurden sie implementiert, ohne zu wechseln Kernel-Kontext.
Zweitens endet SIGSEGV nicht immer mit einem Prozessabsturz, aber wir werden userfaultfd
darauf userfaultfd
wenn es um userfaultfd
.
HAFTUNGSAUSSCHLUSS: Denken Sie daran, dass Sie mit den meisten der hier vorgestellten Funktionen Ihr Linux-Programm binden. Dies ist normal, wenn Sie auf diese Weise die optionale Optimierung für einen bestimmten Systemtyp oder eine zusätzliche Funktion durchführen, die ansonsten einfach nicht vorhanden wäre. Ansonsten empfehle ich, darüber nachzudenken, wie ein plattformübergreifender Fallback durchgeführt werden kann.
Allgemeine Fragen
Wie kann das alles für den Anfang debuggt werden? Natürlich hilft uns strace
! Da die strace
der Systemaufrufe begrenzt ist und die meisten strace
"vom Sehen" wissen, wird nicht nur "der Zeiger 0x12345678 wird übergeben" angezeigt, sondern es wird auch beschrieben, was in dieser Struktur in dieser oder jener Richtung übertragen wird. Wenn strace
frisch genug ist, können Sie mit der Option -k
einen Aufrufstapel ausgeben.
Es sieht ungefähr so aus $ strace -k sleep 1 execve("/bin/sleep", ["sleep", "1"], 0x7ffe9f9cce30 /* 60 vars */) = 0 > /lib/x86_64-linux-gnu/libc-2.30.so(execve+0xb) [0xe601b] > /usr/bin/strace(+0x0) [0xa279c] > /usr/bin/strace(+0x0) [0xa41d2] > /usr/bin/strace(+0x0) [0x7090b] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /usr/bin/strace(+0x0) [0x7112a] brk(NULL) = 0x558936ded000 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x20b) [0x1ccdb] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1cd2) [0x1b872] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] arch_prctl(0x3001 /* ARCH_??? */, 0x7fff593c0070) = -1 EINVAL ( ) > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e25) [0x1b9c5] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] access("/etc/ld.so.preload", R_OK) = -1 ENOENT ( ) > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x10cb) [0x1db9b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c12] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x1238) [0x1dd08] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_debug_state+0x73a) [0x11d4a] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_exception_free+0x908) [0x189c8] > /lib/x86_64-linux-gnu/ld-2.30.so() [0xa362] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x41b5) [0xeb35] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_exception+0x65) [0x1ca85] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x4603) [0xef83] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c55] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] fstat(3, {st_mode=S_IFREG|0644, st_size=254851, ...}) = 0 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x1009) [0x1dad9] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_debug_state+0x761) [0x11d71] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_exception_free+0x908) [0x189c8] > /lib/x86_64-linux-gnu/ld-2.30.so() [0xa362] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x41b5) [0xeb35] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_exception+0x65) [0x1ca85] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x4603) [0xef83] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c55] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] mmap(NULL, 254851, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc49621c000 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x1426) [0x1def6] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_debug_state+0x79d) [0x11dad] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_exception_free+0x908) [0x189c8] > /lib/x86_64-linux-gnu/ld-2.30.so() [0xa362] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x41b5) [0xeb35] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_exception+0x65) [0x1ca85] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x4603) [0xef83] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c55] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] close(3) = 0 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x10fb) [0x1dbcb] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_debug_state+0x780) [0x11d90] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_exception_free+0x908) [0x189c8] > /lib/x86_64-linux-gnu/ld-2.30.so() [0xa362] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x41b5) [0xeb35] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_exception+0x65) [0x1ca85] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x4603) [0xef83] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c55] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x1238) [0x1dd08] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x7d40] > /lib/x86_64-linux-gnu/ld-2.30.so() [0xa3a8] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x41b5) [0xeb35] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_exception+0x65) [0x1ca85] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x4603) [0xef83] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c55] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360r\2\0\0\0\0\0"..., 832) = 832 > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_error+0x12f8) [0x1ddc8] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x7d79] > /lib/x86_64-linux-gnu/ld-2.30.so() [0xa3a8] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x41b5) [0xeb35] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_catch_exception+0x65) [0x1ca85] > /lib/x86_64-linux-gnu/ld-2.30.so(_dl_rtld_di_serinfo+0x4603) [0xef83] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x3c55] > /lib/x86_64-linux-gnu/ld-2.30.so(__get_cpu_features+0x1e7b) [0x1ba1b] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x203c] > /lib/x86_64-linux-gnu/ld-2.30.so() [0x1108] ... ... brk(NULL) = 0x558936ded000 > /lib/x86_64-linux-gnu/libc-2.30.so(brk+0xb) [0x11755b] > /lib/x86_64-linux-gnu/libc-2.30.so(__sbrk+0x67) [0x117617] > /lib/x86_64-linux-gnu/libc-2.30.so(__default_morecore+0xd) [0x9fd3d] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x2725) [0x9a745] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x3943) [0x9b963] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x3b2b) [0x9bb4b] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x4d9e) [0x9cdbe] > /lib/x86_64-linux-gnu/libc-2.30.so(textdomain+0x740) [0x3be70] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x1d35) [0x35515] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0xbdf) [0x343bf] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x215) [0x339f5] > /bin/sleep() [0x25f0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] brk(0x558936e0e000) = 0x558936e0e000 > /lib/x86_64-linux-gnu/libc-2.30.so(brk+0xb) [0x11755b] > /lib/x86_64-linux-gnu/libc-2.30.so(__sbrk+0x91) [0x117641] > /lib/x86_64-linux-gnu/libc-2.30.so(__default_morecore+0xd) [0x9fd3d] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x2725) [0x9a745] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x3943) [0x9b963] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x3b2b) [0x9bb4b] > /lib/x86_64-linux-gnu/libc-2.30.so(thrd_yield+0x4d9e) [0x9cdbe] > /lib/x86_64-linux-gnu/libc-2.30.so(textdomain+0x740) [0x3be70] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x1d35) [0x35515] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0xbdf) [0x343bf] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x215) [0x339f5] > /bin/sleep() [0x25f0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 > /lib/x86_64-linux-gnu/libc-2.30.so(__open64_nocancel+0x4c) [0x11679c] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x1ce9) [0x354c9] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0xbdf) [0x343bf] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x215) [0x339f5] > /bin/sleep() [0x25f0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] fstat(3, {st_mode=S_IFREG|0644, st_size=8994080, ...}) = 0 > /lib/x86_64-linux-gnu/libc-2.30.so(__fxstat64+0x19) [0x1107b9] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x1e33) [0x35613] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0xbdf) [0x343bf] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x215) [0x339f5] > /bin/sleep() [0x25f0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] mmap(NULL, 8994080, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc495795000 > /lib/x86_64-linux-gnu/libc-2.30.so(mmap64+0x26) [0x11baf6] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x1e5d) [0x3563d] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0xbdf) [0x343bf] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x215) [0x339f5] > /bin/sleep() [0x25f0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] close(3) = 0 > /lib/x86_64-linux-gnu/libc-2.30.so(__close_nocancel+0xb) [0x1165bb] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x1eab) [0x3568b] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0xbdf) [0x343bf] > /lib/x86_64-linux-gnu/libc-2.30.so(setlocale+0x215) [0x339f5] > /bin/sleep() [0x25f0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] nanosleep({tv_sec=1, tv_nsec=0}, NULL) = 0 > /lib/x86_64-linux-gnu/libc-2.30.so(nanosleep+0x17) [0xe5d17] > /bin/sleep() [0x5827] > /bin/sleep() [0x5600] > /bin/sleep() [0x27b0] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xf3) [0x271e3] > /bin/sleep() [0x287e] close(1) = 0 > /lib/x86_64-linux-gnu/libc-2.30.so(__close_nocancel+0xb) [0x1165bb] > /lib/x86_64-linux-gnu/libc-2.30.so(_IO_file_close_it+0x70) [0x92fc0] > /lib/x86_64-linux-gnu/libc-2.30.so(fclose+0x166) [0x85006] > /bin/sleep() [0x5881] > /bin/sleep() [0x2d27] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_secure_getenv+0x127) [0x49ba7] > /lib/x86_64-linux-gnu/libc-2.30.so(exit+0x20) [0x49d60] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xfa) [0x271ea] > /bin/sleep() [0x287e] close(2) = 0 > /lib/x86_64-linux-gnu/libc-2.30.so(__close_nocancel+0xb) [0x1165bb] > /lib/x86_64-linux-gnu/libc-2.30.so(_IO_file_close_it+0x70) [0x92fc0] > /lib/x86_64-linux-gnu/libc-2.30.so(fclose+0x166) [0x85006] > /bin/sleep() [0x5881] > /bin/sleep() [0x2d4d] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_secure_getenv+0x127) [0x49ba7] > /lib/x86_64-linux-gnu/libc-2.30.so(exit+0x20) [0x49d60] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xfa) [0x271ea] > /bin/sleep() [0x287e] exit_group(0) = ? +++ exited with 0 +++ > /lib/x86_64-linux-gnu/libc-2.30.so(_exit+0x36) [0xe5fe6] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_secure_getenv+0x242) [0x49cc2] > /lib/x86_64-linux-gnu/libc-2.30.so(exit+0x20) [0x49d60] > /lib/x86_64-linux-gnu/libc-2.30.so(__libc_start_main+0xfa) [0x271ea] > /bin/sleep(+0x0) [0x287e]
True, Quelldateinamen und Zeilennummern werden hier nicht angezeigt. addr2line
hilft addr2line
(sofern diese Informationen grundsätzlich vorhanden sind).
Es gibt eine zweite Frage: Einige Systemaufrufe haben keine Wrapper in libc
. Dann können Sie den universellen Wrapper namens syscall
:
syscall(SYS_kcmp, getpid(), getpid(), KCMP_FILE, 1, fd)
Eine Datei ist eine sehr seltsame Sache ...
Systemaufrufe sind nicht nur eine Möglichkeit, den Kernel aufzufordern, im Namen des Prozesses auf die Hardware zuzugreifen. Es ist auch eine universelle API, die für alle Bibliotheken im System verständlich ist. Wenn die von Ihnen benötigte Funktionalität in der Bibliothek nicht unterstützt wird, wird sie wahrscheinlich automatisch ausgeführt, wenn Sie den Kernel korrekt anfordern. Darüber hinaus wird ein Teil der „Einstellungen“ des Prozesses von execve
geerbt, sodass Sie versuchen können, dies ohne komplizierte Krücken zu tun, indem Sie einfach den Status vor dem Starten des Prozesses richtig bilden (etwa „Warum sollte stderr
manuell in eine Datei übertragen werden, wenn Sie die Datei nur öffnen und tun können stderr
“) seine FD # 2 für den untergeordneten Prozess ").
Einmal musste ich eine Folge von Netzwerkpaketen von einer Datei subtrahieren. Irgendwann überschritt die Anzahl der Krücken alle vernünftigen Grenzen, und ich entschied, dass libpcap
wahrscheinlich nicht komplizierter sein würde als das, was ich schrieb. Außerdem war es der Standard, und es gab allgemein akzeptierte Tools zum Öffnen dieser Dateien. Es stellte sich heraus, dass das Lesen von Dumps mit libpcap
so schwierig ist wie das fopen
von Dateien: Sie öffnen den Dump einfach mit pcap_(f)open_offline
und pcap_(f)open_offline
Pakete über pcap_next_ex
. Das ist alles! Es lohnt sich immer noch, die Müllkippe nach Abschluss der Arbeiten zu schließen ...
Aber hier ist das Problem: Es scheint, dass libpcap
nicht aus dem Speicher lesen kann. Vielleicht kann er es natürlich, wenn Sie sich damit befassen, aber für unser "Labor" werden wir uns vorstellen, dass er es nicht kann.
Ein Modellbeispiel: Wir warten auf stdin
Folge von Bytes, danach folgt ein Speicherauszug, der mit 4 Bytes ausgerichtet ist. Ich verstehe, dass Sie gepufferte Eingaben und etwas ungetc
(da libpcap
immer noch FILE *
benötigt), aber im Allgemeinen können wir es zum Beispiel unterwegs entpacken oder die Bibliothek kann direkt mit read
/ write
.
Lösung 1: memfd_create
Mit dem memfd_create
können memfd_create
einen "allgemein anonymen" Dateideskriptor erstellen. Die Datei befindet sich im Speicher und ist vorhanden, während mindestens ein Deskriptor darauf geöffnet ist. Im einfachsten Fall erhalten Sie einfach einen solchen Deskriptor, schreiben Daten durch write
, spulen lseek
und fdopen
libc
mit fdopen
darüber:
int fd = memfd_create("pcap-dump-contents", 0); write(fd, buf, length); lseek(fd, 0, SEEK_SET); FILE *file = fdopen(fd, "r");
Der mit dem ersten Argument übergebene Dateiname wird in einem Symlink in /proc/<PID>/fd
angezeigt:
$ ls -l /proc/31747/fd 0 lr-x------ 1 trosinenko trosinenko 64 10 13:12 0 -> /path/to/128test.pcap lrwx------ 1 trosinenko trosinenko 64 10 13:12 1 -> /dev/pts/17 lrwx------ 1 trosinenko trosinenko 64 10 13:12 2 -> /dev/pts/17 lrwx------ 1 trosinenko trosinenko 64 10 13:12 23 -> '/home/trosinenko/.cache/appstream-cache-AH3OA0.mdb (deleted)' lrwx------ 1 trosinenko trosinenko 64 10 13:12 3 -> '/memfd:pcap-dump-contents (deleted)' lrwx------ 1 trosinenko trosinenko 64 10 13:12 57 -> 'socket:[41036]'
Lösung 2: Öffnen Sie mit dem Flag O_TMPFILE
Unter Linux können Sie ab einer Version beim Erstellen einer Datei O_TMPFILE
und den Verzeichnisnamen anstelle des Dateinamens O_TMPFILE
. Infolgedessen scheint die Datei, wie ein literarisches Zeichen sagt (ungefähr), vorhanden zu sein, existiert aber nicht ... Ich weiß nicht, ob die Daten auf die Festplatte geschrieben werden, aber es hängt wahrscheinlich vom Dateisystem ab (im Übrigen sollte dieser Modus unterstützt werden). . Die Datei verschwindet immer noch, wenn der letzte Link geschlossen wird, aber sie kann mit linkat
an den Verzeichnisbaum linkat
:
int fd = open(".", O_RDWR | O_TMPFILE, S_IRUSR | S_IWUSR); assert(fd != -1); assert(write(fd, buffer + offset, len - offset) == len - offset); assert(lseek(fd, 0, SEEK_SET) == 0); const char *link_to = getenv("LINK_TO"); if (link_to != NULL) { char path[128]; snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); linkat(AT_FDCWD, path, AT_FDCWD, link_to, AT_SYMLINK_FOLLOW); }
Zusätzlich zu der Möglichkeit, nicht unter der Benennung der Dateien zu leiden, ist es möglich, die Datei auszufüllen, die Rechte usw. zu konfigurieren und dann eine atomare Verknüpfung mit dem Verzeichnisbaum herzustellen.
Beispiel (für beide Ansätze) #define _GNU_SOURCE #ifdef NDEBUG
$ fallocate -l 128 zero128 $ cat zero128 test.pcap > 128test.pcap $ ./memfd < 128test.pcap Found PCAP dump at offset 128 Read packet: full length = 105 bytes, available 105 bytes. Read packet: full length = 105 bytes, available 105 bytes. Read packet: full length = 66 bytes, available 66 bytes. Read packet: full length = 385 bytes, available 385 bytes. Read packet: full length = 66 bytes, available 66 bytes. ...
userfaultfd: Behandelt Speicherfehler im Userspace
Ich denke, es wird nicht sehr neu sein, zu sagen, dass auf UNIX-ähnlichen Systemen Dateideskriptoren einfach nichts anzeigen. Unter Linux kann dies beispielsweise ein Socket, eine Pipe, ein eventfd oder sogar eine Verknüpfung zu einem ebpf-Programm sein. Aber vielleicht überrascht Sie dieses Beispiel trotzdem. Am Anfang des Artikels habe ich darüber gesprochen, dass Seitenfehler im Kernel häufig vorkommen: Swap, Copy-on-Write, das ist alles ... Wenn der Benutzerprozess „fehlschlägt“, wird SIGSEGV an ihn gesendet. Soweit ich weiß, ist die Rückgabe der Kontrolle vom SIGSEGV-Handler, der vom Kernel generiert wurde, undefiniertes Verhalten, und dennoch gibt es eine GNU- Bibliothek libsigsegv , die die Funktionen zur Behandlung von Speicherzugriffsfehlern auf verschiedenen Plattformen, auch unter Windows, verallgemeinert (ACHTUNG: GPL-Lizenz, falls nicht bereit für verbreiten Sie Ihr Programm, verwenden Sie nicht libsigsegv) . Vor nicht allzu langer Zeit erschien unter Linux eine vollständig dokumentierte Methode namens userfaultfd
: Mit dem gleichnamigen Systemaufruf öffnen Sie einen Dateideskriptor, in dem Sie lesen und schreiben, welche speziellen Strukturen Befehle sind.
Mit einem solchen Dateideskriptor können Sie einen Bereich virtueller Adressen für Ihren Prozess markieren. Danach schläft der Ablauf beim ersten Zugriff auf jede markierte Speicherseite ein, und das Lesen aus dem Dateideskriptor gibt Informationen darüber zurück, was passiert ist. Danach füllt der Handler die Antwortstruktur mit einem Zeiger auf die Daten, die zum Initialisieren der "Problem" -Seite verwendet werden müssen. Der Kernel initialisiert sie und aktiviert den Thread, der sich gedreht hat. In diesem Fall wird davon ausgegangen, dass es einen separaten Datenstrom gibt, zu dessen Aufgaben das Lesen von Befehlen aus dem Deskriptor und das Ausgeben von Antworten gehören. Im Allgemeinen können andere Informationen userfaultfd
über userfaultfd
abgerufen werden, z. B. einige Benachrichtigungen über eine Änderung der virtuellen Prozesskarte.
Anwendungsbeispiel #define _GNU_SOURCE #ifdef NDEBUG
$ ./userfaultfd UFFD open Before reading Fault: addr = 0x7f46f40d5000 Data at 0x7f46f40d5000: abababab
"Die Schlüsselfrage der Mathematik: Ist das alles dasselbe?" ©
Was ist, wenn Sie herausfinden müssen, ob sich dieser Dateideskriptor auf stdin
bezieht? Es scheint, dass if (fd == 0) ...
- und das war's. Na gut ...
#define _GNU_SOURCE #include <unistd.h> #include <stdio.h> int main() { int fd = dup(0); printf("stdin is fd %d, too\n", fd); if (fd == 0) printf("stdin"); else printf("not stdin"); return 0; }
$ gcc kcmp.c -o kcmp $ ./kcmp stdin is fd 3, too not stdin
Ups ... Der Griff ist ähnlich wie einer, aber die Aliase sind unterschiedlich. CRIU - Checkpoint / Restore In Userspace hilft uns dabei . , , . userspace-, , , kcmp
: PID, , , , , :
#define _GNU_SOURCE #include <linux/kcmp.h> #include <syscall.h> #include <unistd.h> #include <stdio.h> int main() { int fd = dup(0); printf("stdin is fd %d, too\n", fd); int pid = getpid(); if (syscall(SYS_kcmp, pid, pid, KCMP_FILE /* _FILES! */, 0 /* stdin fd */, fd) == 0) printf("stdin\n"); else printf("not stdin\n"); if (syscall(SYS_kcmp, pid, pid, KCMP_FILE, 1 /* stdout fd */, fd) == 0) printf("stdout\n"); else printf("not stdout\n"); return 0; }
$ ./kcmp stdin is fd 3, too stdin stdout
! , ...
$ ls -l /proc/self/fd 0 lrwx------ 1 trosinenko trosinenko 64 10 14:45 0 -> /dev/pts/17 lrwx------ 1 trosinenko trosinenko 64 10 14:45 1 -> /dev/pts/17 lrwx------ 1 trosinenko trosinenko 64 10 14:45 2 -> /dev/pts/17 lrwx------ 1 trosinenko trosinenko 64 10 14:45 23 -> '/home/trosinenko/.cache/appstream-cache-AH3OA0.mdb (deleted)' lr-x------ 1 trosinenko trosinenko 64 10 14:45 3 -> /proc/17265/fd lrwx------ 1 trosinenko trosinenko 64 10 14:45 57 -> 'socket:[41036]'
, , , , . , bash , ls
!
$ ./kcmp < kcmp.c stdin is fd 3, too stdin not stdout
, — -, best effort - .
, ...
- … JIT- userspace? , eBPF- . , ( , , -) . ., JIT-, . , , BPF.
- … ? ,
sigaltstack
. - … , :
readahead
oldolduname
...