Bir program çalışırken canlı güncelleme nasıl yapılabilir?


15

Thunderbird veya Firefox gibi katil uygulamaların hala çalışırken paket yöneticisi aracılığıyla nasıl güncellenebileceğini merak ediyorum. Güncellenirken eski kodla ne olur? Çalışırken kendini güncelleyen bir program a.out yazmak istediğimde ne yapmam gerekir?



@derobert Tam olarak değil: bu iş parçacığı yürütülebilir dosyaların özelliklerine girmiyor. Alakalı bir arka plan sağlar, ancak yinelenmez.
Gilles 'SO- kötü olmayı bırak'

@Gilles Peki, eğer diğer şeylerden ayrılırsanız çok geniş. Burada iki (en azından) soru var. Ve diğer soru oldukça yakın, güncellemek için dosyaları değiştirmenin neden Unix'te çalıştığını soruyor.
derobert

Bunu gerçekten kendi kodunuzla yapmak istiyorsanız, "sıcak" kod güncellemelerinin önemli bir özellik olduğu Erlang programlama diline bakmak isteyebilirsiniz. learnyousomeerlang.com/relups
mattdm

1
@Gilles BTW: Yanıt olarak eklediğiniz makaleyi gördükten sonra, yakın oyumu geri çektim. Bu soruyu, güncellemelerin nasıl çalıştığını bilmek isteyen herkesi göstermek için iyi bir yere çevirdiniz.
derobert

Yanıtlar:


21

Genel olarak dosyaların değiştirilmesi

İlk olarak, bir dosyayı değiştirmek için birkaç strateji vardır:

  1. Varolan dosyayı yazmak için açın , 0 uzunluğa kısaltın ve yeni içeriği yazın. (Daha az yaygın olan bir varyant, mevcut dosyayı açmak, eski içeriğin üzerine yeni içerik yazmak, daha kısaysa dosyayı yeni uzunluğa kısaltmaktır.) Kabuk terimlerinde:

    echo 'new content' >somefile
    
  2. Eski dosyayı kaldırın ve aynı adla yeni bir dosya oluşturun. Kabuk terimleriyle:

    rm somefile
    echo 'new content' >somefile
    
  3. Geçici bir adla yeni bir dosyaya yazın, ardından yeni dosyayı mevcut ada taşıyın . Taşıma eski dosyayı siler. Kabuk terimleriyle:

    echo 'new content' >somefile.new
    mv somefile.new somefile
    

Stratejiler arasındaki tüm farklılıkları listelemeyeceğim, burada önemli olanlardan bahsedeceğim. Stategy 1 ile, herhangi bir işlem şu anda dosyayı kullanıyorsa, işlem yeni içeriği güncellenirken görür. İşlem dosya içeriğinin aynı kalmasını beklerse, bu biraz karışıklığa neden olabilir. Bunun yalnızca dosyayı açık olan işlemlerde (içinde lsofveya içinde görüldüğü gibi ; bir belgenin açık olduğu etkileşimli uygulamaların (örneğin bir düzenleyicide bir dosyayı açma) genellikle dosyayı açık tutmadığını, dosya içeriğini “Belgeyi aç” işlemi yapılır ve “belgeyi kaydet” işlemi sırasında dosyayı (yukarıdaki stratejilerden birini kullanarak) değiştirirler./proc/PID/fd/

Strateji 2 ve 3'te, bazı işlemlerde dosya somefileaçıksa, içerik yükseltme sırasında eski dosya açık kalır. 2. strateji ile, dosyayı kaldırma adımı yalnızca dosyanın dizindeki girişini kaldırır. Dosyanın kendisi yalnızca kendisine yol açan dizin girişi olmadığında (tipik Unix dosya sistemlerinde, aynı dosya için birden fazla dizin girişi olabilir ) ve hiçbir işlemin açık olmadığı durumlarda kaldırılır . İşte bunu gözlemlemenin bir yolu - dosya yalnızca sleepişlem öldürüldüğünde kaldırılır ( rmyalnızca dizin girişini kaldırır).

echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .

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şlemle 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örecektir - karışık içerik alma riski yoktur veya dosya mevcut.

Yürütülebilir dosyaları değiştirme

Linux'ta çalışan bir yürütülebilir programla strateji 1'i denerseniz bir hata alırsınız.

cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy

“Metin dosyası” , belirsiz tarihsel nedenlerden dolayı yürütülebilir kod içeren bir dosya anlamına gelir . Linux, diğer birçok unix varyantı gibi, çalışan bir programın kodunun üzerine yazmayı reddediyor; birkaç unix varyantı buna izin verir ve yeni kod eski kodda çok iyi bir değişiklik olmadığı sürece çökmelere neden olur.

Linux'ta, dinamik olarak yüklenen bir kitaplığın kodunun üzerine yazabilirsiniz. Kullanan programın çökmesine neden olması muhtemeldir. (Bunu izleyemeyebilirsiniz, sleepçünkü başladığında ihtiyaç duyduğu tüm kütüphane kodunu yükler. Uyuduktan sonra faydalı bir şey yapan daha karmaşık bir program deneyin perl -e 'sleep 9; print lc $ARGV[0]'.)

Bir tercüman komut dosyası çalıştırıyorsa, komut dosyası dosya yorumlayıcısı tarafından normal bir şekilde açılır, bu nedenle komut dosyasının üzerine yazılmasına karşı koruma yoktur. Bazı tercümanlar ilk satırı yürütmeye başlamadan önce tüm komut dosyasını okur ve ayrıştırır, diğerleri ise komut dosyasını gerektiği gibi okur. Bkz. Bir komut dosyasını yürütme sırasında düzenlerseniz ne olur? ve Linux kabuk komut dosyalarıyla nasıl başa çıkıyor? daha fazla ayrıntı için.

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.

Bir uygulamayı yükseltme

Çoğu paket yöneticisi, yukarıda belirtilen büyük avantajdan dolayı dosyaları değiştirmek için strateji 3'ü kullanır - herhangi bir zamanda dosyayı açmak geçerli bir sürümüne yol açar.

Uygulama yükseltmelerinin kesilebileceği yerlerde, bir dosyayı yükseltmek atomikken, uygulamanın bir bütün olarak yükseltilmesi, uygulamanın birden fazla dosyadan (program, kütüphaneler, veriler, ...) oluşması değildir. Aşağıdaki olay sırasını göz önünde bulundurun:

  1. Uygulamanın bir örneği başlatılır.
  2. Uygulama yükseltildi.
  3. Çalışan örnek uygulaması veri dosyalarından birini açar.

3. adımda, uygulamanın eski sürümünün çalışan örneği, yeni sürümden bir veri dosyası açar. Bunun işe yarayıp yaramadığı uygulamaya, hangi dosyanın olduğuna ve dosyanın ne kadar değiştirildiğine bağlıdır.

Yükseltme işleminden sonra eski programın hala çalıştığını göreceksiniz. Yeni sürümü çalıştırmak istiyorsanız, eski programdan çıkmanız ve yeni sürümü çalıştırmanız gerekir. Paket yöneticileri genellikle yükseltme işleminde cinleri öldürür ve yeniden başlatır, ancak son kullanıcı uygulamalarını tek başına bırakır.

Birkaç artalan sürecinin, artalan sürecini öldürmek ve yeni örneğin yeniden başlatılmasını beklemek zorunda kalmadan yükseltmeleri işlemek için özel prosedürleri vardır (bu, hizmet kesintisine neden olur). Bu öldürülemeyen init durumunda gereklidir ; init sistemleri, çalışan örnek çağrısının execvekendisini yeni sürümle değiştirmesini istemek için bir yol sağlar .


"daha sonra yeni dosyayı mevcut ada taşıyın. Taşıma eski dosyayı siler." Daha unlinksonra ele alacağınız gibi, bu sadece biraz olduğu için biraz kafa karıştırıcı . Belki "mevcut ismin yerini alır", ama yine de biraz kafa karıştırıcı.
derobert

@derobert “Bağlantıyı kes” terminolojisine yoğunlaşmak istemedim. Ben daha sonra açıklanacak bir incelik, dizin girişine referans olarak "sil" kullanıyorum. Bu aşamada kafa karıştırıcı mı?
Gilles 'SO- kötü olmayı bırak'

Muhtemelen bağlantıyı açıklayan fazladan bir paragraf veya iki yukarı garanti edecek kadar kafa karıştırıcı değildir. Kafa karıştırıcı olmayan, ancak teknik olarak doğru olan bazı ifadeler umuyorum. Belki sadece açıklamak için zaten bir bağlantı koymak "kaldır" kullanın?
derobert

3

Yükseltme, program çalışırken çalıştırılabilir, ancak gördüğünüz çalışan program aslında eski sürümüdür. Eski ikili, siz programı kapatana kadar diskte kalır.

Açıklama: Linux sistemlerinde, dosya sadece birkaç düğüm içeren bir inode'dur. Örneğin. /bin/bashGörmek sadece bir bağlantı inode 3932163sistemimde. Bağlantıda yayınlayarak hangi inode'un bir şey yaptığını bulabilirsiniz ls --inode /path. Bir dosya (inode) yalnızca ona işaret eden sıfır bağlantı varsa ve herhangi bir program tarafından kullanılmıyorsa kaldırılır. Paket yöneticisi yükseltildiğinde. /usr/bin/firefox, önce bağlantıyı kaldırır (sabit bağlantıyı kaldırır /usr/bin/firefox), ardından /usr/bin/firefoxfarklı bir inode'a (yeni firefoxsürümü içeren dosya) bir sabit bağlantı adı verilen yeni bir dosya oluşturur . Eski inode artık ücretsiz olarak işaretlenmiştir ve yeni verileri saklamak için yeniden kullanılabilir, ancak diskte kalır (inodlar yalnızca dosya sisteminizi oluşturduğunuzda oluşturulur ve asla silinmez). Bir sonraki başlangıcındafirefox, yenisi kullanılacak.

Çalışırken kendisini "yükselten" bir program yazmak istiyorsanız, aklıma gelen tek olası çözüm, kendi ikili dosyasının zaman damgasını düzenli olarak kontrol etmektir ve programın başlangıç ​​zamanından daha yeni ise, kendini yeniden yükleyin.


1
Aslında, dosyaların silinmesinin (bağlantısının kaldırılması) Unix'te nasıl çalıştığı nedeniyle; bkz. unix.stackexchange.com/questions/49299/… Ayrıca, en azından Linux'ta, gerçekten çalışan bir ikiliye yazamazsınız , "metin dosyası meşgul" hatası alırsınız.
derobert

Tuhaf ... Öyleyse nasıl oluyor? Debian'ın aptyükseltme çalışması? Iceweasel( Firefox) Dahil olmak üzere sorunsuz çalışan herhangi bir programı yükseltebilirim .
psimon

2
APT (veya daha doğrusu dpkg) dosyaların üzerine yazmaz. Bunun yerine, bağlantılarını kaldırır ve aynı adla yeni bir tane koyar. Açıklama için bağlandığım soru ve cevaba bakınız.
derobert

2
Sadece RAM'de olduğu için değil, hala diskte. Dosya, programın son örneği çıkana kadar silinmez.
derobert

2
Site, bir düzenleme önermeme izin vermiyor (temsilciniz yeterince yüksek olduğunda, yalnızca düzenlemeler yaparsınız, artık öneremezsiniz). Yani, bir yorum olarak: Unix sistemindeki bir dosyanın (inode) tipik olarak bir adı vardır. Ancak ln(sabit bağlantılar) ile adlar eklerseniz daha fazlası olabilir . rm(Bağlantıyı kaldır) ile adları kaldırabilirsiniz. Bir dosyayı doğrudan doğrudan silemezsiniz, yalnızca dosya adlarını kaldırabilirsiniz. Bir dosyanın adı yoksa ve ek olarak açık değilse, çekirdek dosyayı siler. Çalışan bir programın çalıştığı dosya açıktır, bu nedenle tüm adları kaldırdıktan sonra bile dosya hala vardır.
derobert

0

Thunderbird veya Firefox gibi katil uygulamaların hala çalışırken paket yöneticisi aracılığıyla nasıl güncellenebileceğini merak ediyorum. Peki, bunun gerçekten işe yaramadığını söyleyebilirim ... Bir paket güncellemesi çalışırken açık bıraktığımda Firefox'u oldukça korkunç bir şekilde kırdım. Bazen zorla öldürmek ve yeniden başlatmak zorunda kaldım, çünkü o kadar kırıldı ki düzgün bir şekilde kapatamadım.

Güncellenirken eski kodla ne olur? Normalde Linux'ta bir program belleğe yüklenir, bu nedenle program çalışırken disk üzerindeki yürütülebilir dosya gerekmez veya kullanılmaz. Aslında, yürütülebilir dosyayı bile silebilirsiniz ve program umursamaz ... Ancak, bazı programların yürütülebilir dosyaya ihtiyacı olabilir ve bazı işletim sistemleri (Windows gibi) yürütülebilir dosyayı kilitleyerek silme veya hatta yeniden adlandırma / taşıma işlemlerini gerçekleştirir. program çalışıyor. Firefox kırılıyor, çünkü aslında oldukça karmaşık ve GUI'sini (kullanıcı arayüzü) nasıl oluşturacağını söyleyen bir grup veri dosyası kullanıyor. Bir paket güncellemesi sırasında bu dosyaların üzerine yazılır (güncellenir), bu nedenle eski bir Firefox yürütülebilir (bellekte) yeni GUI dosyalarını kullanmaya çalıştığında, garip şeyler olabilir ...

Çalışırken kendini güncelleyen bir program a.out yazmak istediğimde ne yapmam gerekir? Sorunuza zaten çok sayıda yanıt var. Şuna bir göz at: /programming/232347/how-should-i-implement-an-auto-updater Bu arada programlama ile ilgili sorular StackOverflow'da daha iyi durumda.


2
Yürütülebilir dosyalar aslında isteğe bağlı olarak sayfalanır (değiştirilir). Tamamen belleğe yüklenmezler ve sistem başka bir şey için RAM istediğinde bellekten çıkarılabilirler. Eski sürüm aslında diskte kalır; bkz. unix.stackexchange.com/questions/49299/… . En azından Linux'ta, çalışan bir yürütülebilir dosyaya gerçekten yazamazsınız, "metin dosyası meşgul" hatasını alırsınız. Kök bile yapamaz. (Yine de Firefox konusunda oldukça haklısın).
derobert
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.