Kullanıcı Kipine Geçiş - Init Süreci

Linux çekirdeği açılış sürecinin kendisiyle ilgili son 2 adımında önce kök dosya sistemini bağlar, ardından kök dosya sisteminde yer alan bir adet uygulamayı başlatır.

Eğer çekirdek kendisiyle ilgili tüm açılış işlemlerini yapmasına rağmen, çalıştıracağı uygulamayı sistemde bulamaz veya uygulamayı çalıştıramaz ise, aşağıdaki gibi bir hata mesajı ile panic durumuna geçer:

Kernel panic - not syncing: No init found

Bu senaryo oluştuğunda henüz kullanıcı kipinde hiç bir uygulama çalıştırılamamış olduğundan, sistemi kullanmanız mümkün olmaz. Yapılacak yegane işlem, panic=10 gibi bir değeri çekirdek açılış parametresi olarak kullanmaktır. Bu değer çekirdeğe, herhangi bir sebeple panic durumuna düşülürse, sistemin girilen saniye değeri sonrasında yeniden başlatılmasının istendiğini belirtir. Özellikle gömülü sistemler için panic=xxx parametresinin kullanılması önerilir. Herhangi bir beklenmedik sebeple çekirdek bu duruma düşerse, özellikle uzak lokasyonlarda yer alan cihazlarınız olduğunu varsayarsak, panic durumda kalmaktansa bir süre sonra otomatik yeniden başlama sürecinin denenmesini talep etmek ve sisteme tekrar erişim sağlamayı ummak daha iyi bir alternatiftir.

Init Uygulamasının Çalıştırılması

Çekirdek tarafından kullanıcı kipinde çalıştırılan bu uygulamanın Process ID (PID) değeri her zaman 1 olur. Eğer çalıştırılan bu process herhangi bir şekilde sonlanırsa, çekirdek tarafından yeniden bir process çalıştırmayı denemek şeklinde bir aksiyon alınmaz. Dolayısıyla kullanıcı kipinden baktığımızda sistem kilitlenmiş olur.

SORU: Sistem açıldıktan sonra root kullanıcısı ile 1 nolu process'e kill 1 veya kill -9 1 komutu ile SIGTERM ve SIGKILL sinyali gönderirsek sistem kilitlenir mi?

1 nolu process'in çekirdek seviyesinde özel bir durumu vardır. Sistemdeki 1 nolu process'i root kullanıcısı ile dahi sinyal göndererek sonlandıramazsınız. 1 nolu process başlatılırken çekirdek tarafındaki process tablosunda SIGNAL_UNKILLABLE bayrağı ile işaretlenir. Bu sayede sonraki aşamalarda process'in sonlanmasına yol açabilecek türden hiç bir sinyal çekirdek tarafından bu process'e gönderilmez. Bu süreci detaylı incelemek isteyenler kernel/fork.c içerisindeki is_child_reaper() ve kernel/signal.c içerisindeki SIGNAL_UNKILLABLE kullanımlarına göz atabilir.

İlk çalıştırılan uygulamaya tehlikeli sinyallerin gönderilmesi çekirdek tarafından engelleniyor fakat uygulama içerisinde hata olması durumunda veya kontrollü bir exit yapılıyorsa, ilk çalışan uygulama sonlanmış olacağı için sistem kilitlenmiş olur.

Bu noktadan hareketle, sistemdeki ilk çalışacak uygulamayı kendimiz geliştirmek yerine, belki de toplam milyarlarca farklı sistem ve cihazda çalışan ve hiçbir hata içermediği böylece kanıtlanmış olan bir uygulamayı kullansak daha iyi olmaz mıydı?

Bu sorunun yanıtı System V ekolündeki Unix/Linux sistemleri için /sbin/init uygulamasıdır.

Eğer açılış sırasında çekirdek parametresi olarak init=xxx şeklinde bir parametre verilerek ilk çalıştırılacak uygulama özellikle belirtilmemişse, öntanımlı olarak /sbin/init çalıştırılır.

Aslında çoğu çekirdek versiyonunda öntanımlı uygulama olarak /sbin/init'in çalıştırılması denenir ancak başarısız olursa sistemi açabilmek adına bir kaç farklı dosya daha sistemde aranır ve çalıştırılmaya çalışılır. Çekirdek içerisindeki init/main.c dosyasındaki aşağıdaki bölümü inceleyiniz:

...
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

panic("No init found.  Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");

SORU: 1 nolu uygulamayı sonlandıramazsınız diyorsunuz. Peki 1 nolu init uygulaması sistem açıldıktan, tüm servisler (daemon'lar) ayağa kalktıktan sonra kendi isteğiyle exit ederse ne olur? Sistem açılmış, servisler çalışmaya başlamış olduğunda göre init'in çalışmasına halen ihtiyaç var mıdır?

Böyle bir durumda sistemimiz intihar teşebbüsünde bulunmuş olacaktır. Bir uygulamanın servis (daemon) olması ne demektir? Uygulamaların onları başlatan uygulamalar (parent process) sonlansa dahi arkada çalışmaya devam eder hale getirilmesine şeytanlaştırma :) (daemonize) ismini veriyoruz. Aslında bu sürecin arkasında yatan basit mantık da, daemon haline getirmek istediğimiz uygulamanın parent process id değerini (PPID), 1 nolu process olarak değiştirmekten ibarettir. 1 nolu process hiç ölmeyeceği için daemon yaptığımız uygulamanın parent process'i hiç sonlanmayacak, bu sayede daemon da arka planda çalışmaya devam edecektir. Herhangi bir şekilde sadece 1 nolu uygulamayı sonlandırabiliyor olsaydık dahi, tüm daemon'ların anası 1 nolu process olduğundan, çekirdek tarafından tüm child process'leri de sonlandırılacaktı.

/sbin/init'le Çalışmak

Buraya kadar okuduklarımızda, açılış sürecini yönetecek uygulamayı sıfırdan yazma riskini üstlenmektense, onun yerine init uygulamasını kullanmamız gerektiğini öğrendik. Peki, açılış sürecinin tümünü yönetmek istediğimize göre, bu görevi init'e devredersek sonra nasıl tekrar süreci kontrol edeceğiz?

Init uygulama kodu oldukça sadedir. Temel bazı işlevler haricinde fazla bir iş yapmaz. Normal Linux dağıtımlarımızda kullandığımız init uygulaması ile busybox içerisinden çıkan kırpılmış init uygulaması birebir aynı değildir ancak temelde aynı işlevleri yerine getirirler. Gömülü sistemlerde kullanacağımız busybox init uygulaması orjinalinden farklı olarak çalışm seviyelerini (runlevel) desteklemez. Ancak çalışma seviyelerinin gömülü bir sistem için zaten pek işlevsel bir kullanımı bulunmamaktadır. Bu sebeple busybox init uygulamasının konfigürasyon dosyasında da minik farklar bulunur.

Init uygulaması ayağa kalktığında /etc/inittab dosyasını okur. Busybox versiyonunda /etc/inittab dosyasının okunması derleme sürecinde CONFIG_FEATURE_USE_INITTAB opsiyonuyla değiştirilebilmektedir. Bu özellik devre dışı bırakıldığında da init uygulaması çalışabilir bir sisteme ulaşmak adına öntanımlı olarak /etc/init.d/rcS betik uygulamasını çalıştırır ve sistemin öntanımlı konsolunda kabuk uygulamasını başlatır.

Süreci daha açık hale getirmek için /etc/inittab dosyasını kullanma yolunu tercih edeceğiz (önerilen çalışma şekli de budur).

Aşağıda busybox için örnek bir inittab dosyası yer almaktadır:

# /etc/inittab

::sysinit:/etc/init.d/rcS

# /bin/sh invocations on selected ttys
#
# Note below that we prefix the shell commands with a "-" to indicate to the
# shell that it is supposed to be a login shell.  Normally this is handled by
# login, but since we are bypassing login in this case, BusyBox lets you do
# this yourself...

# Start an "askfirst" shell on the console (whatever that may be)
::askfirst:-/bin/sh

# Start an "askfirst" shell on /dev/tty2-4
tty2::askfirst:-/bin/sh
tty3::askfirst:-/bin/sh
tty4::askfirst:-/bin/sh

# /sbin/getty invocations for selected ttys
tty5::respawn:/sbin/getty 38400 tty5
tty6::respawn:/sbin/getty 38400 tty6

# Example how to put a getty on a modem line.
#::respawn:/sbin/getty 115200 ttyS2

# Stuff to do when restarting the init process
::restart:/sbin/init

# Stuff to do before rebooting
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a

Şimdi bu dosyadaki önemli satırları detaylandıralım.

::sysinit:/etc/init.d/rcS

Dosyada bizi en çok ilgilendiren bölüm, sysinit ile belirtilen kısımlardır. Bu anahtar kelime ile, init uygulamasına açılış sürecinde bizim adımıza bir başka uygulamayı başlatmasını söylüyoruz. Yani açılış sürecini init uygulamasına devretmekle kaybettiğimiz kontrolü, sysinit ile tanımladığımız uygulama üzerinden tekrar geri alıyoruz. Bu şekilde belirttiğimiz uygulamamızda sistemin açılışında yapılması gereken tüm işlemleri gerçekleştirip, gerekli servisleri de ayağa kaldırdığımızda tam anlamıyla çalışan bir sisteme kavuşmuş olacağız.

sysinit deyimi birden fazla satırda yer alabilir. Böyle bir kullanımda init uygulaması dosyada yer alan sıralama doğrultusunda her bir sysinit ile belirtilen uygulamanın çalışmasının sonlanmasını bekler ve bir sonraki uygulamaya geçer. Bu şekildeki bir kullanım yerine inittab dosyasında tek bir sysinit içeren satırın yer alması ve çalıştırılacak diğer mekanizmaların ilgili uygulama içerisinden yönetilmesi tercih edilir.

::askfirst:-/bin/sh

Bu özel satır, çekirdeğin açılış sürecinde de kullandığımız ön tanımlı sistem konsolu üzerinde, /bin/sh kabuğunu başlatacaktır. askfirst anahtar kelimesi sayesinde karşımıza bir bilgi mesajı çıkacak ENTER tuşuna basmamız halinde ise kabuk uygulaması çalışacak ve kabuğu kullanmaya başlayabileceğiz. Herhangi bir güvenlik kontrolü içermeyen bu mekanizma, geliştirme sürecinin başlangıcında işimize yarayabilir ancak daha sonra devre dışı bırakılmalıdır. Aksi takdirde sistemimize bir şekilde seri konsol bağlantısı yapmayı başaran birisi, doğrudan sistem üzerinde bir kabuk çalıştırabilir.

tty2::askfirst:-/bin/sh

Konsolun haricinde benzer şekilde, tty2, tty3 ve tty4 sanal konsollarında güvenlik kontrolü olmadan kabuk uygulamasının başlatılması bu şekilde öntanımlı olarak örnek inittab dosyasında yer almaktadır. Eğer sisteme bağlı bir monitor var ve bu sanal konsol mekanizmasını kullanmak istiyorsak, aşağıda anlatacağımız şekilde askfirst ile değil, parola korumasıyla girişi sağlamalıyız. Ya da pek çok gömülü sistemde olduğu gibi, bu şekilde bir sanal konsol kullanımına ihtiyacımız yok ise, bu satırların inittab dosyasından çıkarılması yerinde olacaktır.

tty5::respawn:/sbin/getty 38400 tty5

Bu örnekte 5. sanal konsolda getty uygulamasının 38400 tty5 parametreleri ile başlatılması sağlanmaktadır. Eğer sanal konsol kullanacaksak bu yöntem izlenmelidir. getty uygulaması çalıştığı konsolda öncelikle login uygulamasını çalıştıracak ve kullanıcı adı, parola kontrolünü yapacak, başarılı olması durumunda kullanıcı adına kabuk uygulamasını başlayacaktır.

respawn özel deyimi ise, init uygulaması tarafından sağlanan ek bir özellik olup, ilgili kuralda çalştırılmak üzere belirtilen uygulama herhangi bir şekilde sonlanacak olursa, otomatik olarak aynı parametrelerle yeniden başlatılmasını (respawning) sağlar (init uygulaması bu gibi fonksiyonları sağlayabilmek adına da hiç ölmemelidir).

Respawn özelliği gömülü sistemimizde bizim için faydalı bir kullanım alanı bulabilir. Örnek olarak sisteminizde 3 adet önemli daemon yazdığınızı düşünelim. 4. olarak da bu daemon'ların çalışıp çalışmadığını periyodik olarak kontrol eden ve sürekli arka planda çalışan bir uygulamanız olduğunu düşünelim, adı controller olsun. Eğer controller uygulamanızda uygulamanın sonlanmasına neden olacak herhangi bir hata olursa, tüm kontrol mekanizması devre dışı kalacaktır. controller uygulamanızı kontrol edecek ayrı bir uygulama yapsanız aynı sorun onun için de geçerli olacağından, hiç bir zaman ölmeyecek bir uygulamadan yardım almamız gerekecektir ki bu uygulama init olmaktadır. Teorik olarak bunun için çekirdekten de yardım talep edebilirdik ancak Linux çekirdeği böyle işlere bulaşmaz ve kullanıcı kipinde çalışan uygulamalardan ilk çalıştırdığı init haricindekilere özel bir muamele yapmaz. O yüzden biz de init uygulamasının yardımına başvurarak, sistemdeki diğer servisleri kontrol eden controller uygulamamızı, respawn deyimiyle /etc/inittab dosyasına yazıp, init tarafından sonlansa bile yeniden başlayacak hale getirebiliriz.

Bu noktada şöyle de düşünebilirsiniz: zaten controller uygulama kodumuz çok basit olacağından hiç çökmeyecek bir şey yazabiliriz. O yüzden gene de init'in yardımına ihtiyacımız var mı?

Doğru bir soru, gidiş yolundan puan verilebilir. Temel hedef tüm hataların kontrol edildiği ve çökmeyecek uygulamalar yazabilmek olmalıdır. Aksi takdirde bu iş tüm uygulamaların init üzerinden başlatılmasına kadar gider ki bu işlemin burada ayrıntısına girmeyeceğimiz başka zorlukları da vardır.

Fakat özellikle gömülü sistemler için, sistemin sağlıklı çalışması ve örneğin uzaktan bağlantı yapılabilmeye devam etmesi için gereken minimum servislerin her zaman çalışır durumda olduğunun kontrolünü yapan ve çalışmayanları yeniden başlatan, belki bundan biraz daha fazlasını da yapan genel bir controller yazılımının kendisini init üzerinden respawn ile çalıştırılacak şekilde başlatmak daha doğru bir davranış olacaktır.

controller uygulamanızı çok iyi yazmış olabilirsiniz, ancak sistemdeki başka bir uygulamanın belleği tükettiğini ve yeni bellek isteyen bir uygulama için çekirdek içerisindeki Out Of Memory Killer mekanizmasının bellekte yer açmak için kullandığı algoritma sonucu sizin controller process'inizi seçtiğini ve onu sorgusuz sualsiz öldürdüğünü düşünelim (çekirdek gerçekten böyle işler de yapmaktadır). Bu durumda sizin uygulamanız çok iyi yazılmış olsa bile beklentiniz dışında bir sonlanma da yaşayabilirsiniz. Linux OOM Killer algoritmasının aday process seçim sürecinde controller uygulamanıza daha düşük değer vermesini sağlamak için yapabileceğiniz bazı işlemler bulunmaktadır. Ancak bunun gibi farkında olamayabileceğiniz detaylar nedeniyle, controller uygulaması için init uygulamasından yardım talep etmemiz yerinde olacaktır, ayıp karşılanmaz.

::respawn:/sbin/getty 115200 ttyS2

Bu satır respawn mekanizmasıyla ttyS2 seri portunda 115200 baudrate ile getty uygulamasının başlatılmasını sağlamaktadır.

Gömülü sistemlerde seri port aygıt dosyası isimlendirmeleri kullanılan teknoloji ve board ailesine göre (ttyO1 vb.) değişkenlik gösterebilmektedir. Bu bölümde sistemin standart konsolu veya diğer seri portları üzerinde kullanıcı kimlik denetimi yaparak girişe izin vermek istediğimiz aygıtlar için her biri ayrı bir satırda yer alacak şekilde ayarlamalarımızı yapabiliriz.

::restart:/sbin/init

Bu ayar bir miktar kafa karıştırıcı olabilir. Bazen SIGHUP sinyali göndererek /etc/inittab dosyasının yeniden okunmasını sağlamak yeterli olmaz ve init sürecini yeniden başlatmak gerekir. Init sürecini yeniden başlatmak istediğimizde (restart), hangi uygulamanın çalıştırılacağını söylüyor. Bazı özel senaryolarda, sistemin kök dizinini çalışma sırasında değiştirmeniz gerekebilir. Yeni kök dizinde farklı bir /sbin/init versiyonu da yer alabilir. Buradaki öntanımlı davranışla init'e restart eyleminde gene /sbin/init uygulamasının çalıştırılacağını söylemiş oluyoruz.

SORU: Peki bu şekilde bir restart işlemi sonucunda yeni oluşacak init process'in PID değeri 1'den büyük olmayacak mıdır?

::ctrlaltdel:/sbin/reboot

Bu deyim özellikle X86 tabanlı sistemlerde klavyeden CTRL-ALT-DEL tuşlarına basıldığında tetiklenen sistemi yeniden başlatma işleminde hangi uygulamanın çalışacağını göstermektedir.

::shutdown:/bin/umount -a -r

Shutdown anahtar kelimesi ile yer alan satırlar, sistem kapatılırken hangi uygulamaların çalışacağını belirtir. Birden fazla tekrar edildiğinde, kapanış sırasında inittab dosyasında yer aldığı sıralama ile ilgili komutlar çalıştırılacaktır.

Shutdown anahtar kelimesinin tekrarından oluşan satırlar ile kapanış sırasında birden fazla işlem yapmak yerine,

::shutdown:/etc/kapanis

örneğindeki gibi tüm süreci bir betik uygulamasına yönlendirip, betik içerisinde gereken diğer tüm işlemleri yapmanız daha doğru olacaktır.

results matching ""

    No results matching ""