Bölüm 16: Linux
Giriş
Öğrenme Çıktıları
UNIX’ten BSD, MINIX, GNU ve Linux’a uzanan çizgiyi; dağıtımları ve Android’i konumlandırmak.
Kütüphane / sistem çağrısı / çekirdek katmanlarını ayırmak; boru hatlarının fork, pipe, dup2 ile kurulduğunu göstermek.
fork/exec/wait, sinyaller, task_struct, clone() ve NPTL; EEVDF ve gerçek zamanlı sınıfları açıklamak.
Adres alanı düzenini, 4/5 düzeyli sayfa tablolarını, buddy + SLUB ayırıcılarını, sayfa önbelleğini ve OOM’u yorumlamak.
Aygıt dosyaları, blk-mq, ağ yığını; VFS, i-node, ext4 extent ve günlükleme, bağlar ve /proc.
rwx/setuid/umask hesaplamak; yetenekler, namespaces + cgroups, SELinux/AppArmor, seccomp ve Android kum havuzunu tanımak.
Giriş ve Tarihçe · Adım adım
UNIX’ten Linux’a Zaman Çizelgesi
Ken Thompson ve Dennis Ritchie UNIX’i 1969’da PDP-7 üzerinde assembly ile yazdı; 1973’te C ile yeniden yazılması taşınabilirliği getirdi. AT&T kaynak kodunu üniversitelere verdi: Berkeley’den BSD doğdu.
Ticari UNIX’ler (System V, SunOS, AIX…) birbirinden ayrışınca POSIX ortak arayüzü tanımladı. Tanenbaum öğretim için MINIX’i yazdı; Linus Torvalds 1991’de MINIX üzerinde kendi çekirdeğini geliştirdi ve GNU araçlarıyla birleştirdi.
Linux 1.0 (1994), 2.0 ile çok işlemci (Bkz. Bölüm 12), 2.6 ile kesilebilir çekirdek ve O(1)/CFS çizelgeleyicileri. 2005’te BitKeeper sorunu üzerine Torvalds git’i yazdı. İlk Android telefonu 2008’de çıktı.
2017’den beri dünyanın en hızlı 500 süper bilgisayarının tamamı Linux çalıştırıyor. 6.x serisinde Rust desteği, EEVDF ve sched_ext geldi; numaralar yalnızca büyüdüğü için artar (6.19’un ardından 7.0).
Giriş ve Tarihçe
GNU, Linux ve Dağıtımlar
| Aile | Örnekler | Paket |
|---|---|---|
| Debian | Debian, Ubuntu, Mint, Pardus | .deb / apt |
| Red Hat | Fedora, RHEL, Rocky, Alma | .rpm / dnf |
| Arch | Arch, Manjaro | pacman |
| SUSE | openSUSE, SLES | zypper |
| Hafif | Alpine (musl + BusyBox) | apk |
Sürüm modeli: ~9–10 haftada bir ana sürüm (2 haftalık birleştirme penceresi + rc1…rc7). Her yılın son sürümü LTS olur: 6.6, 6.12, 6.18…
“Linux” tam olarak yalnızca çekirdeğin adıdır; kullanıcıların kurduğu sistem bir dağıtımdır (GNU/Linux).
Giriş ve Tarihçe
Tasarım Hedefleri ve UNIX Felsefesi
Az sayıda, iyi tanımlı kavram: süreç, dosya, boru.
Küçük, tek iş yapan araçlar birleştirilerek büyük iş yapılır.
“Her şey dosyadır”: disk, terminal, boru aynı read/write ile.
Kabuk betikleri, boru hatları, düzenli ifadeler.
Metin akışları evrensel arayüz; araçlar birbirini bilmeden çalışır.
Sistemi programcılar programcılar için tasarladı: az “sihir”, sessiz başarı.
POSIX bu arayüzü standartlaştırır: aynı kaynak kod Linux, BSD ve macOS’ta derlenebilir.
Kabuk ve Arayüzler · Adım adım
Katmanlar ve Arayüzler: Bir printf’in Yolculuğu
Program printf çağırır. Bu henüz bir sistem çağrısı değildir: glibc metni kullanıcı alanındaki bir tampona yazar.
Tampon boşaltılırken glibc’nin write() sarmalayıcısı çalışır: sistem çağrısı numarasını (x86-64’te write = 1) rax’a, argümanları yazmaçlara koyar ve syscall komutunu yürütür.
CPU çekirdek kipine geçer (Bkz. Bölüm 2). Giriş kodu numarayla sys_call_table’dan işleyiciyi seçer; VFS dosya tanımlayıcısı 1’in arkasındaki nesneyi bulur.
fd 1 bir sözde terminale (pty) bağlıdır; tty katmanı veriyi terminal öykünücüsüne iletir. Disk olsaydı yol sayfa önbelleğine ve blok katmanına giderdi.
Dönüş değeri rax’ta (8 bayt) döner, sysret ile kullanıcı kipine dönülür. Hata olursa negatif değer glibc’de errno’ya çevrilir ve −1 döner.
Kabuk ve Arayüzler
Temel Komutlar ve Yönlendirme
| Kategori | Komutlar | Not |
|---|---|---|
| Dosya / dizin | ls cd pwd mkdir rm -r cp mv touch find tar | touch zaman damgasını günceller; dosya yoksa boş dosya oluşturur |
| Metin (filtre) | cat less head tail grep sort uniq wc echo | stdin’den okur, stdout’a yazar: boru hattında birleşir |
| Süreç / sistem | ps top kill chmod chown df du sync | sync kirli tamponları diske yazar (makineler arası eşitleme: rsync) |
| Ağ | ssh scp ping curl wget traceroute | ip addr, ip route, ss, nft eski ifconfig, route, netstat, iptables’ın yerini aldı |
| Bilgi | man uname free lsblk | /proc komut değil, sözde dosya sistemidir: cat /proc/meminfo |
# yönlendirme: 0 = stdin, 1 = stdout, 2 = stderr sort < girdi.txt > cikti.txt 2> hatalar.log # < okur, > yazar (üzerine) echo "satır" >> gunluk.txt # >> sona ekler make 2>&1 | tee derleme.log & # stderr→stdout, boru, arka plan
Kabuk ve Arayüzler · Adım adım
Boru Hattı: Veri Aşamalardan Akar
Kabuk satırı ayrıştırır, 3 boru ve 4 süreç oluşturur. Süreçler aynı anda çalışır; her biri yalnızca kendi stdin/stdout’unu bilir.
cat dosyanın 7 satırını boruya yazar. Boru tamponu (varsayılan 64 KiB) dolarsa yazan süreç bloke olur: üretici–tüketici (Bkz. Bölüm 6).
grep satır satır okur, eşleşen 5 satırı hemen iletir. Filtre durumsuzdur, akışı bekletmez.
sort ise tüm girdiyi (EOF’a kadar) okumadan çıktı veremez: son satır ilk sırayı değiştirebilir.
uniq -c yalnızca ardışık tekrarları sayar; bu yüzden önce sort gerekir. Sonuç: 2 × “hata: ağ”, 3 × “hata: disk”.
Kabuk ve Arayüzler · Kodu adım adım çalıştır
Kabuk Bir Boru Hattını Nasıl Kurar?
int fd[2]; pipe(fd); /* 3: okuma, 4: yazma */ if (fork() == 0) { /* çocuk 1 */ dup2(fd[1], 1); close(fd[0]); close(fd[1]); execlp("ls", "ls", NULL); } if (fork() == 0) { /* çocuk 2 */ dup2(fd[0], 0); close(fd[0]); close(fd[1]); execlp("wc", "wc", "-l", NULL); } /* kabuk: iki ucu da kapat */ close(fd[0]); close(fd[1]); wait(NULL); wait(NULL);
pipe() çekirdekte bir tampon ve iki uç oluşturur: en küçük boş numaralar 3 (okuma) ve 4 (yazma).
fork(): çocuk, tanımlayıcı tablosunun kopyasını alır; iki süreç aynı boruyu işaret eder.
dup2(4, 1): stdout artık borunun yazma ucu. Fazla uçlar kapatılır.
execlp görüntüyü ls ile değiştirir; tanımlayıcı tablosu korunur. ls hiçbir şey bilmeden boruya yazar.
İkinci çocuk stdin’ini borunun okuma ucuna bağlar ve wc -l olur.
Kabuk iki ucu da kapatır. Kapatmasaydı yazma ucu açık kalır, wc hiç EOF görmez ve sonsuza dek beklerdi. Sonra iki çocuğu bekler.
Çekirdek Yapısı
Linux Çekirdeğinin Yapısı
Üstte sistem çağrısı arayüzü, altta kesmeler ve dağıtıcı (bağlam değiştirme). Tüm bileşenler tek adres alanında çalışır: Linux monolitik bir çekirdektir (Bkz. Bölüm 2). Klasik 4.4BSD çekirdeği de aynı üç bileşene ayrılıyordu.
Çekirdek Yapısı · Adım adım
Yüklenebilir Çekirdek Modülleri (LKM)
Monolitik ama modüler: sürücülerin çoğu yüklenebilir çekirdek modülüdür (.ko). Yönetici modprobe ile ister ya da udev aygıt görününce otomatik yükler.
modprobe, /lib/modules/$(uname -r)/modules.dep dosyasından bağımlılıkları bulur ve önce onları yükler (insmod bunu yapmaz).
finit_module() sistem çağrısı modülü çekirdeğe kopyalar; Secure Boot açıkken imzası doğrulanır. CAP_SYS_MODULE yeteneği gerekir.
Çekirdek modülün yer değiştirmelerini yapar ve kullandığı sembolleri EXPORT_SYMBOL ile dışa açılmış çekirdek fonksiyonlarına bağlar.
module_init fonksiyonu çalışır: sürücü kendini PCI alt sistemine kaydeder (pci_register_driver).
Eşleşen aygıt için probe() çağrılır, ağ arayüzü oluşur. lsmod listeler, rmmod kaldırır. Modül çekirdek ayrıcalığıyla çalışır: hatası tüm sistemi çökertebilir.
Kaynak ağacı ~40 milyon satır; büyük kısmı sürücüler. 6.1’den beri bazı sürücüler Rust ile yazılabiliyor; eBPF güvenli, doğrulanmış küçük programları çekirdekte çalıştırır.
Süreçler
Süreç Ağacı
Her sürecin bir PID’i ve bir ebeveyni (PPID) vardır; ağaç pstree -p veya ps -ef --forest ile görülür. Eski UNIX’te 1 numara init, 2 numara “sayfa cini” idi; Linux’ta PID 1 systemd, sayfa geri kazanımı ise kthreadd altındaki kswapd çekirdek iş parçacığıdır (Bkz. Bölüm 3).
Süreçler · Kodu adım adım çalıştır
fork, exec ve wait
int main(void) { pid_t pid = fork(); if (pid == 0) { /* çocuk */ execlp("ls", "ls", "-l", NULL); _exit(127); /* exec hatası */ } int st; /* ebeveyn */ waitpid(pid, &st, 0); printf("çocuk bitti: %d\n", WEXITSTATUS(st)); return 0; }
Kabuk programı başlatmış; tek süreç var (PID 501).
fork() bir kez çağrılır, iki kez döner: ebeveynde çocuğun PID’i (748), çocukta 0. Bellek kopyalanmaz; sayfalar paylaşılır ve yazınca kopyalanır.
Çocuk execlp ile kendini ls ile değiştirir. Başarılı exec geri dönmez; 5. satır yalnızca hata durumunda çalışır.
Ebeveyn waitpid’de bloke olur (durum S). Hangi sürecin önce CPU alacağı belirsizdir, ama bekleme sayesinde çıktı sırası deterministiktir.
ls çıktısını yazar ve exit(0) ile biter. Çıkış durumu okunana kadar süreç zombi (Z) kalır; ebeveyne SIGCHLD gider.
waitpid döner, zombi toplanır. WEXITSTATUS(st) = 0. Ebeveyn hiç beklemeseydi zombiler birikir, ebeveyn ölünce PID 1 toplardı.
Süreçler
fork ve exec Çekirdekte Ne Yapar?
fork() çekirdekte
- Yeni
task_structayır, ebeveynden doldur, yeni PID ata - Çekirdek yığını ayır; yazmaçları kopyala (çocukta dönüş değeri 0)
- Sayfa tablolarını kopyala, yazılabilir sayfaları salt okunur işaretle (COW)
- Açık dosyaları paylaş:
struct filereferans sayısı artar - Sinyal işleyicileri, cwd, kimlik bilgileri kopyalanır
execve() çekirdekte
- Yürütülebilir dosyayı bul, x iznini denetle, ELF başlığını oku
- argv ve ortam değişkenlerini çekirdeğe kopyala
- Eski adres alanını bırak; yeni
mm_struct, segmentlerimmapet - argv/ortamı yeni yığına yerleştir; setuid bitine göre kimliği ayarla
- Yakalanan sinyalleri varsayılana döndür, close-on-exec fd’leri kapat; PC ← ld.so veya giriş noktası
Eski UNIX fork’ta veri ve yığını gerçekten kopyalardı; Linux yalnızca sayfa tablolarını kopyalar (COW). Çoğu fork’u hemen exec izlediği için kopyalanan sayfa sayısı çok azdır.
Süreçler
POSIX Sinyalleri
Sinyal, bir sürece olay bildiren yazılım kesmesidir. Senkron (SIGSEGV, SIGFPE: sürecin kendi komutu) ya da asenkron (SIGINT, SIGTERM: dışarıdan) olabilir. Süreç her sinyal için varsayılan eylemi, yok saymayı ya da bir işleyici (kaynak temizleme, günlük yazma) seçebilir.
| Sinyal | No | Ne zaman üretilir? | Varsayılan |
|---|---|---|---|
| SIGHUP | 1 | Terminal bağlantısı koptu; cinlerde “yapılandırmayı yeniden oku” | sonlandır |
| SIGINT | 2 | Ctrl-C (ön plan süreç grubuna) | sonlandır |
| SIGQUIT | 3 | Ctrl-\ | çekirdek dökümü |
| SIGILL | 4 | Geçersiz makine komutu | çekirdek dökümü |
| SIGABRT | 6 | abort(): süreç kendini anormal sonlandırır | çekirdek dökümü |
| SIGFPE | 8 | Tamsayı sıfıra bölme gibi aritmetik hata | çekirdek dökümü |
| SIGKILL | 9 | kill -9; yakalanamaz, yok sayılamaz | sonlandır |
| SIGSEGV | 11 | Geçersiz bellek erişimi | çekirdek dökümü |
| SIGPIPE | 13 | Okuyanı kalmamış boruya yazma | sonlandır |
| SIGALRM | 14 | alarm() süresi doldu | sonlandır |
| SIGTERM | 15 | kill komutunun varsayılanı; nazik sonlandırma isteği | sonlandır |
| SIGUSR1/2 | 10/12 | Kullanıcı tanımlı | sonlandır |
| SIGCHLD | 17 | Çocuk süreç bitti/durdu | yok say |
| SIGSTOP / SIGCONT | 19/18 | Durdur (yakalanamaz) / devam ettir; Ctrl-Z → SIGTSTP | durdur / sürdür |
Numaralar x86/ARM Linux içindir. SIGKILL ve SIGTERM’ü ikisi de kill komutuyla gönderilebilir; fark yakalanabilirlikte.
Süreçler · Adım adım
Sinyal Teslimi
Kullanıcı Ctrl-C’ye basar: tty satır disiplini ön plan süreç grubuna SIGINT üretir. Çekirdek yalnızca hedef görevin bekleyen sinyal kümesinde 2. biti açar.
Sinyal hemen işlenmez. Süreç bir sistem çağrısından ya da kesmeden kullanıcı kipine dönerken çekirdek “bekleyen ve maskelenmemiş” sinyal var mı diye bakar.
sigaction ile işleyici kurulmuşsa çekirdek kullanıcı yığınına bir sinyal çerçevesi (kaydedilmiş yazmaçlar + dönüş trampolini) yazar ve PC’yi işleyiciye ayarlar.
İşleyici kullanıcı kipinde çalışır. İçinde yalnızca async-signal-safe fonksiyonlar (ör. write, bir volatile sig_atomic_t bayrağı) güvenlidir; printf, malloc değil.
İşleyici dönünce trampolin rt_sigreturn sistem çağrısını yapar: çekirdek çerçevedeki yazmaçları geri yükler. (sigreturn işleyiciye girmez, işleyiciden döner.)
Süreç kesildiği komuttan devam eder. İşleyici yoksa varsayılan eylem uygulanırdı: SIGINT için sonlandırma.
Süreçler
Süreç Yönetimi Sistem Çağrıları
| Sistem çağrısı | Açıklama |
|---|---|
fork() | Süreci kopyalayarak yeni süreç oluşturur (COW) |
clone(), clone3() | Neyin paylaşılacağı bayraklarla seçilen yeni görev (iş parçacığı, konteyner) |
execve() | Mevcut süreç görüntüsünü yeni programla değiştirir |
waitpid() | Çocuğun bitmesini bekler, çıkış durumunu alır (zombiyi toplar) |
exit() / _exit() | Çağıran süreci sonlandırır (exit stdio tamponlarını boşaltır) |
kill() | Sürece sinyal gönderir; sinyal sonlandırma olmak zorunda değil |
sigaction() | Bir sinyal için işleyici ya da eylem atar |
sigprocmask() | Sinyal maskesini (bloke edilen sinyaller) okur/değiştirir |
sigpending() | Bloke olduğu için bekleyen sinyalleri döndürür |
sigsuspend() | Maskeyi geçici değiştirir ve bir sinyal gelene kadar uyur |
rt_sigreturn() | İşleyiciden dönüşte kaydedilmiş bağlamı geri yükler |
alarm() / pause() | n saniye sonra SIGALRM / bir sinyal gelene kadar uyu |
Sistem çağrısı, kullanıcı programının çekirdekten hizmet istemesinin tek yoludur; Linux x86-64’te ~460 tane vardır. Dosya, G/Ç ve IPC çağrıları ileride.
Süreçler
Süreç Tanımlayıcısı: task_struct
Linux’ta süreç ile iş parçacığı arasında yapısal fark yoktur: ikisi de bir görev (task_struct). Fark, hangi alt yapıları paylaştıklarıdır. Kullanıcının gördüğü PID aslında tgid’dir (iş parçacığı grubu kimliği).
Süreçler · Adım adım
clone() ve İş Parçacıkları
fork() glibc’de bayraksız bir clone()’dur: çocuk her yapının kopyasını alır (bellek COW ile). Ayrı PID, ayrı TGID: yeni bir süreç.
pthread_create() → clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | …). Hepsi paylaşılır; yeni görev aynı TGID’e girer. NPTL: her iş parçacığı bir çekirdek görevi (1:1 model, Bkz. Bölüm 4).
Ad alanı bayrakları (CLONE_NEWPID, CLONE_NEWNS, CLONE_NEWNET…) çocuğa yeni bir görünüm verir: içeride kendini PID 1 sanır. Konteynerlerin temeli budur.
Süreçler
POSIX İş Parçacıkları (pthread) ve futex
| Fonksiyon | İşlev |
|---|---|
pthread_create | Yeni iş parçacığı oluşturur |
pthread_exit | Çağıran iş parçacığını sonlandırır |
pthread_join | Bir iş parçacığının bitmesini bekler |
pthread_mutex_init / destroy | Muteksi başlatır / yok eder |
pthread_mutex_lock / unlock | Muteksi alır (gerekirse bekler) / bırakır |
pthread_cond_init / destroy | Koşul değişkenini başlatır / yok eder |
pthread_cond_wait | Muteksi atomik olarak bırakıp bekler; uyanınca yeniden alır |
pthread_cond_signal | Bekleyenlerden en az birini uyandırır (broadcast: hepsini) |
/* tüketici */ pthread_mutex_lock(&m); while (sayac == 0) /* if değil! */ pthread_cond_wait(&c, &m); sayac--; pthread_mutex_unlock(&m);
futex(FUTEX_WAIT) sistem çağrısıyla uyunur (Bkz. Bölüm 6).Koşul değişkeninde sahte uyanmalar olabilir ve başka bir iş parçacığı koşulu önce tüketebilir; bu yüzden bekleme her zaman while döngüsündedir.
Süreçler
Süreç Durumları
ps/top STAT sütunu bu harfleri gösterir. D durumundaki süreç SIGKILL ile bile hemen ölmez (çoğu yerde artık TASK_KILLABLE kullanılır). Çalışabilir ve çalışıyor Linux’ta aynı harfle (R) gösterilir (Bkz. Bölüm 3).
Çizelgeleme ve Önyükleme
Çizelgeleme Sınıfları
Çizelgeleyici sınıfları sırayla sorar: üst sınıfta çalışabilir görev varsa alttakiler bekler. FIFO görev bırakana kadar çalışır; RR 100 ms dilim kullanır. RT görevler saniyede en fazla 0,95 s alabilir ki sistem kilitlenmesin. 6.12 ile PREEMPT_RT ana çekirdeğe girdi (Bkz. Bölüm 5, 12).
Çizelgeleme ve Önyükleme · Adım adım
CFS’ten EEVDF’ye: Adil Paylaşım
Her görev için v (vruntime) ve d = v + dilim × 1024 / ağırlık. Ağırlık nice’a bağlı: nice 0 → 1024, nice 5 → 335, nice −5 → 3121. V = ağırlıklı ortalama sanal zaman = 6,0. Uygun (v ≤ V, gecikmesi ≥ 0): A, C. Bunlardan en erken sanal son tarih d: A. CFS en küçük v’yi (C) seçerdi.
A 3 ms çalıştı: v’si 3 × 1024 / ağırlık kadar ilerledi. V = ağırlıklı ortalama sanal zaman = 6,5. Uygun (v ≤ V, gecikmesi ≥ 0): B, C. Bunlardan en erken sanal son tarih d: B.
B 3 ms çalıştı: v’si 3 × 1024 / ağırlık kadar ilerledi. V = ağırlıklı ortalama sanal zaman = 7,1. Uygun (v ≤ V, gecikmesi ≥ 0): A, C, D. Bunlardan en erken sanal son tarih d: D.
D 3 ms çalıştı: v’si 3 × 1024 / ağırlık kadar ilerledi. V = ağırlıklı ortalama sanal zaman = 7,6. Uygun (v ≤ V, gecikmesi ≥ 0): A, C. Bunlardan en erken sanal son tarih d: A.
A 3 ms çalıştı: v’si 3 × 1024 / ağırlık kadar ilerledi. V = ağırlıklı ortalama sanal zaman = 8,2. Uygun (v ≤ V, gecikmesi ≥ 0): C, D. Bunlardan en erken sanal son tarih d: D.
D 3 ms çalıştı: v’si 3 × 1024 / ağırlık kadar ilerledi. V = ağırlıklı ortalama sanal zaman = 8,7. Uygun (v ≤ V, gecikmesi ≥ 0): C. Bunlardan en erken sanal son tarih d: C. Sonunda düşük ağırlıklı C’nin sırası gelir; çalışınca v’si 3 × 1024 / 335 ≈ 9,2 ilerler. Uzun vadede herkes ağırlığı oranında CPU alır.
CFS (2.6.23, 2007) kırmızı-siyah ağaçta en küçük vruntime’ı seçiyordu; 6.6 (2023) ile yerini alan EEVDF aynı ağacı kullanır ama gecikmeye duyarlı (kısa dilimli) görevlere daha erken son tarih verir. Öncesinde O(1) çizelgeleyici (2.6.0–2.6.22) etkin/süresi dolmuş dizileri kullanıyordu.
Çizelgeleme ve Önyükleme · Adım adım
Önyükleme: UEFI’den systemd’ye
Güç verilince UEFI ürün yazılımı donanımı sınar ve ESP bölümündeki EFI uygulamasını yükler. Secure Boot açıksa her aşama bir sonrakinin imzasını doğrular (Bkz. Bölüm 13).
Önyükleyici çekirdek görüntüsünü (vmlinuz) ve initramfs’i belleğe yükler, komut satırı parametrelerini verir. Çekirdek EFI uygulaması olarak doğrudan da başlatılabilir (UKI).
Çekirdek kendini açar, start_kernel() bellek yönetimini, kesmeleri, çizelgeleyiciyi kurar. İlk görev PID 0 boşta görevi olur; kernel_init (PID 1) ve kthreadd (PID 2) oluşturulur.
Kök dosya sistemini bağlamak için gereken sürücüler çekirdekte olmayabilir (NVMe, RAID, şifreli disk). initramfs RAM’de geçici bir kök sağlar, gerçek kökü bağlar ve switch_root yapar.
PID 1 artık systemd: birim dosyalarındaki bağımlılıklara göre servisleri paralel başlatır, hedefe (multi-user.target veya graphical.target) ulaşır.
Konsolda agetty → login → kabuk; masaüstünde gdm; uzaktan sshd. Klasik UNIX’teki init → getty → login → sh zinciri hâlâ tanınabilir.
Bellek Yönetimi
Sürecin Sanal Adres Alanı
Her bölge bir VMA (vm_area_struct) ile tanımlanır; VMA’lar mm_struct içinde bir “maple tree”de tutulur. Yığın ve mmap aşağı, öbek yukarı büyür; aradaki boşluk yalnızca sanaldır, fiziksel bellek harcamaz (Bkz. Bölüm 9).
Bellek Yönetimi
Paylaşım, Eşlenmiş Dosyalar ve Bellek Çağrıları
| Çağrı | İşlev |
|---|---|
brk / sbrk | Öbeğin (veri kesiminin) sonunu taşıyarak büyütür/küçültür |
mmap | Dosyayı, aygıtı ya da anonim belleği adres alanına eşler |
munmap | Eşlemeyi kaldırır, belleği sisteme geri verir |
mprotect | Bölgenin izinlerini (r/w/x) değiştirir |
madvise | Çekirdeğe kullanım ipucu verir (ör. MADV_DONTNEED) |
msync | Paylaşımlı eşlemenin kirli sayfalarını dosyaya yazar |
MAP_SHARED: yazılanlar dosyaya ve diğer süreçlere görünür. MAP_PRIVATE: yazınca kopyala, dosya değişmez. glibc malloc küçük istekleri öbekten, ≥ 128 KiB istekleri doğrudan mmap ile karşılar.
Bellek Yönetimi · Adım adım
Sayfa Tablosu Yürüyüşü: 4 ve 5 Düzey
x86-64’te 4 KiB sayfa → 12 bit ofset. Kalan 36 bit, her biri 512 girdilik (9 bit) dört tabloya bölünür. 63–48. bitler 47. bitin kopyasıdır (kanonik adres).
CR3 yazmacı sürecin en üst tablosunu (PGD) gösterir; bağlam değiştirmede CR3 değişir. Dizin 254 → PUD tablosunun adresi.
PUD[232] bir PMD tablosunu gösterir. (Burada 1 GiB’lık dev sayfa da bitebilir.)
PMD[225] bir PTE tablosunu gösterir. (2 MiB’lık dev sayfa / THP burada biter.)
PTE[181]: geçerli (present) biti, çerçeve numarası, R/W, U/S, NX, kirli (D) ve erişildi (A) bitleri. Present = 0 ise sayfa hatası (Bkz. Bölüm 9).
Çerçeve numarası ofsetle birleşir: fiziksel adres = çerçeve × 4096 + ofset.
Her erişimde 4 ek okuma pahalıdır: TLB çeviriyi önbelleğe alır. 5 düzeyli sayfalama (LA57, Linux 4.14+) araya P4D ekleyerek 57 bit adres sağlar. Linux’un genel modeli 5 düzeydir; donanım daha azını kullanıyorsa düzeyler katlanır.
Bellek Yönetimi
Fiziksel Belleğin Gösterimi
Linux fiziksel belleği düğüm → bölge → sayfa hiyerarşisiyle tanımlar. Çok soketli makinede her CPU kendi düğümünden ayırmayı tercih eder (NUMA, Bkz. Bölüm 12). 64 bit sistemlerde ZONE_HIGHMEM yoktur; yalnızca 32 bit çekirdeklerde kalmıştır.
Bellek Yönetimi · Adım adım
Buddy (Eş) Ayırıcı
Buddy ayırıcı belleği 2’nin kuvveti büyüklüğünde bloklarla yönetir: order 0 = 1 sayfa … order 10 = 1024 sayfa (4 MiB). Her order için ayrı bir boş liste vardır.
A 8 sayfa (order 3) ister. 64’lük blok ikiye bölünür: 32+32 → 16+16 → 8+8. İlk 8 A’ya verilir; artanlar listelere eklenir.
B 8 sayfa ister: order-3 listesinde hazır blok (8–15) var, bölme gerekmez.
C 4 sayfa ister: order 2 boş; 16–31 bloğu 8+8’e, 16–23 de 4+4’e bölünür; C 16–19’u alır.
B serbest kalır. Kardeşi (buddy) A — adresi XOR ile bulunur: 8 ⊕ 8 = 0 — dolu olduğu için birleşme yok.
A serbest kalır: kardeşi 8–15 boş → birleşip 0–15 (order 4). Onun kardeşi 16–31 kısmen dolu (C) → dur.
C serbest kalır: 16–19 + 20–23 → 16–23, + 24–31 → 16–31, + 0–15 → 0–31, + 32–63 → 0–63. Tüm bellek yeniden tek blok: dış parçalanma azalır.
Kardeş blok bir ağaçta aranmaz: blok başlangıcının order bitini çevirmek (s ⊕ 2^order) yeterlidir. Bedeli iç parçalanmadır: 65 sayfa isteyen 128 sayfa alır.
Bellek Yönetimi
SLUB Nesne Ayırıcısı
Çekirdek çok sayıda küçük, aynı boyda nesne (task_struct, dentry, inode) ayırır; her biri için sayfa ayırmak israftır. SLUB nesne önbellekleri tutar; kmalloc 8 B–8 KiB boy sınıflarını kullanır. SLOB (6.4) ve SLAB (6.8) kaldırıldı: tek ayırıcı SLUB. Büyük, fiziksel bitişik olması gerekmeyen alan için vmalloc.
Bellek Yönetimi · Adım adım
Sayfa Önbelleği, Geri Kazanım ve OOM
Boş bellek israftır: Linux kullanılmayan RAM’i sayfa önbelleği olarak dosya verisi için kullanır. free -h’deki “available” bu önbelleğin geri alınabilir kısmını da sayar.
Uygulama büyüdü, boş bellek düşük su seviyesinin altına indi: kswapd uyanır ve arka planda temiz önbellek sayfalarını bırakır (kirli olanlar önce diske yazılır).
MGLRU (6.1) sayfaları erişim nesillerine ayırır; en soğuk nesil önce gider. Soğuk anonim sayfaların dosyası yoktur: zswap ile RAM’de sıkıştırılır, gerekirse takas alanına yazılır.
Ayırma hızı geri kazanımı geçti, boş bellek min seviyesinin altında: ayıran süreç kendisi doğrudan geri kazanım yapar ve takılır (gecikme artar).
Hiçbir şey yetmezse OOM killer en yüksek oom_score’lu (çoğunlukla en büyük) süreci SIGKILL ile öldürür; oom_score_adj ile etkilenebilir. cgroup sınırı aşılırsa yalnız o gruptan seçilir.
Giriş/Çıkış
Aygıtlar Dosyadır: Major ve Minor Numaralar
$ ls -l /dev # seçilmiş satırlar
brw-rw---- 1 root disk 8, 0 sda
brw-rw---- 1 root disk 8, 1 sda1
brw-rw---- 1 root disk 259, 0 nvme0n1
crw-rw-rw- 1 root root 1, 3 null
crw-rw-rw- 1 root tty 5, 0 tty- c karakter aygıtı: bayt akışı, genelde önbelleksiz (terminal,
/dev/null, fare) - b blok aygıtı: sabit boy bloklar, rastgele erişim; sayfa önbelleği ve G/Ç zamanlayıcı kullanır
- Ağ arayüzleri (
eth0)/dev’de yoktur; soketlerle kullanılır - Düğümleri devtmpfs + udev aygıt görününce dinamik oluşturur
Boyut sütunu yerine “major, minor” görünür. Major sürücüyü, minor o sürücünün yönettiği birimi seçer. Uygulama diske de terminale de aynı open/read/write ile erişir (Bkz. Bölüm 11).
Giriş/Çıkış
Linux G/Ç Sisteminin Yapısı
Üç aygıt türü üç ayrı yoldan iner. Klasik BSD’de de benzer ayrım vardı; System V’in STREAMS modül yığını ise Linux ana çekirdeğine hiç girmedi. Ağ yolunda veri sk_buff yapılarında taşınır; XDP/eBPF paketleri sürücü düzeyinde işleyebilir.
Giriş/Çıkış
Sürücü Arayüzü: file_operations ve Terminal
static const struct file_operations my_fops = { .owner = THIS_MODULE, .open = my_open, .release = my_release, /* close */ .read = my_read, .write = my_write, .unlocked_ioctl = my_ioctl, .poll = my_poll, .mmap = my_mmap, }; cdev_init(&cdev, &my_fops); /* major:minor → fops */
| Eski UNIX girişleri | Linux karşılığı |
|---|---|
k_open, tty_open, pr_open, ip_open | .open |
k_close, tty_close, pr_close… | .release |
mem_read, k_read, tty_read | .read |
mem_write, tty_write, pr_write… | .write |
k_ioctl, tty_ioctl, pr_ioctl… | .unlocked_ioctl |
tcgetattr/tcsetattr terminal parametrelerini okur/yazar: kanonik kip (satır düzenleme, ICANON) ya da ham kip, ECHO. cfset/cfget[io]speed giriş/çıkış baud hızı; bugün çoğunlukla seri portlar için, terminaller ise sözde terminaldir (pty).Klasik ders kitabı tablosu her sürücü için ayrı giriş noktaları (k_*, tty_*, pr_*, ip_*) listeler; Linux bunları tek bir işlev işaretçisi yapısında toplar. VFS her read()’i ilgili sürücünün .read’ine yönlendirir.
Giriş/Çıkış
Blok Katmanı: blk-mq ve G/Ç Zamanlayıcılar
Tek kuyruklu eski blok katmanı (CFQ, deadline, noop) 5.0’da kaldırıldı. Milyonlarca IOPS yapan NVMe’de kilit çekişmesini önlemek için her CPU kendi kuyruğuna yazar. Varsayılanlar: NVMe none, SATA mq-deadline, etkileşimli masaüstü/HDD bfq. cat /sys/block/nvme0n1/queue/scheduler → [none] mq-deadline kyber bfq. io_uring (5.1) paylaşımlı halkalarla sistem çağrısı yükünü azaltan asenkron G/Ç arayüzüdür (Bkz. Bölüm 11).
Giriş/Çıkış · Adım adım
Ağ: Soketler ve Bağlantı Kurulumu
Sunucu bir soket oluşturur (fd 3), 80. porta bağlar (bind) ve listen ile dinleme kuyruğu açar. Soket de bir dosya tanımlayıcısıdır.
Sunucu accept’te bloke olur. İstemci kendi soketini oluşturur.
connect çağrısıyla istemci çekirdeği üçlü el sıkışmayı (SYN, SYN-ACK, ACK) yapar; uygulama bu paketleri hiç görmez.
Bağlantı kurulunca accept yeni bir soket (fd 4) döndürür; fd 3 dinlemeye devam eder. Çok istemcili sunucular epoll veya iş parçacıklarıyla binlerce soketi yönetir.
Veri write/read ile akar: çekirdek sk_buff’lara koyar, TCP/IP başlıklarını ekler, sürücü kartın halkasına yazar. close FIN gönderir.
Dosya Sistemleri
Dizin Ağacı ve Bağlama
/usr/bin: komutlar (/binartık ona bağlantı)/etc: metin yapılandırma dosyaları/dev: aygıt dosyaları/usr/lib: paylaşılan kütüphaneler,/usr/lib/modules: çekirdek modülleri/usr: salt okunur, paylaşılabilir yazılım (bin, lib, share)/home: kullanıcı dizinleri;/root: yöneticinin/var: değişken veri (günlükler, kuyruklar, önbellek)/proc,/sys: çekirdeğin sözde dosya sistemleri/boot: çekirdek ve initramfs/tmp: geçici (çoğu dağıtımda tmpfs)
Windows’taki sürücü harfleri yerine tek bir ağaç vardır: başka bir dosya sistemi herhangi bir dizine bağlanır (mount). Düzen FHS standardına uyar; çoğu dağıtım “usrmerge” ile /bin, /lib’i /usr altına taşıdı.
Dosya Sistemleri · Adım adım
VFS: Tanımlayıcılar, Açık Dosyalar ve i-node’lar
fd = open("x.txt", O_RDONLY): VFS yolu çözer, bir struct file (açık dosya: konum, kip) oluşturur ve sürecin tablosundaki ilk boş numarayı (3) ona bağlar.
read(3, buf, 100) konumu 100’e ilerletir. Konum (f_pos) fd tablosunda değil, açık dosya nesnesinde tutulur.
fork() sonrası çocuk aynı struct file’ı paylaşır (referans 2). Çocuk 50 bayt okuyunca ebeveynin konumu da 150 olur: kabukta (cmd1; cmd2) > log bu yüzden çalışır.
İlgisiz bir süreç aynı dosyayı açarsa yeni bir struct file alır: konumlar bağımsızdır, ama dentry ve inode ortaktır.
VFS, ext4, XFS, Btrfs, NFS, procfs… hepsine aynı nesnelerle bakar; her nesnenin işlem tablosu (inode_operations, file_operations) çağrıyı gerçek dosya sistemine yönlendirir.
Dosya Sistemleri
Dosya ve Dizin Sistem Çağrıları, stat
| Dosya çağrısı | İşlev |
|---|---|
open, creat | Aç / oluştur, fd döndür |
close | Tanımlayıcıyı kapat |
read, write | Konumdan oku / yaz |
lseek | Konumu taşı |
stat, fstat, lstat | Bilgi al (lstat: bağın kendisi) |
pipe, dup2 | Boru oluştur / fd kopyala |
fcntl | Kilit, bayraklar vb. |
mkdir, rmdir | Dizin oluştur / sil (boşsa) |
link, unlink | Ad ekle / sil |
rename, chdir | Taşı (atomik) / cwd değiştir |
getdents64 | Dizin girdilerini oku |
| stat alanı | Anlamı |
|---|---|
st_dev | Dosyanın bulunduğu aygıt |
st_ino | i-node numarası |
st_mode | Tür + izin bitleri |
st_nlink | Sert bağ sayısı |
st_uid, st_gid | Sahip ve grup |
st_size | Bayt cinsinden boyut |
st_atime | Son erişim zamanı |
st_mtime | Son içerik değişikliği |
st_ctime | Son i-node değişikliği (oluşturma değil!) |
st_blocks | Ayrılan 512 B blok sayısı |
opendir/readdir/closedir sistem çağrısı değil, getdents64 üzerine kurulu kütüphane fonksiyonlarıdır. Oluşturma zamanı yalnız yeni statx() ile (stx_btime) alınabilir.
Dosya Sistemleri · Adım adım
Dosya Kilitleme
A süreci 4–7. baytlara paylaşımlı (okuma) kilit koyar: fcntl(fd, F_SETLK, &fl) ile l_type = F_RDLCK, l_start = 4, l_len = 4.
B 6–9’a paylaşımlı kilit ister: çakışma paylaşımlı kilitler arasında serbesttir, istek kabul edilir.
C 2–11’e paylaşımlı kilit koyar; 6–7 baytları artık üç kilitle kaplı.
D bayt 9’a özel (F_WRLCK) kilit ister: B ve C’nin kilitleriyle çakışır. F_SETLK hemen hata döndürür, F_SETLKW ise kilitler kalkana dek bekler. Çekirdek bekleme döngüsünü fark ederse EDEADLK döndürür (Bkz. Bölüm 7).
Bu kilitler tavsiye niteliğindedir (advisory): kilidi sormayan bir süreç yine yazabilir. Bütün dosya için flock(); iş parçacıklarıyla güvenli kullanım için açık dosyaya bağlı OFD kilitleri vardır.
Dosya Sistemleri
ext2/ext4 Disk Düzeni ve i-node
Klasik UNIX düzeni (önyükleme bloğu, süper blok, i-node’lar, veri) ext2’de blok gruplarına bölündü: i-node ile verisi aynı gruba yakın konur, süper blok yedeklenir. Dolaylı bloklarla teorik sınır ~4 TiB’tır (ext2’de i_blocks alanı nedeniyle 2 TiB). Kök dizinin i-node numarası her zaman 2’dir.
Dosya Sistemleri · Adım adım
ext4 Günlükleme (Journaling)
Bir write() + fsync() birkaç bloğu değiştirir: veri (D) ile birlikte i-node, blok bit eşlemi ve extent gibi üst veriler. Bunlardan biri yazılıp diğeri yazılmadan çökme olursa dosya sistemi tutarsız kalır (Bkz. Bölüm 10).
Varsayılan data=ordered kipinde önce veri blokları asıl yerine yazılır; böylece üst veri hiçbir zaman çöp içeren bloğu göstermez.
Üst veri blokları günlüğe bir işlem (T42) olarak yazılır; en sonda commit kaydı. Commit diske ulaştığı an işlem kalıcıdır.
Checkpoint: üst veri asıl yerlerine yazılır, günlükteki alan yeniden kullanılır (dairesel tampon).
Çökmeden sonra tam fsck taraması yerine günlük okunur: commit’li işlemler yeniden oynatılır, yarım kalanlar atılır. Saniyeler içinde tutarlılık.
Diğer kipler: data=journal (veri de günlüğe; en güvenli, en yavaş) ve data=writeback (sıra garantisi yok). XFS de üst veri günlüğü kullanır; Btrfs ise günlük yerine yazınca kopyala ile tutarlıdır.
Dosya Sistemleri · Adım adım
Dizinler ve Yol Çözümleme
Mutlak yol kökten başlar: kök dizinin i-node’u (2) okunur, veri bloklarındaki girdilerde “home” aranır → 131073. Her dizin için x (arama) izni denetlenir.
“home” dizininin verisinde “ali” aranır → 131074.
“ali” dizininde “notlar.txt” → 132010. (Göreli yol olsaydı süreç cwd’sinden başlanırdı.)
Son bileşenin i-node’u okunur, izinler denetlenir, struct file oluşturulup fd döndürülür.
Her adım disk okuması gerektirseydi yavaş olurdu: dentry önbelleği (dcache) ad → i-node eşlemelerini bellekte tutar; sıcak yollar diske hiç gitmeden çözülür.
Dosya Sistemleri
Sert ve Sembolik Bağlar
$ ln a.txt yedek/b.txt # sert $ ln -s a.txt k # sembolik $ ls -li 500 -rw-r--r-- 2 ali ali 9 a.txt 777 lrwxrwxrwx 1 ali ali 5 k -> a.txt
- Sert bağ: aynı i-node, bağ sayısı artar.
rm a.txtyalnız adı siler; veri sayaç 0 olunca (ve açık fd kalmayınca) serbest kalır. - Sert bağ dosya sistemi sınırını aşamaz, dizinlere konamaz.
- Sembolik bağ: hedefin yolunu tutar; başka dosya sistemine, dizine işaret edebilir. Hedef silinirse bağ sarkar.
Dosya Sistemleri
ext4’ün Ötesi: XFS, Btrfs ve Diğerleri
| Dosya sistemi | Öne çıkan özellik | Tipik kullanım |
|---|---|---|
| ext4 | Günlükleme, extent, olgun ve hızlı fsck | Çoğu dağıtımda varsayılan |
| XFS | Paralel G/Ç, büyük dosyalar, gecikmeli ayırma | RHEL varsayılanı, büyük sunucular |
| Btrfs | Yazınca kopyala, anlık görüntü, sağlama toplamı, sıkıştırma, alt birimler | Fedora, openSUSE varsayılanı |
| F2FS | Flash’a uygun günlük yapılı (log-structured) | Android telefonlar |
| tmpfs | Tamamen RAM’de (sayfa önbelleği + takas) | /tmp, /run, /dev/shm |
| NFS, SMB | Ağ dosya sistemleri | Paylaşılan ev dizinleri |
VFS sayesinde hepsi aynı sistem çağrılarıyla kullanılır; findmnt veya df -T hangi dizinde hangi dosya sisteminin bağlı olduğunu gösterir.
Dosya Sistemleri
/proc ve /sys: Çekirdeğe Açılan Pencereler
$ cat /proc/loadavg
0.42 0.37 0.30 2/1234 98765
$ grep -E "MemTotal|MemAvailable" /proc/meminfo
MemTotal: 16303452 kB
MemAvailable: 9876544 kB
$ ls /proc/self
cmdline cwd environ exe fd maps status …
$ cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq
$ sysctl vm.swappiness # = /proc/sys/vm/swappiness
vm.swappiness = 60Süreç başına dizinler (/proc/PID/status, maps, fd/) ve çekirdek istatistikleri. ps, top, free verisini buradan okur. /proc/sys: ayarlanabilir çekirdek parametreleri.
Aygıt modeli: her aygıt, sürücü ve veri yolu bir dizin; dosya başına tek değer. /sys/fs/cgroup cgroup v2 arayüzüdür.
ps, top, /proc, PID/PPID ilişkileri, süreç ağacı, dosya tanımlayıcıları ve bellek haritalarını keşfedebilirsiniz.Dosya Sistemleri
Ağ Dosya Sistemi (NFS)
İstemci uzak dizini kendi ağacına bağlar; uygulama farkı görmez, çünkü NFS de VFS altında bir dosya sistemidir. NFSv3 durumsuzdu (her istek kendi başına anlamlı); NFSv4 durumlu, tek port (2049), TCP, ACL, Kerberos ve istemciye yetki devri (delegation) sunar.
Güvenlik
Dosya Koruma: İzin Bitleri
| İkili | Sekizli | Sembolik | İzin verilen erişim |
|---|---|---|---|
111000000 | 0700 | rwx------ | Sahip okur, yazar, çalıştırır |
111111000 | 0770 | rwxrwx--- | Sahip ve grup okur, yazar, çalıştırır |
110100000 | 0640 | rw-r----- | Sahip okur ve yazar; grup okur |
110100100 | 0644 | rw-r--r-- | Sahip okur ve yazar; herkes okur |
111101101 | 0755 | rwxr-xr-x | Sahip her şeyi yapar; diğerleri okur ve çalıştırır |
000000000 | 0000 | --------- | Kimsenin erişimi yok |
000000111 | 0007 | ------rwx | Yalnız “diğerleri” erişir (garip ama geçerli) |
Dizinlerde r = girdileri listele, w = girdi ekle/sil, x = içine gir / ad ara. Bir dosyayı silmek için dosyaya değil dizine yazma izni gerekir.
Güvenlik · Adım adım
chmod ve umask Hesapları
Her sekizli rakam üç bittir: 7 = 111 = rwx, 5 = 101 = r-x, 4 = 100 = r--.
Sembolik kip mevcut bitleri değiştirir: grup yazma biti eklenir, diğerlerinin okuma biti silinir → 0770.
Yeni dosya 666, dizin 777 ister; umask’taki bitler silinir: 666 & ~022 = 0644, dizin 777 & ~022 = 0755. (Çıkarma değil, bit temizleme!)
Daha sıkı umask: dosya 666 & ~027 = 0640 (rw-r-----), dizin 777 & ~027 = 0750 (rwxr-x---). Diğerleri hiçbir şey göremez.
4xxx setuid: çalışan süreç dosya sahibinin kimliğini alır → rwsr-xr-x. 2xxx setgid (dizinde: yeni dosyalar grubu miras alır). 1xxx yapışkan bit: /tmp’de herkes yazar ama yalnız kendi dosyasını silebilir → rwxrwxrwt.
Kontrol: stat -c "%a %A" dosya sekizli ve sembolik izni birlikte gösterir; umask -S maskeyi sembolik yazar.
Güvenlik
Erişim Denetimi, root ve Yetenekler
- euid = 0 (root) → izin ver (çalıştırma için en az bir x biti gerekir)
- euid = dosya sahibi → yalnız sahip bitlerine bak
- egid ya da ek gruplardan biri = dosya grubu → grup bitleri
- aksi halde diğer bitleri
İlk eşleşen sınıf kazanır: ----rw-rw- dosyasını sahibi ali okuyamaz, başkaları okur.
CAP_NET_BIND_SERVICE (1024 altı port), CAP_NET_RAW, CAP_KILL, CAP_DAC_OVERRIDE, CAP_SYS_ADMIN… setcap cap_net_raw+ep /usr/bin/ping ile setuid root gerekmez (Bkz. Bölüm 13).| Çağrı | İşlev |
|---|---|
chmod | Dosyanın izin kipini değiştirir |
chown | Sahip ve grubu değiştirir |
access | Erişimi gerçek uid/gid ile sınar |
getuid, geteuid | Gerçek / etkin uid |
getgid, getegid | Gerçek / etkin gid |
setuid, setresuid | uid değiştirir (ayrıcalık bırakma) |
setgid | gid değiştirir |
umask | Yeni dosyalar için izin maskesi |
Kullanıcı ve grup kimlikleri /etc/passwd ve /etc/group’ta; parola özetleri yalnız root’un okuyabildiği /etc/shadow’da tutulur. sudo yetkilendirilmiş kullanıcıya komut başına root yetkisi verir.
Güvenlik · Adım adım
Konteynerler: Ad Alanları + cgroups
Çalışma zamanı (runc, crun) clone()/unshare() ile yeni ad alanları oluşturur: kendi PID numaraları, bağlama ağacı, ağ yığını, ana makine adı, IPC, kullanıcı eşlemesi (içerideki root dışarıda sıradan uid), cgroup ve saat görünümü.
Bağlama ad alanında pivot_root ile imajın kök dosya sistemine geçilir. Kök genelde overlayfs’tir: salt okunur imaj katmanları + konteynere özel yazılabilir üst katman.
cgroup v2 kaynakları sınırlar: cpu.max = "50000 100000" (yarım CPU), memory.max = 512M, pids.max = 100. Ad alanı ne görüleceğini, cgroup ne kadar kullanılacağını belirler.
Saldırı yüzeyi daraltılır: gereksiz yetenekler düşürülür, no_new_privs, seccomp filtresi (onlarca riskli sistem çağrısı engellenir) ve AppArmor/SELinux profili uygulanır.
Son olarak execve: uygulama içeride PID 1, konakta PID 48213. Hepsi aynı çekirdeği paylaşır: VM’den çok hafiftir ama çekirdek açığı tüm konteynerleri etkiler (Bkz. Bölüm 13).
Güvenlik
seccomp ve Linux Güvenlik Modülleri (LSM)
Etiket tabanlı zorunlu erişim denetimi (MAC): her süreç ve dosyanın bir tipi var; politika hangi tipin hangisine ne yapabileceğini söyler. RHEL, Fedora, Android.
Yol tabanlı profiller: “bu program yalnız şu dosyalara erişebilir”. Yazması kolay. Ubuntu, SUSE, Debian.
Ayrıcalıksız süreç kendini kum havuzuna alır: “bundan sonra yalnız ./veri altına yazabilirim”.
Sürecin yapabileceği sistem çağrılarını filtreler; Chrome, systemd servisleri, Docker, Android uygulamaları kullanır.
Android ve Özet · Adım adım
Android: Binder, Zygote ve Kum Havuzu
Android Linux çekirdeği üzerinde çalışır ama GNU kullanıcı alanı yoktur: C kütüphanesi Bionic, uygulamalar ART’ta çalışır. Çekirdeğe eklenenler: Binder IPC, enerji yönetimi (wakelock), zorunlu SELinux.
Önyüklemede init zygote sürecini başlatır; zygote çatı sınıflarını ve kaynakları bir kez yükleyip bekler.
Uygulama açılırken zygote fork() eder: yüklü sayfalar COW ile paylaşılır, başlatma hızlanır. Çocuk uygulamanın benzersiz uid’ine, SELinux bağlamına ve seccomp filtresine geçer.
Uygulama bir servisi (konum, kamera) Binder ile çağırır: çekirdek veriyi alıcının eşlenmiş tamponuna tek kopyayla taşır ve arayanın uid’ini iletir; servis izni buna göre denetler.
Kum havuzu: her uygulama ayrı bir Linux kullanıcısıdır, verisi yalnız kendisine açıktır. Bellek azalınca kullanıcı alanındaki lmkd, PSI sinyalleriyle önemsiz arka plan uygulamalarını öldürür.
Özet
Özet: Dersin Kavramları Linux’ta
| Bölüm | Kavram | Linux’taki karşılığı |
|---|---|---|
| 2 · Yapılar | Sistem çağrısı, monolitik çekirdek | syscall, LKM, eBPF |
| 3 · Süreçler | PCB, oluşturma, IPC | task_struct, fork/exec/wait, sinyal, boru |
| 4 · İş parçacıkları | Çekirdek iş parçacığı modeli | clone(), NPTL 1:1 |
| 5 · Çizelgeleme | Adil paylaşım, gerçek zaman | EEVDF, FIFO/RR/DEADLINE, sched_ext |
| 6 · Senkronizasyon | Kilit, koşul değişkeni | futex, spinlock, RCU |
| 7 · Kilitlenme | Tespit ve önleme | lockdep, fcntl EDEADLK |
| 8 · Bellek | Ayırma, parçalanma | buddy, SLUB, NUMA bölgeleri |
| 9 · Sanal bellek | Sayfalama, değiştirme | 4/5 düzey tablo, COW, MGLRU, zswap, OOM |
| 10 · Dosya sistemleri | i-node, dizin, günlük | VFS, ext4, XFS, Btrfs |
| 11 · G/Ç | Sürücü, zamanlama | aygıt dosyaları, blk-mq, io_uring |
| 12 · Çok işlemcili | Yük dengeleme, NUMA | CPU başına kuyruklar, zamanlama alanları |
| 13 · Güvenlik | Erişim denetimi, yalıtım | rwx, yetenekler, namespaces, SELinux, seccomp |
| 14 · Çoklu ortam | Zamanında işleme | PipeWire, ALSA, V4L2 |
| 15 · Tasarım | Mekanizma ve politika ayrımı | cgroup, sched_ext: politika kullanıcıda |
Özet
Kontrol Soruları
fork()neden “bir kez çağrılıp iki kez döner”? Dönüş değerleri nedir, COW neyi kazandırır?- Kabuk
ls | wc -l’yi nasıl kurar? Ebeveyn boru uçlarını kapatmazsa ne olur? - Linux’ta süreç ile iş parçacığı arasındaki fark nedir? clone bayraklarıyla açıklayın.
- EEVDF’de bir görev hangi koşulda seçilebilir? nice 5 görevin vruntime’ı neden daha hızlı ilerler?
0x00007f3a1c2b5e88adresinin 4 düzeyli tablodaki dört dizini ve ofseti nedir?- Buddy ayırıcıdan 5 sayfa istenirse hangi order verilir, iç parçalanma kaç sayfadır?
umask 027iken oluşturulan dosya ve dizinin izinleri nedir?- Sert bağ ile sembolik bağ arasındaki fark nedir? Hedef silinince ne olur?
- ext4 sıralı günlüklemede çökmeden sonra tutarlılık nasıl sağlanır?
- Konteyner = ad alanları + cgroups: her biri neyi sağlar? VM’den farkı nedir?
Cevaplar
- fork süreci kopyalar; ikisi de çağrının içinden döner. Ebeveyne çocuğun PID’i, çocuğa 0 döner. COW ile sayfalar ancak yazılınca kopyalanır; fork’u exec izlediğinde neredeyse hiç kopya yapılmaz.
- pipe() → fork; çocuk 1 dup2(yazma, 1) + exec ls; çocuk 2 dup2(okuma, 0) + exec wc. Ebeveyn yazma ucunu açık tutarsa wc EOF görmez, sonsuza dek bekler.
- Yapısal fark yok: ikisi de task_struct. İş parçacığı CLONE_VM, CLONE_FILES, CLONE_SIGHAND, CLONE_THREAD ile adres alanını, dosyaları ve işleyicileri paylaşır, aynı TGID’dedir.
- Uygun olmalı (v ≤ V, gecikme ≥ 0); uygunlar arasında en erken sanal son tarihli seçilir. vruntime artışı gerçek süre × 1024 / ağırlık; nice 5’in ağırlığı 335 olduğu için ~3 kat hızlı ilerler.
- PGD = 254, PUD = 232, PMD = 225, PTE = 181, ofset = 0xe88.
- 5 sayfa 2’nin kuvvetine yuvarlanır: order 3 = 8 sayfa; 3 sayfa iç parçalanma.
- Dosya 666 & ~027 = 0640 (rw-r-----), dizin 777 & ~027 = 0750 (rwxr-x---).
- Sert bağ aynı i-node’a ikinci addır; hedef adı silinse de veri kalır. Sembolik bağ yolu tutan ayrı dosyadır; hedef silinince sarkar. Sert bağ dosya sistemi sınırını aşamaz.
- Veri önce asıl yerine, üst veri günlüğe yazılır, sonra commit. Bağlarken commit’li işlemler yeniden oynatılır, commit’siz olanlar atılır; tam fsck gerekmez.
- Ad alanları görünümü yalıtır (PID, ağ, bağlama…), cgroups kaynak kullanımını sınırlar. Konteynerler konak çekirdeğini paylaşır: hafif ama yalıtım VM’den zayıftır.
Özet
Kaynaklar
- A. S. Tanenbaum, H. Bos, Modern Operating Systems, 4. baskı (2014) / 5. baskı (2022), Pearson — Bölüm 10: Case Study 1: Unix, Linux and Android.
- A. Silberschatz, P. B. Galvin, G. Gagne, Operating System Concepts, 10. baskı, Wiley, 2018 — Bölüm 20: The Linux System.
- R. Love, Linux Kernel Development, 3. baskı, Addison-Wesley, 2010.
- M. Kerrisk, The Linux Programming Interface, No Starch Press, 2010; man7.org kılavuz sayfaları.
- The Linux Kernel Documentation, docs.kernel.org — scheduler (EEVDF, sched-ext), admin-guide/mm (MGLRU, zswap), block (blk-mq), filesystems (vfs, ext4), userspace-api (seccomp, landlock).
- Android Open Source Project, source.android.com — mimari, Binder, uygulama kum havuzu.
- D. M. Ritchie, K. Thompson, “The UNIX Time-Sharing System”, Communications of the ACM, 1974.