Bash Komut Dosyalarında Linux Sinyalleri Nasıl Kullanılır

Yayınlanan: 2022-08-09
Bir bash istemi gösteren Linux dizüstü bilgisayar
fatmawati achmad zaenuri/Shutterstock.com

Linux çekirdeği, tepki vermeleri gereken olaylar hakkında süreçlere sinyaller gönderir. İyi niyetli komut dosyaları, sinyalleri zarif ve sağlam bir şekilde işler ve Ctrl+C'ye bassanız bile arkalarını temizleyebilir. İşte nasıl.

Sinyaller ve Süreçler

Sinyaller, komut dosyaları, programlar ve arka plan programları gibi işlemlere gönderilen kısa, hızlı, tek yönlü mesajlardır. Sürecin olan bir şey hakkında bilgi sahibi olmasını sağlarlar. Kullanıcı Ctrl+C'ye basmış olabilir veya uygulama erişimi olmayan belleğe yazmaya çalışmış olabilir.

Sürecin yazarı, kendisine belirli bir sinyalin gönderilebileceğini öngördüyse, bu sinyali işlemek için programa veya komut dosyasına bir rutin yazabilir. Böyle bir rutine sinyal işleyici denir. Sinyali yakalar veya yakalar ve buna yanıt olarak bazı eylemler gerçekleştirir.

Linux Terminalinden İşlemler Nasıl Yönetilir: Bilmeniz Gereken 10 Komut
İLGİLİ Linux Terminalinden İşlemler Nasıl Yönetilir: Bilmeniz Gereken 10 Komut

Göreceğimiz gibi, Linux çok sayıda sinyal kullanır, ancak komut dosyası oluşturma açısından, ilgilenebileceğiniz yalnızca küçük bir sinyal alt kümesi vardır. Özellikle, önemsiz olmayan komut dosyalarında, kapatılacak komut dosyası tuzağa düşürülmeli (mümkünse) ve zarif bir kapatma gerçekleştirilmelidir.

Örneğin, geçici dosyalar oluşturan veya güvenlik duvarı bağlantı noktalarını açan komut dosyalarına, geçici dosyaları silme veya bağlantı noktalarını kapatmadan önce kapatma şansı verilebilir. Komut dosyası, sinyali aldığı anda ölürse, bilgisayarınız öngörülemeyen bir durumda kalabilir.

Kendi komut dosyalarınızdaki sinyalleri nasıl ele alabileceğiniz aşağıda açıklanmıştır.

Sinyallerle Tanışın

Bazı Linux komutlarının şifreli adları vardır. Sinyalleri yakalayan komut öyle değil. trap denir. Ayrıca, Linux'un kullandığı tüm sinyal listesini bize göstermek için -l (liste) seçeneğiyle trap da kullanabiliriz.

 tuzak -l 

Ubuntu'daki sinyalleri trap -l ile listeleme

Numaralı listemiz 64'te bitmesine rağmen, aslında 62 sinyal var. 32 ve 33 sinyalleri eksik. Linux'ta uygulanmazlar. Gerçek zamanlı iş parçacıklarını işlemek için gcc derleyicisindeki işlevsellik ile değiştirildiler. SIGRTMIN SIGRTMAX her şey gerçek zamanlı sinyallerdir.

Farklı Unix benzeri işletim sistemlerinde farklı listeler göreceksiniz. Örneğin OpenIndiana'da 32 ve 33 sinyalleri ve toplam sayıyı 73'e çıkaran bir dizi ekstra sinyal mevcuttur.

OpenIndiana'daki sinyalleri trap -l ile listeleme

Sinyallere ad, numara veya kısaltılmış adları ile başvurulabilir. Kısaltılmış adları, yalnızca baştaki "SIG" kaldırılmış olan adlarıdır.

Sinyaller birçok farklı nedenden dolayı yükseltilir. Onları deşifre edebilirseniz, amaçları adlarında yazılıdır. Bir sinyalin etkisi birkaç kategoriden birine girer:

  • Sonlandır: İşlem sonlandırılır.
  • Yoksay: Sinyal süreci etkilemez. Bu yalnızca bilgi amaçlı bir sinyaldir.
  • Çekirdek: Bir döküm çekirdek dosyası oluşturulur. Bu genellikle, işlemin bellek ihlali gibi bir şekilde ihlal edilmesi nedeniyle yapılır.
  • Durdur: İşlem durdurulur. Yani duraklatılır , sonlandırılmaz.
  • Devam: Durdurulan bir işleme yürütmeye devam etmesini söyler.

Bunlar en sık karşılaşacağınız sinyallerdir.

  • SIGHUP : Sinyal 1. SSH sunucusu gibi uzak bir ana bilgisayara bağlantı beklenmedik şekilde kesildi veya kullanıcı oturumu kapattı. Bu sinyali alan bir komut dosyası düzgün bir şekilde sonlandırılabilir veya uzak ana bilgisayara yeniden bağlanmayı deneyebilir.
  • SIGINT : Sinyal 2. Kullanıcı, bir işlemi kapanmaya zorlamak için Ctrl+C kombinasyonuna basmıştır veya sinyal 2 ile birlikte kill komutu kullanılmıştır. Teknik olarak, bu bir kesme sinyalidir, sonlandırma sinyali değil, bir sinyal işleyici genellikle sonlandırılır.
  • SIGQUIT : Sinyal 3. Kullanıcı, bir işlemi çıkmaya zorlamak için Ctrl+D kombinasyonuna bastı veya kill komutu sinyal 3 ile kullanıldı.
  • SIGFPE : Signal 8. İşlem, sıfıra bölme gibi geçersiz (imkansız) bir matematiksel işlem gerçekleştirmeye çalıştı.
  • SIGKILL : Sinyal 9. Bu, bir giyotinin sinyal eşdeğeridir. Onu yakalayamaz veya görmezden gelemezsiniz ve anında gerçekleşir. İşlem derhal sonlandırılır.
  • SIGTERM : Signal 15. Bu, SIGKILL daha düşünceli versiyonudur. SIGTERM ayrıca bir işlemin sonlandırılmasını söyler, ancak kapana kısılabilir ve işlem kapanmadan önce temizleme işlemlerini çalıştırabilir. Bu, zarif bir kapatma sağlar. Bu, kill komutu tarafından oluşturulan varsayılan sinyaldir.

Komut Satırındaki Sinyaller

Bir sinyali yakalamanın bir yolu, sinyalin numarası veya adı ve sinyal alındığında olmasını istediğiniz bir yanıt ile trap kullanmaktır. Bunu bir terminal penceresinde gösterebiliriz.

Bu komut SIGINT sinyalini yakalar. Yanıt, terminal penceresine bir metin satırı yazdırmaktır. echo ile -e (çıkışları etkinleştir) seçeneğini kullanıyoruz, böylece “ \n ” biçim belirtecini kullanabiliriz.

 trap 'echo -e "\nCtrl+c Algılandı." SIGINT 

Komut satırında Ctrl+C'yi yakalama

Ctrl+C kombinasyonuna her bastığımızda metin satırımız yazdırılır.

Bir sinyalde bir bindirmenin ayarlanıp ayarlanmadığını görmek için -p (bindirme yazdır) seçeneğini kullanın.

 tuzak -p İŞARET 

Bir sinyale tuzak kurulup kurulmadığını kontrol etme

trap kullanmak aynı şeyi yapar.

Sinyali yakalanmamış, normal durumuna sıfırlamak için bir tire “ - ” ve yakalanan sinyalin adını kullanın.

 tuzak - SIGINT
 tuzak -p İŞARET 

Bir sinyalden bir tuzağın çıkarılması

trap -p komutundan çıkış olmaması, o sinyalde ayarlanmış bir tuzak olmadığını gösterir.

Komut Dosyalarında Sinyalleri Yakalama

Aynı genel format trap komutunu bir komut dosyası içinde kullanabiliriz. Bu komut dosyası, SIGINT , SIGQUIT ve SIGTERM olmak üzere üç farklı sinyali yakalar.

 #!/bin/bash

tuzak "echo I SIGINT sonlandırıldı; çık" SIGINT
tuzak "echo I SIGQUIT sonlandırıldı; çıkış" SIGQUIT
trap "echo I SIGTERM sonlandırıldı; çık" SIGTERM

yankı $$
sayaç=0

doğru iken
yapmak 
  echo "Döngü numarası:" $((++sayaç))
  uyku 1
tamamlamak

Üç trap ifadesi, komut dosyasının en üstündedir. exit komutunu sinyallerin her birine verilen yanıtın içine dahil ettiğimizi unutmayın. Bu, komut dosyasının sinyale tepki verdiği ve ardından çıktığı anlamına gelir.

Metni düzenleyicinize kopyalayın ve “simple-loop.sh” adlı bir dosyaya kaydedin ve chmod komutunu kullanarak yürütülebilir hale getirin. Kendi bilgisayarınızda takip etmek istiyorsanız, bu makaledeki tüm komut dosyalarına bunu yapmanız gerekecektir. Her durumda uygun komut dosyasının adını kullanın.

 chmod +x basit döngü.sh 

chmod ile çalıştırılabilir bir komut dosyası yapma

Senaryonun geri kalanı çok basit. Komut dosyasının işlem kimliğini bilmemiz gerekiyor, bu yüzden komut dosyası bize bunu yansıtıyor. $$ değişkeni, betiğin işlem kimliğini tutar.

counter adında bir değişken oluşturup sıfıra ayarlıyoruz.

while döngüsü, zorla durdurulmadıkça sonsuza kadar çalışacaktır. counter değişkenini arttırır, ekrana yansıtır ve bir saniye uyur.

Komut dosyasını çalıştıralım ve ona farklı sinyaller gönderelim.

 ./simple-loop.sh 

Ctrl+C ile sonlandırıldığını tanımlayan bir komut dosyası

“Ctrl+C”ye bastığımızda terminal penceresine mesajımız yazdırılır ve script sonlandırılır.

Tekrar çalıştıralım ve kill komutunu kullanarak SIGQUIT sinyalini gönderelim. Bunu başka bir terminal penceresinden yapmamız gerekecek. Kendi komut dosyanız tarafından bildirilen işlem kimliğini kullanmanız gerekir.

 ./simple-loop.sh
 öldür -SIGQUIT 4575 

SIGQUIT ile sonlandırıldığını tanımlayan bir komut dosyası

Beklendiği gibi, komut dosyası gelen sinyali bildirir ve ardından sonlandırılır. Ve son olarak, konuyu kanıtlamak için SIGTERM sinyali ile tekrar yapacağız.

 ./simple-loop.sh
 öldür -SIGTERM 4584 

SIGTERM ile sonlandırıldığını tanımlayan bir komut dosyası

Bir komut dosyasında birden çok sinyali yakalayabileceğimizi ve her birine bağımsız olarak tepki verebileceğimizi doğruladık. Tüm bunları ilginçten faydalıya doğru ilerleten adım, sinyal işleyicileri eklemektir.

Komut Dosyalarında Sinyalleri Kullanma

Yanıt dizesini, komut dosyanızdaki bir işlevin adıyla değiştirebiliriz. trap komutu, sinyal algılandığında bu işlevi çağırır.

Bu metni bir düzenleyiciye kopyalayın ve “grace.sh” adlı bir dosya olarak kaydedin ve chmod ile çalıştırılabilir hale getirin.

 #!/bin/bash

trap graceful_shutdown SIGINT SIGQUIT SIGTERM

zarif_shutdown()
{
  echo -e "\nGeçici dosya kaldırılıyor:" $temp_file
  rm -rf "$temp_file"
  çıkış
}

temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX)
echo "Geçici dosya oluşturuldu:" $temp_file

sayaç=0

doğru iken
yapmak 
  echo "Döngü numarası:" $((++sayaç))
  uyku 1
tamamlamak

Komut dosyası, tek bir trap ifadesi kullanarak üç farklı sinyal ( SIGHUP , SIGINT ve SIGTERM ) için bir tuzak kurar. Yanıt, graceful_shutdown() işlevinin adıdır. Yakalanan üç sinyalden biri alındığında işlev çağrılır.

Komut dosyası, “/tmp” dizininde mktemp kullanarak geçici bir dosya oluşturur. Dosya adı şablonu “tmp.XXXXXXXXXX” olduğundan, dosyanın adı “tmp” olacaktır. ardından on rastgele alfasayısal karakter gelir. Dosyanın adı ekranda yankılanır.

Komut dosyasının geri kalanı, bir counter değişkeni ve sonsuz bir while döngüsü ile öncekiyle aynıdır.

 ./grace.sh 

Geçici bir dosyayı silerek zarif bir kapatma gerçekleştiren bir komut dosyası

Dosyaya kapanmasına neden olan bir sinyal gönderildiğinde, graceful_shutdown() işlevi çağrılır. Bu, tek geçici dosyamızı siler. Gerçek dünya durumunda, betiğinizin gerektirdiği her türlü temizleme işlemini gerçekleştirebilir.

Ayrıca, kapana kısılmış tüm sinyallerimizi bir araya topladık ve bunları tek bir işlevle ele aldık. Sinyalleri tek tek yakalayabilir ve kendi özel işleyici işlevlerine gönderebilirsiniz.

Bu metni kopyalayın ve “triple.sh” adlı bir dosyaya kaydedin ve chmod komutunu kullanarak yürütülebilir hale getirin.

 #!/bin/bash

trap sigint_handler SIGINT
tuzak sigusr1_handler SIGUSR1
tuzak çıkış_handler EXIT

function signt_handler() {
  ((++signt_count))

  echo -e "\nSIGINT $sigint_count zaman(lar) aldı."

  if [[ "$sigint_count" -eq 3 ]]; sonra
    echo "Kapatma işlemi başlatılıyor."
    loop_flag=1
  fi
}

function sigusr1_handler() {
  echo "SIGUSR1 $((++sigusr1_count)) zaman(lar)ını gönderdi ve aldı."
}

function exit_handler() { 
  echo "Çıkış işleyicisi: Komut dosyası kapanıyor..."
}

yankı $$
sigusr1_count=0
signt_count=0
loop_flag=0

while [[ $loop_flag -eq 0 ]]; yapmak
  öldür -SIGUSR1 $$
  uyku 1
tamamlamak

Komut dosyasının üst kısmında üç tuzak tanımlarız.

  • Biri SIGINT yakalar ve sigint_handler() adlı bir işleyiciye sahiptir.
  • İkincisi, SIGUSR1 adlı bir sinyali yakalar ve sigusr1_handler() adlı bir işleyici kullanır.
  • Üç numaralı tuzak, EXIT sinyalini yakalar. Bu sinyal, kapandığında betiğin kendisi tarafından yükseltilir. EXIT için bir sinyal işleyici ayarlamak, komut dosyası sona erdiğinde her zaman çağrılacak bir işlev ayarlayabileceğiniz anlamına gelir (Sinyal SIGKILL ile öldürülmediği sürece). İşleyicimiz, exit_handler() olarak adlandırılır.

SIGUSR1 ve SIGUSR2 , komut dosyalarınıza özel sinyaller gönderebilmeniz için sağlanan sinyallerdir. Onları nasıl yorumlayacağınız ve onlara nasıl tepki vereceğiniz tamamen size kalmış.

Sinyal işleyicileri şimdilik bir kenara bırakırsak, betiğin gövdesi size tanıdık gelecektir. İşlem kimliğini terminal penceresine yansıtır ve bazı değişkenler oluşturur. sigusr1_count SIGINT sayısını kaydeder ve sigint_count , SIGUSR1 işlenme sayısını kaydeder. loop_flag değişkeni sıfıra ayarlanır.

while döngüsü sonsuz bir döngü değildir. loop_flag değişkeni sıfır olmayan herhangi bir değere ayarlanırsa, döngü duracaktır. while döngüsünün her dönüşü, SIGUSR1 sinyalini komut dosyasının işlem kimliğine göndererek bu komut dosyasına göndermek için kill kullanır. Komut dosyaları kendilerine sinyal gönderebilir!

sigusr1_handler() işlevi, sigusr1_count değişkenini artırır ve terminal penceresine bir mesaj gönderir.

SIGINT sinyali her alındığında, siguint_handler() işlevi sigint_count değişkenini artırır ve değerini terminal penceresine yansıtır.

sigint_count değişkeni üçe eşitse, loop_flag değişkeni bire ayarlanır ve terminal penceresine kullanıcıya kapatma işleminin başladığını bildiren bir mesaj gönderilir.

loop_flag artık sıfıra eşit olmadığı için, while döngüsü sona erer ve komut dosyası tamamlanır. Ancak bu eylem otomatik olarak EXIT sinyalini yükseltir ve exit_handler() işlevi çağrılır.

 ./üçlü.sh 

Kapatmak için üç Ctrl+C kombinasyonu gerektiren ve kapatma sırasında EXIT sinyalini yakalayan SIGUSR1 kullanan bir komut dosyası

Üç Ctrl+C basıldıktan sonra komut dosyası sonlandırılır ve otomatik olarak exit_handler() işlevini çağırır.

Sinyalleri Okuyun

Sinyalleri yakalayarak ve bunlarla basit işleyici işlevleriyle ilgilenerek, beklenmedik bir şekilde sonlandırılsalar bile Bash betiklerinizi arkalarında toparlayabilirsiniz. Bu size daha temiz bir dosya sistemi sağlar. Ayrıca betiği bir sonraki çalıştırışınızda kararsızlığı önler ve betiğinizin amacına bağlı olarak güvenlik açıklarını bile önleyebilir.

İLGİLİ: Linux Sisteminizin Güvenliğini Lynis ile Denetleme