Latar Belakang:
Overhead panggilan sistem jauh lebih besar daripada overhead panggilan fungsi (perkiraan berkisar antara 20-100x) sebagian besar karena pergantian konteks dari ruang pengguna ke ruang kernel dan kembali. Adalah umum untuk fungsi sebaris untuk menyimpan overhead panggilan fungsi dan panggilan fungsi jauh lebih murah daripada syscalls. Masuk akal bahwa pengembang ingin menghindari beberapa overhead panggilan sistem dengan menjaga sebanyak mungkin operasi dalam kernel dalam satu syscall mungkin.
Masalah:
Hal ini telah menciptakan banyak (berlebihan?) Panggilan sistem seperti sendmmsg () , recvmmsg () serta chdir, terbuka, lseek dan / atau kombinasi symlink seperti: 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
dll ...
Sekarang Linux telah menambahkan copy_file_range()
yang tampaknya menggabungkan read lseek dan write syscalls. Hanya masalah waktu sebelum ini menjadi fcopy_file_range (), lcopy_file_range (), copy_file_rangeat (), fcopy_file_rangeat () ... dan lcopy_file_rangeat () ... tetapi karena ada 2 file yang terlibat, bukan X lebih banyak panggilan, itu bisa menjadi X ^ 2 lebih. OK, Linus dan berbagai pengembang BSD tidak akan membiarkannya sejauh itu, tetapi poin saya adalah bahwa jika ada syscall batching, semua (sebagian besar?) Ini dapat diimplementasikan di ruang pengguna dan mengurangi kompleksitas kernel tanpa menambahkan banyak jika ada overhead di sisi libc.
Banyak solusi kompleks telah diusulkan yang mencakup beberapa bentuk syscall thread khusus untuk syscalls non-blocking ke syscalls proses batch; namun metode ini menambah kompleksitas yang signifikan pada kernel dan ruang pengguna dengan cara yang hampir sama dengan libxcb vs libX11 (panggilan asinkron membutuhkan lebih banyak pengaturan)
Larutan?:
Syscall batching generik. Ini akan mengurangi biaya terbesar (beberapa mode switch) tanpa kerumitan yang terkait dengan memiliki utas kernel khusus (meskipun fungsionalitas itu dapat ditambahkan kemudian).
Pada dasarnya sudah ada dasar yang baik untuk prototipe di soketcall () syscall. Hanya perluas dari mengambil array argumen untuk bukannya mengambil array pengembalian, pointer ke array argumen (yang termasuk nomor syscall), jumlah syscalls dan argumen flags ... sesuatu seperti:
batch(void *returns, void *args, long ncalls, long flags);
Satu perbedaan utama adalah bahwa argumen mungkin semua harus menjadi petunjuk untuk kesederhanaan sehingga hasil dari syscall sebelumnya dapat digunakan oleh syscalls berikutnya (misalnya deskriptor file dari open()
untuk digunakan di read()
/ write()
)
Beberapa kemungkinan keuntungan:
- lebih sedikit ruang pengguna -> ruang kernel -> perpindahan ruang pengguna
- switch kompiler yang mungkin -fcombine-syscalls untuk mencoba melakukan batch secara otomatis
- panji opsional untuk operasi asinkron (kembalikan fd untuk menonton segera)
- kemampuan untuk mengimplementasikan fungsi syscall gabungan di masa depan dalam userspace
Pertanyaan:
Apakah layak untuk menerapkan syscall batching?
- Apakah saya kehilangan beberapa Gotcha yang jelas?
- Apakah saya melebih-lebihkan manfaatnya?
Apakah ada gunanya bagi saya untuk repot menerapkan syscall batching (saya tidak bekerja di Intel, Google atau Redhat)?
- Saya telah menambal kernel saya sendiri sebelumnya, tetapi takut berurusan dengan LKML.
- Sejarah telah menunjukkan bahwa meskipun sesuatu secara luas bermanfaat bagi pengguna "normal" (pengguna akhir non-perusahaan tanpa akses tulis git), itu mungkin tidak akan pernah diterima di hulu (unionfs, aufs, cryptodev, tuxonice, dll ...)
Referensi:
sumber
batch
memasukkan syscalls ke dalambatch
syscalls, Anda dapat membuat pohon panggilan yang dalam secara sewenang-wenang dari syscall yang sewenang-wenang. Pada dasarnya, Anda dapat menempatkan seluruh aplikasi Anda menjadi satu syscall.Jawaban:
Saya mencoba ini di x86_64
Patch terhadap 94836ecf1e7378b64d37624fbb81fe48fbd4c772: (juga di sini https://github.com/pskocik/linux/tree/supersyscall )
Dan tampaknya berfungsi - saya dapat menulis halo ke fd 1 dan dunia ke fd 2 hanya dengan satu syscall:
Pada dasarnya saya menggunakan:
sebagai prototipe syscall universal, yang tampaknya merupakan cara kerja x86_64, jadi syscall "super" saya adalah:
Ia mengembalikan jumlah syscalls yang dicoba (
==Nargs
jikaSUPERSYSCALL__continue_on_failure
bendera dilewatkan, jika tidak>0 && <=Nargs
) dan kegagalan untuk menyalin antara ruang kernel dan ruang pengguna ditandai oleh segfault bukannya biasa-EFAULT
.Apa yang saya tidak tahu adalah bagaimana ini akan port ke arsitektur lain, tetapi tentu akan menyenangkan untuk memiliki sesuatu seperti ini di kernel.
Jika ini memungkinkan untuk semua lengkungan, saya membayangkan mungkin ada pembungkus userspace yang akan memberikan keamanan tipe melalui beberapa serikat pekerja dan makro (bisa memilih anggota serikat berdasarkan nama syscall dan semua serikat pekerja kemudian akan dikonversi ke 6 rindu atau apa pun yang setara dengan arsitektur 6 jam dari).
sumber
open
dalamwrite
danclose
. Itu akan meningkatkan kompleksitas sedikit karena mendapatkan / put_user, tetapi mungkin sepadan. Mengenai portabilitas IIRC, beberapa arsitektur dapat mengalahkan register syscall untuk args 5 dan 6 jika syscall 5 atau 6 arg disatukan ... menambahkan 2 arg tambahan untuk penggunaan di masa mendatang akan memperbaikinya dan dapat digunakan di masa depan untuk parameter panggilan asinkron jika bendera SUPERSYSCALL__async disetelDua gotchas utama yang langsung terlintas dalam pikiran adalah:
Penanganan kesalahan: setiap syscall dapat diakhiri dengan kesalahan yang perlu diperiksa dan ditangani oleh kode ruang pengguna Anda. Karena itu panggilan batching harus menjalankan kode ruang pengguna setelah setiap panggilan individu sehingga manfaat dari panggilan batch ruang kernel akan dinegasikan. Selain itu, API harus sangat kompleks (jika mungkin dirancang sama sekali) - misalnya bagaimana Anda mengekspresikan logika seperti "jika panggilan ketiga gagal, lakukan sesuatu dan lewati panggilan keempat tetapi lanjutkan dengan yang kelima")?
Banyak panggilan "gabungan" yang sebenarnya diimplementasikan menawarkan manfaat tambahan selain dari tidak harus berpindah antara ruang pengguna dan kernel. Misalnya, mereka akan sering menghindari menyalin memori dan menggunakan buffer sama sekali (misalnya mentransfer data langsung dari satu tempat di buffer halaman ke yang lain alih-alih menyalinnya melalui buffer perantara). Tentu saja, ini hanya masuk akal untuk kombinasi panggilan tertentu (mis. Baca-lalu-tulis), bukan untuk kombinasi panggilan batch yang sewenang-wenang.
sumber