Tło:
Narzut wywołania systemowego jest znacznie większy niż narzut wywołania funkcji (szacowany zakres od 20-100x), głównie z powodu przełączania kontekstu z przestrzeni użytkownika na przestrzeń jądra iz powrotem. Zazwyczaj funkcje wbudowane oszczędzają narzutu wywołania funkcji, a wywołania funkcji są znacznie tańsze niż wywołania systemowe. Jest oczywiste, że programiści chcieliby uniknąć narzutu wywołania systemowego, dbając o jak najwięcej operacji wewnątrz jądra w jednym wywołaniu systemowym.
Problem:
W ten sposób powstało wiele (zbędne?) Wywołań systemowych, takich jak sendmmsg () , recvmmsg () jak również CHDIR, otwarty, lseek i / lub kombinacji dowiązań symbolicznych, takich jak: openat
, mkdirat
, mknodat
, fchownat
, futimesat
, newfstatat
, unlinkat
, fchdir
, ftruncate
, fchmod
, renameat
, linkat
, symlinkat
, readlinkat
, fchmodat
, faccessat
, lsetxattr
, fsetxattr
, execveat
, lgetxattr
, llistxattr
, lremovexattr
, fremovexattr
, flistxattr
, fgetxattr
, pread
, pwrite
itd ...
Teraz Linux dodał, copy_file_range()
który najwyraźniej łączy odczyt lseek i pisanie wywołań systemowych. To tylko kwestia czasu, zanim zmieni się w fcopy_file_range (), lcopy_file_range (), copy_file_rangeat (), fcopy_file_rangeat () i lcopy_file_rangeat () ... ale ponieważ są dwa pliki zaangażowane zamiast X więcej wywołań, może stać się X ^ 2 więcej. OK, Linus i różni programiści BSD nie pozwolili by posunąć się tak daleko, ale chodzi mi o to, że gdyby istniało wsadowe wywołanie systemowe, wszystkie (większość?) Można by zaimplementować w przestrzeni użytkownika i zmniejszyć złożoność jądra bez dodawania dużej ilości jeśli narzut po stronie libc.
Zaproponowano wiele złożonych rozwiązań, które obejmują niektóre specjalne wątki systemowe do nie wywoływania bloków systemowych do przetwarzania wsadowego; jednak metody te znacznie zwiększają złożoność przestrzeni jądra i użytkownika, podobnie jak libxcb vs. libX11 (wywołania asynchroniczne wymagają znacznie większej konfiguracji)
Rozwiązanie?:
Ogólny system wsadowy. Zmniejszyłoby to największy koszt (przełączniki wielu trybów) bez złożoności związanej z posiadaniem specjalistycznego wątku jądra (chociaż tę funkcjonalność można by dodać później).
Zasadniczo istnieje już dobra podstawa dla prototypu w funkcji systemowej socketcall (). Po prostu rozszerz go od pobierania tablicy argumentów, aby zamiast tego pobierał tablicę zwrotów, wskaźnik do tablic argumentów (w tym numer syscall), liczbę syscall i argument flagi ... coś w stylu:
batch(void *returns, void *args, long ncalls, long flags);
Jedną z głównych różnic jest to, że argumenty prawdopodobnie wszystko trzeba być wskaźniki dla uproszczenia tak, że wyniki poprzednich syscalli mogłyby zostać wykorzystane przez kolejnych syscalli (na przykład deskryptor pliku z open()
do zastosowania w read()
/ write()
)
Niektóre możliwe zalety:
- mniejsza przestrzeń użytkownika -> przestrzeń jądra -> przełączanie przestrzeni użytkownika
- możliwy przełącznik kompilatora -fcombine-syscalls, aby spróbować wsadowo zautomatyzować
- opcjonalna flaga dla operacji asynchronicznej (zwróć fd, aby obejrzeć natychmiast)
- możliwość implementacji przyszłych połączonych funkcji syscall w przestrzeni użytkownika
Pytanie:
Czy można wdrożyć syscall wsadowy?
- Czy brakuje mi oczywistych problemów?
- Czy przeceniam korzyści?
Czy warto zawracać sobie głowę wdrażaniem systemu wsadowego (nie pracuję w Intelu, Google ani Redhat)?
- Już wcześniej załatałem własne jądro, ale boję się radzenia sobie z LKML.
- Historia pokazała, że nawet jeśli coś jest bardzo przydatne dla „zwykłych” użytkowników (nie-korporacyjnych użytkowników końcowych bez dostępu do zapisu git), może nigdy nie zostać zaakceptowane powyżej (unionfs, aufs, cryptodev, tuxonice itp.)
Bibliografia:
batch
syscall wbatch
syscall, możesz stworzyć dowolnie głębokie drzewo wywołań arbitralnych syscall. Zasadniczo możesz umieścić całą aplikację w jednym wywołaniu systemowym.