epoll. Di sini kita akan berbicara tentang bagaimana itu epollmentransfer peristiwa dari ruang kernel ke ruang pengguna, dan bagaimana mode pemicu edge dan level diimplementasikan.
Artikel ini ditulis lebih lambat dari yang lain. Ketika saya mulai mengerjakan materi pertama, kernel Linux stabil terbaru adalah 3.16.1. Dan pada saat penulisan ini, ini sudah versi 4.1. Artikel ini didasarkan pada kode versi kernel ini. Kode, bagaimanapun, tidak banyak berubah, jadi pembaca artikel sebelumnya tidak perlu khawatir tentang fakta bahwa sesuatu dalam implementasi telah banyak berubah.
epoll
Berinteraksi dengan ruang pengguna
Di artikel sebelumnya, saya menghabiskan cukup banyak waktu untuk menjelaskan cara kerja sistem penanganan acara di kernel. Tetapi, seperti yang Anda ketahui, kernel perlu meneruskan informasi tentang kejadian ke program yang berjalan di ruang pengguna agar program dapat menggunakan informasi ini. Ini terutama dilakukan dengan pemanggilan sistem epoll_wait (2) .
Kode untuk fungsi ini dapat ditemukan pada baris 1961 dari berkas tersebut
fs/eventpoll.c. Fungsinya sendiri sangat sederhana. Setelah pemeriksaan cukup normal, itu hanya mendapatkan penunjuk ke eventpolldari deskriptor file dan memanggil fungsi berikut:
error = ep_poll(ep, events, maxevents, timeout);
Fungsi Ep_poll ()
Fungsi ini
ep_poll()dideklarasikan pada baris 1585 dari file yang sama. Ini dimulai dengan memeriksa untuk melihat apakah pengguna telah menetapkan nilai timeout. Jika demikian, fungsi akan menginisialisasi antrian tunggu dan menyetel waktu tunggu ke nilai yang ditentukan oleh pengguna. Jika pengguna tidak mau menunggu, , timeout = 0maka fungsinya segera menuju ke blok kode dengan label check_events:, yang bertanggung jawab untuk menyalin acara.
Jika pengguna telah menentukan nilai
timeout, dan tidak ada peristiwa yang dapat dilaporkan kepadanya (kehadiran mereka ditentukan menggunakan panggilan ep_events_available(ep)), fungsi ep_poll()menambahkan dirinya sendiri ke antrean tunggu ep->wq(ingat apa yang kita bicarakan di artikel ketiga seri ini). Di sana kami menyebutkan bahwa ep_poll_callback()dalam prosesnya, ini mengaktifkan semua proses yang menunggu dalam antrian.ep->wq...
Fungsi tersebut kemudian beralih ke standby dengan memanggil
schedule_hrtimeout_range(). Berikut adalah keadaan di mana proses "tidur" bisa "bangun":
- Batas waktu telah kedaluwarsa.
- Proses tersebut menerima sinyal.
- Peristiwa baru telah muncul.
- Tidak ada yang terjadi, dan penjadwal memutuskan untuk mengaktifkan prosesnya.
Dalam skenario 1, 2, dan 3, fungsi menyetel flag yang sesuai dan keluar dari loop tunggu. Dalam kasus terakhir, fungsi hanya masuk ke mode siaga lagi.
Setelah bagian pekerjaan ini selesai, ia
ep_poll()terus mengeksekusi kode blok check_events:.
Di blok ini, kehadiran peristiwa pertama kali diperiksa, dan kemudian panggilan berikutnya dibuat, di mana yang paling menarik terjadi.
ep_send_events(ep, events, maxevents)
Fungsi
ep_send_events()dideklarasikan pada baris 1546. Ini, setelah panggilan, memanggil fungsi ep_scan_ready_list(), meneruskan panggilan balik ep_send_events_proc(),. Fungsi ep_scan_ready_list()mengulang melalui daftar deskriptor file siap dan panggilan ep_send_events_proc()untuk setiap acara siap yang ditemukannya. Di bawah ini akan menjadi jelas bahwa mekanisme yang melibatkan penggunaan callback diperlukan untuk memastikan keamanan dan penggunaan kembali kode.
Fungsi
ep_send_events()pertama-tama menempatkan data dari daftar deskriptor file siap pakai dari struktur eventpoolke dalam variabel lokalnya. Ini kemudian menetapkan bidang ovfliststruktur eventpoolke NULL(dan defaultnya adalah EP_UNACTIVE_PTR).
Mengapa penulis
epollmenggunakanovflist? Ini dilakukan untuk memastikan efisiensi tinggi epoll! Anda mungkin memperhatikan bahwa setelah daftar deskriptor file siap diambil dari struktur eventpool, itu ep_scan_ready_list()diatur ovflistke NULL. Hal ini menyebabkan ep_poll_callback()tidak mencoba melampirkan acara yang diteruskan ke ruang pengguna kembali ep->rdllist, yang dapat menyebabkan masalah besar. Dengan menggunakan ovflistfungsi ini, ep_scan_ready_list()tidak perlu menahan kunci ep->locksaat menyalin acara ke ruang pengguna. Hasilnya, kinerja solusi secara keseluruhan meningkat.
Setelah itu, itu
ep_send_events_proc()akan melewati daftar deskriptor file siap yang dimilikinya dan memanggil metode mereka lagi.poll()untuk memastikan bahwa peristiwa tersebut benar-benar terjadi. Mengapa epollmemeriksa acara di sini lagi? Ini dilakukan untuk memastikan bahwa acara (atau peristiwa) yang didaftarkan oleh pengguna masih tersedia. Pertimbangkan situasi di mana deskriptor file ditambahkan ke daftar deskriptor siap-ke-file berdasarkan peristiwa EPOLLOUTsaat program pengguna menulis ke deskriptor tersebut. Setelah program selesai menulis, deskriptor file mungkin tidak lagi dapat ditulis. EpollAnda perlu menangani situasi seperti ini dengan benar. Jika tidak, pengguna akan menerima EPOLLOUTsaat operasi tulis diblokir.
Di sini, bagaimanapun, perlu disebutkan satu detail. Fungsi
ep_send_events_proc()melakukan segala upaya untuk memastikan bahwa program ruang pengguna menerima pemberitahuan kejadian yang akurat. Ada kemungkinan, meskipun tidak mungkin, bahwa ketersediaan serangkaian peristiwa akan berubah setelah ep_send_events_proc()pemicu poll(). Dalam kasus ini, program ruang pengguna mungkin menerima pemberitahuan tentang peristiwa yang sudah tidak ada lagi. Inilah sebabnya mengapa dianggap benar untuk selalu menggunakan soket non-pemblokiran saat diterapkan epoll. Ini mencegah aplikasi Anda diblokir secara tidak terduga.
Setelah memeriksa event mask, itu
ep_send_events_proc()hanya menyalin struktur acara ke buffer yang disediakan oleh program ruang pengguna.
Edge-triggered dan level-triggered
Sekarang kita akhirnya bisa membahas perbedaan antara Edge Triggering (ET) dan Level Triggering (LT) dalam hal implementasinya.
else if (!(epi->event.events & EPOLLET)) {
list_add_tail(&epi->rdllink, &ep->rdllist);
}
Sangat mudah! Fungsi
ep_send_events_proc()menambahkan acara kembali ke daftar deskriptor file siap. Akibatnya, pada panggilan berikutnya, ep_poll()deskriptor file yang sama akan diperiksa lagi. Karena itu ep_send_events_proc()selalu memanggil file poll()sebelum mengembalikannya ke aplikasi ruang pengguna, ini sedikit meningkatkan overhead sistem (dibandingkan dengan ET) jika deskriptor file tidak lagi tersedia. Tetapi inti dari semua ini adalah, seperti yang disebutkan di atas, bukan melaporkan peristiwa yang tidak lagi tersedia.
Setelah
ep_send_events_proc()selesai menyalin acara, fungsi tersebut mengembalikan jumlah acara yang disalin ke sana, menjaga aplikasi ruang pengguna tetap mutakhir.
Ketika fungsi telah
ep_send_events_proc()selesai, fungsinyaep_scan_ready_list()perlu dibersihkan sedikit. Pertama, ia mengembalikan ke daftar deskriptor file siap kejadian yang tidak diproses oleh fungsi ep_send_events_proc(). Ini bisa terjadi jika jumlah acara yang tersedia melebihi ukuran buffer yang disediakan oleh program pengguna. Ini juga ep_send_events_proc()dengan cepat melampirkan semua peristiwa dari ovflist, jika ada, kembali ke daftar deskriptor file siap pakai. Selanjutnya, masuk ovflistkembali dicatat EP_UNACTIVE_PTR. Akibatnya, acara baru akan dilampirkan ke daftar tunggu utama ( rdllist). Fungsi keluar dengan mengaktifkan proses "tidur" lainnya jika ada acara lain yang tersedia.
Hasil
Ini menyimpulkan artikel keempat dan terakhir dari seri implementasi
epoll. Saat saya menulis artikel ini, saya terkesan dengan pekerjaan mental yang luar biasa yang telah dilakukan oleh penulis kode kernel Linux untuk mencapai efisiensi dan skalabilitas maksimum. Dan saya berterima kasih kepada semua penulis kode Linux untuk berbagi pengetahuan mereka dengan semua orang yang membutuhkannya dengan membagikan hasil pekerjaan mereka.
Bagaimana perasaan Anda tentang perangkat lunak open source?
