Yüksek trafikli web sunucularında (Nginx, Node.js, Rust, Go) sistem çağrılarının ve CPU bağlam değişiminin (context switch) getirdiği ek yükü hesaplayın. Klasik epoll ile modern io_uring (Batched ve SQPOLL) arasındaki vCPU tasarrufunu, KPTI cezasını ve tavan istek kapasitesini simüle edin.
Kullanıcı/çekirdek sayfa tablosu takası nedeniyle her sistem çağrısına fazladan ~115 ns gecikme ekler.
Bellek sayfalarının her I/O'da pin/unpin edilmesini önleyerek 25 ns ek tasarruf sağlar.
Linux sysctl, Nginx 1.25+ aio io_uring, Rust tokio-uring ve C liburing örnekleri.
# /etc/sysctl.d/99-io-uring.conf # Linux io_uring Cekirdek Yetkilendirme ve Kilitli Bellek Limitleri # 0 = Tum kullanicilara acik (Onerilen yuksek performansli sunucu modu) # 1 = Yalnizca CAP_SYS_ADMIN yetkisine sahip surecler kullanabilir # 2 = io_uring tamamen devre disi kernel.io_uring_disabled = 0 # Kayitli sabit tamponlar (registered buffers) icin kilitli bellek limiti artirilmalidir # /etc/security/limits.d/99-io-uring.conf icerisinde: # * soft memlock unlimited # * hard memlock unlimited
1990'lı yıllarda Unix sistemleri select() ve poll() ile soket durumlarını takip ediyordu. Ancak bu mekanizmalar her çağrıda tüm soket dizisini çekirdeğe kopyaladığı için O(N) karmaşıklığına sahipti ve C10K probleminde tıkandı. 2002 yılında Linux 2.6 ile gelen epoll, durumu çekirdekte saklayarak O(1) karmaşıklığıyla modern internet devrimini başlattı.
Ancak günümüzde ağ hızları 10 Gbps - 100 Gbps seviyesine ve NVMe sürücüleri milyonlarca IOPS'a ulaştığında yeni bir darboğaz ortaya çıktı: Sistem çağrısı maliyeti (Syscall Overhead). Bir web sunucusu epoll kullanırken her istek için en az 3 ila 6 kez kullanıcı alanı ile çekirdek alanı arasında gidip gelmek (context switch) zorundadır. Jens Axboe tarafından Linux 5.1'de çekirdeğe dahil edilen io_uring, bu geçişleri tamamen ortadan kaldırmak üzere tasarlanmıştır.
io_uring, kullanıcı alanı uygulaması ile Linux çekirdeği arasında iki adet kilit içermeyen dairesel tampon (ring buffer) tahsis eder:
mmap() ile hem uygulamanın hem çekirdeğin adres alanına aynı anda haritalanır; bellek kopyalama yapılmaz.Modern işlemcilerde Meltdown ve Spectre güvenlik açıklarını kapatmak için kullanılan Kernel Page Table Isolation (KPTI), her sistem çağrısında işlemciye sayfa tablolarını takas ettirir:
KPTI aktifken standart bir sistem çağrısı 70 ns yerine 180-200 ns sürer. Saniyede 100.000 istek alan bir sunucu epoll kullanıyorsa, yalnızca sistem çağrılarına saniyede 300.000 ila 500.000 kez girip çıkarak 1 ila 2 tam vCPU çekirdeğini çöpe atar. io_uring ise 32 veya 64 işlemi tek bir çağrıda göndererek bu kaybı %95 oranında engeller.
En uç performans gerektiren veri tabanlarında ve yüksek frekanslı ağ ağ geçitlerinde IORING_SETUP_SQPOLL bayrağı kullanılır. Bu modda çekirdek arka planda bağımsız bir thread çalıştırır ve Gönderme Halkasını sürekli olarak dinler (busy-polling). Uygulama sokete veri yazarken veya diskten okurken hiçbir sistem çağrısı yapmaz; doğrudan paylaşılan bellek alanına yazar. Çekirdek thread'i veriyi anında alıp ağ kartına iletir. Bu mod 1 vCPU çekirdeğini tamamen meşgul etse de, gecikmeyi ve CPU dalgalanmalarını mutlak sıfıra yaklaştırır.
1. Güvenlik ve kernel.io_uring_disabled: Bazı Linux dağıtımları (Debian, Ubuntu) eski çekirdeklerdeki CVE geçmişi nedeniyle io_uring'i varsayılan olarak kısıtlayabilir. Üretim sunucunuzda sysctl -w kernel.io_uring_disabled=0 yaparak tüm servislerin kullanımına açabilirsiniz.
2. Nginx Disk AIO Kullanımı: Statik medya, video streaming veya yüksek boyutlu dosya indirmesi sunan Nginx sunucularında aio io_uring; directio 4m; yapılandırması, disk I/O blokajlarını tamamen ortadan kaldırarak sayfa yükleme hızını belirgin biçimde artırır.
3. Sabit Tamponlar (Registered Buffers): Rust (tokio-uring) veya C kütüphanelerinde io_uring_register_buffers kullanıldığında bellek sayfaları önceden çekirdeğe kilitlenir; her I/O işleminde get_user_pages kilit yarışı yaşanmaz.