Ada cukup banyak detail "lebih rendah".
Pertama, pertimbangkan bahwa kernel memiliki daftar proses, dan pada waktu tertentu, beberapa proses ini sedang berjalan, dan beberapa tidak. Kernel memungkinkan setiap proses yang berjalan beberapa waktu CPU, kemudian menyela dan pindah ke yang berikutnya. Jika tidak ada proses yang dapat dijalankan, maka kernel mungkin akan mengeluarkan instruksi seperti HLT ke CPU yang menangguhkan CPU sampai ada gangguan perangkat keras.
Di suatu tempat di server adalah panggilan sistem yang mengatakan "beri aku sesuatu untuk dilakukan". Ada dua kategori luas cara ini bisa dilakukan. Dalam kasus Apache, ia memanggil accept
soket yang sebelumnya telah dibuka oleh Apache, mungkin mendengarkan pada port 80. Kernel mempertahankan antrian upaya koneksi, dan menambahkan ke antrian itu setiap kali TCP SYN diterima. Bagaimana kernel mengetahui TCP SYN diterima tergantung pada driver perangkat; untuk banyak NIC mungkin ada interupsi perangkat keras ketika data jaringan diterima.
accept
meminta kernel untuk kembali kepada saya inisiasi koneksi berikutnya. Jika antrian tidak kosong, maka accept
segera kembali segera. Jika antrian kosong, maka proses (Apache) dihapus dari daftar proses yang berjalan. Ketika suatu koneksi kemudian dimulai, proses dilanjutkan. Ini disebut "blocking", karena untuk proses memanggilnya, accept()
tampak seperti fungsi yang tidak kembali sampai hasilnya, yang mungkin beberapa waktu dari sekarang. Selama waktu itu proses tidak dapat melakukan hal lain.
Setelah accept
kembali, Apache tahu bahwa seseorang sedang mencoba untuk memulai koneksi. Itu kemudian memanggil garpu untuk membagi proses Apache dalam dua proses yang identik. Salah satu dari proses ini berlanjut untuk memproses permintaan HTTP, panggilan lain accept
lagi untuk mendapatkan koneksi berikutnya. Jadi, selalu ada proses utama yang tidak melakukan apa-apa selain memanggil accept
dan menelurkan sub-proses, dan kemudian ada satu sub-proses untuk setiap permintaan.
Ini adalah penyederhanaan: dimungkinkan untuk melakukan ini dengan utas alih-alih proses, dan juga mungkin untuk fork
sebelumnya sehingga ada proses pekerja siap untuk pergi ketika permintaan diterima, sehingga mengurangi overhead startup. Tergantung pada bagaimana Apache dikonfigurasikan, ia dapat melakukan hal-hal ini.
Itulah kategori luas pertama cara melakukannya, dan itu disebut memblokir IO karena sistem memanggil suka accept
dan read
dan write
yang beroperasi pada soket akan menunda proses sampai mereka memiliki sesuatu untuk dikembalikan.
Cara lain yang luas untuk melakukannya disebut non-blocking atau event-based atau asynchronous IO . Ini diimplementasikan dengan panggilan sistem seperti select
atau epoll
. Masing-masing melakukan hal yang sama: Anda memberi mereka daftar soket (atau secara umum, deskriptor file) dan apa yang ingin Anda lakukan dengan mereka, dan kernel memblokir hingga siap untuk melakukan salah satu dari hal-hal itu.
Dengan model ini, Anda dapat memberi tahu kernel (dengan epoll
), "Beri tahu saya ketika ada koneksi baru pada port 80 atau data baru untuk membaca pada salah satu dari 9471 ini koneksi lain yang saya buka". epoll
menghalangi sampai salah satu dari hal-hal itu siap, maka Anda melakukannya. Lalu kamu ulangi. Panggilan sistem seperti accept
dan read
dan write
tidak pernah memblokir, sebagian karena setiap kali Anda memanggil mereka, epoll
hanya mengatakan kepada Anda bahwa mereka siap sehingga tidak akan ada alasan untuk memblokir, dan juga karena ketika Anda membuka soket atau file yang Anda tentukan bahwa Anda menginginkannya dalam mode non-pemblokiran, sehingga panggilan tersebut akan gagal dengan EWOULDBLOCK
alih - alih memblokir.
Keuntungan dari model ini adalah Anda hanya perlu satu proses. Ini berarti Anda tidak perlu mengalokasikan struktur stack dan kernel untuk setiap permintaan. Nginx dan HAProxy menggunakan model ini, dan itu adalah alasan besar mereka dapat menangani lebih banyak koneksi daripada Apache pada perangkat keras yang sama.