Yığın Taşması sorusu ilk başta yeterli gibi görünse de, yorumlarınızdan neden bu konuda hala şüpheniz olabileceğini anlıyorum. Bana göre, bu iki UNIX alt sisteminin (süreçler ve dosyalar) iletişim kurmasıyla ilgili kritik bir durum .
Bildiğiniz gibi, UNIX sistemleri genellikle iki alt sisteme ayrılır: dosya alt sistemi ve işlem alt sistemi. Şimdi, bir sistem çağrısı yoluyla aksi belirtilmedikçe, çekirdeğin bu iki alt sistemin birbiriyle etkileşime girmemesi gerekir. Ancak bir istisna vardır: yürütülebilir bir dosyanın bir sürecin metin bölgelerine yüklenmesi . Tabii ki, bu işlemin bir sistem çağrısı ( execve
) tarafından da tetiklendiğini iddia edebilir , ancak bu genellikle işlem alt sisteminin dosya alt sistemine örtük bir istekte bulunduğu tek durum olarak bilinir .
İşlem alt sisteminin doğal olarak dosyaları işlemesi mümkün olmadığından (aksi takdirde her şeyi ikiye bölmenin bir anlamı olmayacaktır), dosya alt sisteminin dosyalara erişmek için sağladığı her şeyi kullanması gerekir. Bu aynı zamanda işlem alt sisteminin, dosya alt sisteminin dosya basımı / silinmesi ile ilgili olarak aldığı her ölçüye gönderildiği anlamına gelir. Bu noktada, ben okuma öneriyoruz Gilles' cevabını için bu U & L soruya . Cevabımın geri kalanı Gilles'in bu daha genel olanına dayanıyor.
Dikkat edilmesi gereken ilk şey, dahili olarak dosyalara yalnızca inode yoluyla erişilebilir olmasıdır . Çekirdeğe bir yol verilirse, ilk adımı diğer tüm işlemler için kullanılacak bir inode haline dönüştürmek olacaktır. Bir işlem yürütülebilir bir dosyayı belleğe yüklediğinde, bir yolun çevrilmesinden sonra dosya alt sistemi tarafından sağlanan inode üzerinden yapar. Düğümler çeşitli yollarla (bağlantılar) ilişkilendirilebilir ve programlar yalnızca bağlantıları silebilir. Bir dosyanın ve onun inode'unu silmek için, kullanıcı arazisinin bu inode'a olan tüm bağlantıları kaldırması ve tamamen kullanılmadığından emin olması gerekir. Bu koşullar karşılandığında, çekirdek dosyayı otomatik olarak diskten siler.
Gilles'in cevabının yerine getirilebilir çalıştırılabilir kısmına bir göz attıysanız, dosyayı nasıl düzenlediğiniz / sildiğinize bağlı olarak , çekirdeğin her zaman dosya alt sisteminde uygulanan bir mekanizma yoluyla farklı tepki vereceğini / uyarlanacağını göreceksiniz.
- Birinci stratejiyi denerseniz ( open / truncate to zero / write veya open / write / truncate in new ), çekirdeğin isteğinizi yerine getirmeye zahmet etmeyeceğini göreceksiniz. Hata 26 alırsınız: Metin dosyası meşgul (
ETXTBSY
). Hiçbir sonuç yok.
- İkinci stratejiyi denerseniz, ilk adım yürütülebilir dosyalarınızı silmektir. Ancak, bir işlem tarafından kullanıldığından, dosya alt sistemi devreye girer ve dosyanın (ve inode) diskten gerçekten silinmesini önler . Bu noktadan sonra, eski dosyanın içeriğine erişmenin tek yolu, inode yoluyla yapmaktır; bu, işlem alt sisteminin metin bölümlerine yeni veri yüklemesi gerektiğinde yaptığı şeydir (dahili olarak, yolları kullanmanın bir anlamı yoktur, ancak onları inotlara çevirirken). Bağlantıyı kaldırmış olmanıza rağmendosya (tüm yollarını kaldırdı), işlem yine de hiçbir şey yapmamış gibi kullanabilirsiniz. Eski yolla yeni bir dosya oluşturmak hiçbir şeyi değiştirmez: Yeni dosyaya, çalışan işlemin bilgisi olmayan tamamen yeni bir inode verilir.
2. ve 3. stratejiler yürütülebilir dosyalar için de güvenlidir: yürütülebilir dosyalar (ve dinamik olarak yüklenmiş kütüphaneler) bir dosya tanıtıcısı olması açısından açık dosyalar olmamasına rağmen çok benzer şekilde davranırlar. Bazı program kodu çalıştırdığı sürece, dosya bir dizin girişi olmasa bile diskte kalır.
- Üçüncü strateji oldukça benzerdir çünkü
mv
operasyon atomiktir. Bu muhtemelen rename
sistem çağrısının kullanılmasını gerektirecektir ve çekirdek modundayken süreçler kesilemediğinden, hiçbir şey tamamlanıncaya (başarılı olsun veya olmasın) bu işleme müdahale edemez. Yine, eski dosyanın inode'unda herhangi bir değişiklik yoktur: yeni bir tane oluşturulur ve zaten çalışan süreçler, eski inode'nun bağlantılarından biriyle ilişkilendirilmiş olsa bile, bu konuda hiçbir bilgiye sahip olmaz.
Strateji 3 ile, yeni dosyayı mevcut isme taşıma adımı eski içeriğe giden dizin girişini kaldırır ve yeni içeriğe giden bir dizin girişi oluşturur. Bu bir atomik işlemde yapılır, bu nedenle bu stratejinin büyük bir avantajı vardır: bir işlem dosyayı herhangi bir zamanda açarsa, eski içeriği veya yeni içeriği görür - karışık içerik alma riski yoktur veya dosya mevcut.
Bir dosyayı yeniden derleme : kullanırken gcc
(ve davranış muhtemelen diğer birçok derleyici için benzerdir), strateji 2'yi kullanırsınız strace
. Derleyicinizin işlemlerinden birini çalıştırarak bunu görebilirsiniz :
stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
- Derleyici, dosyanın
stat
ve lstat
sistem çağrıları aracılığıyla zaten var olduğunu algılar .
- Dosya bağlantısı kaldırıldı . Burada, adla artık erişilemez olsa da
a.out
, inode ve içeriği, zaten çalışan işlemler tarafından kullanıldığı sürece diskte kalır.
- Yeni bir dosya oluşturulur ve bu ad altında yürütülebilir hale getirilir
a.out
. Bu yepyeni bir inode ve zaten çalışan süreçlerin umurunda olmayan yepyeni içerikler.
Şimdi, paylaşılan kütüphaneler söz konusu olduğunda, aynı davranış uygulanacaktır. Kütüphane nesnesi bir işlem tarafından kullanıldığı sürece, bağlantılarını nasıl değiştirirseniz değiştirin diskten silinmez. Bir şeyin belleğe yüklenmesi gerektiğinde, çekirdek bunu dosyanın inode'undan yapar ve bu nedenle bağlantılarında yaptığınız değişiklikleri (yeni dosyalarla ilişkilendirme gibi) yoksayar.