Yürütme sırasında ikili dosyayı değiştirme


10

Bir ikili dosya çalıştırdığım yerde, genellikle a.outuzun bir iş yaptığı gibi arka planda söylüyorum , genellikle durumla karşılaşıyorum . Bunu yaparken, yine üretilen a.outve derleyen C kodunda değişiklikler yapmak a.out. Şimdiye kadar, bununla ilgili herhangi bir sorun yaşamadım. Çalışan işlem a.outnormal olarak devam eder, asla kilitlenmez ve her zaman başlangıçta başlatıldığı eski kodu çalıştırır.

Bununla birlikte, diyelim ki a.outRAM'in boyutuyla karşılaştırılabilir büyük bir dosyaydı. Bu durumda ne olur? Ve paylaşılan bir nesne dosyasına bağlı olduğunu varsayalım libblas.so, ya libblas.soçalışma sırasında değiştirirsem ? Ne olurdu?

Benim asıl soru - ı çalıştırdığınızda bu işletim garantisi yok a.outgereğince, ardından orijinal kod her zaman normal olarak çalışacak orijinal ikili veya boyutuna bakılmaksızın, ikili .soolanlar bile, kendisine bağlanan dosyalar .ove .sodosya sırasında modfied edilir Çalışma süresi?

Benzer sorunları ele alan şu sorular olduğunu biliyorum: /programming/8506865/when-a-binary-file-runs-does-it-copy-its-entire-binary-data-into-memory -at-once Yürütme sırasında bir komut dosyasını düzenlerseniz ne olur? Bir program çalışırken canlı güncelleme nasıl yapılabilir?

Bu da bu konuda biraz daha anlamamı sağladı ama tam olarak ne istediğimi sorduklarını sanmıyorum, bu da yürütme sırasında bir ikili dosyayı değiştirmenin sonuçları için genel bir kural


Bana göre, bağladığınız sorular (özellikle Yığın Taşması) bu sonuçları (veya bunların yokluğunu) anlamak için zaten önemli bir yardım sağlıyor. Çekirdek, programınızı bellek metni bölgelerine / segmentlerine yüklediğinden, dosya alt sistemi üzerinden yapılan değişikliklerden etkilenmemelidir.
John WH Smith

@JohnWHSmith Stackoverflow, en iyi cevap diyor ki if they are read-only copies of something already on disc (like an executable, or a shared object file), they just get de-allocated and are reloaded from their source, bu yüzden ikili dosya çok büyükse, o zaman ikili parçanızın bir kısmı RAM'den çıkarsa, ancak yine de ihtiyaç duyulursa "kaynaktan yeniden yüklenir" izlenimi edindim - böylece herhangi bir değişiklik .(s)odosya yürütme sırasında yansıtılacaktır. Ama elbette yanlış anlamış olabilirim - bu yüzden bu daha spesifik soruyu soruyorum
texasflood

@JohnWHSmith Ayrıca ikinci cevap diyor ki No, it only loads the necessary pages into memory. This is demand paging., aslında istediğim şeyin garanti edilemeyeceği izlenimi altındaydım.
texasflood

Yanıtlar:


11

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ü mvoperasyon atomiktir. Bu muhtemelen renamesistem ç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 statve lstatsistem ç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.


Fantastik, ayrıntılı cevap. Bu benim karışıklığımı açıklıyor. Bu nedenle, inode hala kullanılabilir olduğundan, orijinal ikili dosyadaki verilerin hala diskte olduğunu ve bu nedenle diskteki dfboş baytların sayısını çözmek için kullanarak inode kullanmadıkları için yanlış olduğunu varsayarak doğru muyum? kaldırılan tüm dosya sistemi bağlantıları dikkate alındı ​​mı? Yani kullanmalıyım df -i? (Bu sadece teknik bir
meraktır

1
Sadece gelecekteki okuyucular için açıklığa kavuşturmak için - benim kafa karışıklığım yürütmeyi düşündüm, tüm ikili RAM'e yüklenecekti, bu yüzden RAM küçükse, ikili kısmın bir kısmı RAM'i terk edip diskten yeniden yüklenmesi gerekiyordu - ki bu dosyayı değiştirirseniz sorunlara yol açar. Ancak yanıt, siz rmveya mvorijinal dosyaya inode olarak tüm işlemler bu inode bağlantılarını kaldırıncaya kadar kaldırılmasa bile, ikili dosyanın hiçbir zaman diskten gerçekten kaldırılmadığını açıkça ortaya koymuştur.
texasflood

@texasflood Kesinlikle. Tüm yollar kaldırıldıktan sonra, hiçbir yeni işlem ( dfdahil) inode hakkında bilgi alamaz. Bulduğunuz yeni bilgiler yeni dosya ve yeni inode ile ilgilidir. Buradaki ana nokta, işlem alt sisteminin bu soruna hiçbir ilgisi olmadığıdır, bu nedenle bellek yönetimi (talep sayfalaması, süreç değişimi, sayfa hataları, ...) kavramları tamamen önemsizdir. Bu bir dosya alt sistemi sorunudur ve dosya alt sistemi tarafından halledilir. Süreç alt sistemi bununla uğraşmaz, bunun için burada değildir.
John WH Smith

@texasflood Aşağıdakiler hakkında bir not df -i: bu araç muhtemelen fs'nin süper bloğundan veya önbelleğinden bilgi alır, yani eski ikili dosyanın inode'unu (tüm bağlantıların silindiği) içerebileceği anlamına gelir. Bu, yeni işlemlerin eski verileri kullanmakta özgür olduğu anlamına gelmez.
John WH Smith

2

Anladığım kadarıyla, çalışan bir işlemin bellek eşlemesi nedeniyle, çekirdek, eşlenen dosyanın ayrılmış bir bölümünün güncellenmesine izin vermeyecektir. Eğer bir işlem çalışıyorsa o zaman tüm dosya ayrılmıştır sanırım böylece kaynak yeni bir sürümü derlenmiş aslında yeni bir inodes kümesi sonuçlanır çünkü bu güncelleme. Kısacası, yürütülebilir dosyalarınızın eski sürümlerine sayfa hatası olayları aracılığıyla diskte erişilebilir kalır. Eğer çok büyük bir dosyayı güncelleştirmek Yani bile, gerektiği erişilebilir kalır ve çekirdek gerektiğini hala uzun bir süreç çalıştığı olduğunca el değmemiş halini görmek. Orijinal dosya düğüm olmamalı sürece işlemin çalıştığı olduğunca tekrar kullanılabilir.

Bu elbette doğrulanmalıdır.


2

Bir .jar dosyasını değiştirirken bu her zaman geçerli değildir. Jar kaynakları ve bazı çalışma zamanı yansıma sınıfı yükleyicileri, program açıkça bilgi talep edene kadar diskten okunmaz.

Bu sadece bir sorundur, çünkü bir kavanoz belleğe eşlenen tek bir yürütülebilir dosya yerine sadece bir arşivdir. Bu biraz durmak bilmiyor ama hala sorunuzun bir dalı ve kendimi ayağından vurduğum bir şey.

Yürütülebilir dosyalar için: evet. Kavanoz dosyaları için: belki (uygulamaya bağlı olarak).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.