GNU/Linux >> Belajar Linux >  >> Linux

Panggilan sistem Linux tercepat

Yang tidak ada, dan karenanya mengembalikan -ENOSYS dengan cepat.

Dari arch/x86/entry/entry_64.S:

#if __SYSCALL_MASK == ~0
    cmpq    $__NR_syscall_max, %rax
#else
    andl    $__SYSCALL_MASK, %eax
    cmpl    $__NR_syscall_max, %eax
#endif
    ja  1f              /* return -ENOSYS (already in pt_regs->ax) */
    movq    %r10, %rcx

    /*
     * This call instruction is handled specially in stub_ptregs_64.
     * It might end up jumping to the slow path.  If it jumps, RAX
     * and all argument registers are clobbered.
     */
#ifdef CONFIG_RETPOLINE
    movq    sys_call_table(, %rax, 8), %rax
    call    __x86_indirect_thunk_rax
#else
    call    *sys_call_table(, %rax, 8)
#endif
.Lentry_SYSCALL_64_after_fastpath_call:

    movq    %rax, RAX(%rsp)
1:

Gunakan nomor panggilan sistem yang tidak valid sehingga kode pengiriman kembali begitu saja
eax = -ENOSYS alih-alih mengirim ke fungsi penanganan panggilan sistem sama sekali.

Kecuali jika ini menyebabkan kernel menggunakan iret jalur lambat alih-alih sysret / sysexit . Itu mungkin menjelaskan pengukuran yang menunjukkan angka yang tidak valid menjadi 17 siklus lebih lambat dari syscall(SYS_getpid) , karena penanganan kesalahan glibc (pengaturan errno ) mungkin tidak menjelaskannya. Tetapi dari pembacaan saya tentang sumber kernel, saya tidak melihat alasan mengapa itu masih tidak menggunakan sysret sambil mengembalikan -ENOSYS .

Jawaban ini untuk sysenter , bukan syscall . Pertanyaan awalnya mengatakan sysenter / sysret (yang aneh karena sysexit cocok dengan sysenter , sementara sysret cocok dengan syscall ). Saya menjawab berdasarkan sysenter untuk proses 32-bit pada kernel x86-64.

syscall 64-bit asli ditangani lebih efisien di dalam kernel. (Pembaruan; dengan patch mitigasi Meltdown / Spectre, masih dikirimkan melalui C do_syscall_64 dalam 4.16-rc2).

Apa yang terjadi jika Anda menggunakan ABI Linux 32-bit int 0x80 dalam kode 64-bit? T&J memberikan ikhtisar tentang sisi kernel titik masuk panggilan sistem dari mode compat ke kernel x86-64 (entry_64_compat.S ). Jawaban ini hanya mengambil bagian yang relevan dari itu.

Tautan dalam jawaban itu dan ini adalah ke sumber Linux 4.12, yang tidak berisi manipulasi tabel halaman mitigasi Meltdown, sehingga akan signifikan biaya tambahan.

int 0x80 dan sysenter memiliki titik masuk yang berbeda. Anda mencari entry_SYSENTER_compat . AFAIK, sysenter selalu pergi ke sana, bahkan jika Anda menjalankannya dalam proses ruang pengguna 64-bit. Titik masuk Linux mendorong __USER32_CS konstan sebagai nilai CS yang disimpan, sehingga akan selalu kembali ke ruang pengguna dalam mode 32-bit.

Setelah mendorong register untuk membuat struct pt_regs di tumpukan kernel, ada TRACE_IRQS_OFF hook (tidak tahu berapa banyak jumlah instruksi), lalu call do_fast_syscall_32 yang ditulis dalam C. (Native 64-bit syscall pengiriman dilakukan langsung dari asm, tetapi panggilan sistem compat 32-bit selalu dikirim melalui C).

do_syscall_32_irqs_on di arch/x86/entry/common.c cukup ringan:cukup periksa apakah prosesnya dilacak (menurut saya begini caranya strace dapat menghubungkan panggilan sistem melalui ptrace ), lalu

   ...
    if (likely(nr < IA32_NR_syscalls)) {
        regs->ax = ia32_sys_call_table[nr]( ... arg );
    }

    syscall_return_slowpath(regs);
}

AFAIK, kernel bisa menggunakan sysexit setelah fungsi ini kembali.

Jadi jalur pengembaliannya sama apakah EAX memiliki nomor panggilan sistem yang valid atau tidak, dan jelas kembali tanpa pengiriman sama sekali adalah jalur tercepat melalui fungsi itu, terutama di kernel dengan mitigasi Spectre di mana cabang tidak langsung pada tabel penunjuk fungsi akan melalui retpoline dan selalu salah prediksi.

Jika Anda benar-benar ingin menguji sysenter/sysexit tanpa semua overhead tambahan itu, Anda harus memodifikasi Linux untuk menempatkan titik masuk yang lebih sederhana tanpa memeriksa pelacakan atau menekan/membuka semua register.

Anda mungkin juga ingin memodifikasi ABI untuk meneruskan alamat pengirim dalam register (seperti syscall lakukan sendiri) alih-alih disimpan di tumpukan ruang pengguna yang sysenter Linux saat ini ABI melakukannya; itu harus get_user() untuk membaca nilai EIP yang harus dikembalikan.

Jika semua overhead ini adalah bagian dari apa yang ingin Anda ukur, Anda pasti sudah siap dengan eax yang memberi Anda -ENOSYS; paling buruk Anda akan mendapatkan satu cabang tambahan yang hilang dari jangkauan-periksa apakah prediktor cabang panas untuk cabang itu berdasarkan panggilan sistem 32-bit normal.


Dalam tolok ukur ini oleh Brendan Gregg (ditautkan dari posting blog ini yang merupakan bacaan menarik tentang topik ini) close(999) (atau fd lain yang tidak digunakan) disarankan.


Linux
  1. Bagaimana menangani kepanikan kernel Linux

  2. Tabel panggilan sistem Linux atau lembar contekan untuk Majelis

  3. Bagaimana cara meneruskan parameter ke panggilan sistem Linux?

  1. Mengapa ada kebutuhan untuk memodifikasi tabel system call di Linux?

  2. Mengapa mirip Linux Unix jika kernelnya monolitik?

  3. Apakah kernel Linux/Unix yang berbeda dapat dipertukarkan?

  1. Cara memeriksa versi Kernel di Linux

  2. Linux – Metode Panggilan Sistem di Kernel Baru?

  3. x86_64 Perakitan Kebingungan Panggilan Sistem Linux