C dilinde sinyal işleyicileri nasıl kullanılır?

How Use Signal Handlers C Language



Bu yazıda size C dilini kullanarak Linux'ta sinyal işleyicilerin nasıl kullanılacağını göstereceğiz. Ama önce sinyalin ne olduğunu, programınızda kullanabileceğiniz bazı ortak sinyalleri nasıl üreteceğini tartışacağız ve ardından program yürütülürken bir program tarafından çeşitli sinyallerin nasıl işlendiğine bakacağız. Haydi başlayalım.

sinyal

Bir sinyal, bir işlemi veya iş parçacığını bazı önemli durumların geldiğini bildirmek için oluşturulan bir olaydır. Bir işlem veya iş parçacığı bir sinyal aldığında, işlem veya iş parçacığı ne yaptığını durduracak ve bazı eylemler gerçekleştirecektir. Sinyal, süreçler arası iletişim için faydalı olabilir.







Standart Sinyaller

Sinyaller başlık dosyasında tanımlanır sinyal.h makro sabiti olarak Sinyal adı bir SIG ile başlar ve ardından sinyalin kısa bir açıklaması gelir. Bu nedenle, her sinyalin benzersiz bir sayısal değeri vardır. Programınız her zaman sinyal numarasını değil, sinyallerin adını kullanmalıdır. Bunun nedeni, sinyal numarasının sisteme göre değişebilmesidir ancak isimlerin anlamı standart olacaktır.



makro NSIG tanımlanan toplam sinyal sayısıdır. Değeri NSIG tanımlanan toplam sinyal sayısından bir büyüktür (Tüm sinyal numaraları ardışık olarak atanır).



Standart sinyaller şunlardır:





Sinyal Adı Açıklama
SIGHUP İşlemi askıya alın. SIGHUP sinyali, muhtemelen bir uzak bağlantının kesilmesi veya kapanması nedeniyle, kullanıcı terminalinin bağlantısının kesildiğini bildirmek için kullanılır.
SIGINT Süreci kesintiye uğratın. Kullanıcı INTR karakterini (normalde Ctrl + C) yazdığında SIGINT sinyali gönderilir.
SIGQUIT İşlemden çıkın. Kullanıcı QUIT karakterini yazdığında (normalde Ctrl + ) SIGQUIT sinyali gönderilir.
FOK Yasadışı talimat. Çöp veya ayrıcalıklı talimat yürütmek için bir girişimde bulunulduğunda, SIGILL sinyali üretilir. Ayrıca, yığın taştığında veya sistem bir sinyal işleyiciyi çalıştırırken sorun yaşadığında SIGILL oluşturulabilir.
SIGTRAP İz tuzağı. Bir kesme noktası talimatı ve diğer tuzak talimatı SIGTRAP sinyalini üretecektir. Hata ayıklayıcı bu sinyali kullanır.
SİGABRT İptal et. Abort() işlevi çağrıldığında SIGABRT sinyali üretilir. Bu sinyal, programın kendisi tarafından algılanan ve abort() işlev çağrısı tarafından bildirilen bir hatayı gösterir.
SIGFPE Kayan nokta istisnası. Önemli bir aritmetik hata oluştuğunda SIGFPE sinyali üretilir.
SIGUSR1 ve SIGUSR2 SIGUSR1 ve SIGUSR2 sinyalleri istediğiniz gibi kullanılabilir. Basit süreçler arası iletişim için sinyali alan programda onlar için bir sinyal işleyicisi yazmak yararlıdır.

Sinyallerin Varsayılan Eylemi

Her sinyalin aşağıdakilerden biri olan varsayılan bir eylemi vardır:

Terim: İşlem sonlandırılacaktır.
Çekirdek: İşlem sonlandırılacak ve bir çekirdek döküm dosyası üretecektir.
Ateş: İşlem sinyali görmezden gelecektir.
Durmak: İşlem duracaktır.
Hesap: İşlem durdurulmadan devam edecek.



Varsayılan eylem, işleyici işlevi kullanılarak değiştirilebilir. Bazı sinyalin varsayılan eylemi değiştirilemez. SIGKILL ve SİGABRT sinyalin varsayılan eylemi değiştirilemez veya yok sayılamaz.

Sinyal İşleme

Bir işlem bir sinyal alırsa, işlemin bu tür bir sinyal için bir eylem seçeneği vardır. İşlem sinyali yok sayabilir, bir işleyici işlevi belirtebilir veya bu tür bir sinyal için varsayılan eylemi kabul edebilir.

  • Sinyal için belirtilen eylem yoksayılırsa, sinyal hemen atılır.
  • Program, aşağıdaki gibi bir işlevi kullanarak bir işleyici işlevini kaydedebilir: sinyal veya imza . Buna işleyici denir, sinyali yakalar.
  • Sinyal işlenmemiş veya göz ardı edilmemişse, varsayılan eylemi gerçekleşir.

kullanarak sinyali işleyebiliriz sinyal veya imza işlev. Burada en basitinin nasıl olduğunu görüyoruz sinyal() işlevi sinyalleri işlemek için kullanılır.

intsinyal() (intimza, geçersiz (*işlev)(int))

NS sinyal() arayacak işlev süreç bir sinyal alırsa işlev imza . NS sinyal() işleve bir işaretçi döndürür işlev başarılıysa veya errno'ya bir hata, aksi halde -1'e bir hata döndürür.

NS işlev işaretçi üç değere sahip olabilir:

  1. SIG_DFL : Sistem varsayılan işlevine bir işaretçidir SIG_DFL () , ilan edildi H başlık dosyası. Sinyalin varsayılan eylemini almak için kullanılır.
  2. SIG_IGN : Sistem yoksayma işlevine bir işaretçidir. SIG_IGN () , ilan edildi H başlık dosyası.
  3. Kullanıcı tanımlı işleyici işlevi işaretçisi : Kullanıcı tanımlı işleyici işlev türü boşluk(*)(int) , dönüş türünün geçersiz olduğu ve int türünde bir bağımsız değişken olduğu anlamına gelir.

Temel Sinyal İşleyici Örneği

#Dahil etmek
#Dahil etmek
#Dahil etmek
geçersizsig_handler(intimza){

//İşleyici işlevinin dönüş türü geçersiz olmalıdır
baskı (' İç işleyici işlevi ');
}

intana(){
sinyal(SIGINT,sig_handler); // Sinyal işleyiciyi kaydet
için(intben=1;;ben++){ //Sonsuz döngü
baskı ('%d : Ana işlevin içinde ',ben);
uyku(1); // 1 saniye gecikme
}
dönüş 0;
}

Example1.c çıktısının ekran görüntüsünde, ana fonksiyonda sonsuz döngünün yürütüldüğünü görebiliriz. Kullanıcı Ctrl+C yazdığında, ana işlevin yürütülmesi durur ve sinyalin işleyici işlevi çağrılır. İşleyici işlevi tamamlandıktan sonra ana işlevin yürütülmesine devam edildi. Kullanıcı Ctrl+ yazdığında işlem sonlandırılır.

Ignore Signals Örneği

#Dahil etmek
#Dahil etmek
#Dahil etmek
intana(){
sinyal(SIGINT,SIG_IGN); // Sinyali yok saymak için sinyal işleyiciyi kaydedin

için(intben=1;;ben++){ //Sonsuz döngü
baskı ('%d : Ana işlevin içinde ',ben);
uyku(1); // 1 saniye gecikme
}
dönüş 0;
}

Burada işleyici işlevi kayıtlıdır SIG_IGN () sinyal eylemini yok sayma işlevi. Böylece, kullanıcı Ctrl+C yazdığında, SIGINT sinyal üretiliyor ancak eylem yok sayılıyor.

Yeniden Kaydetme Sinyal İşleyici Örneği

#Dahil etmek
#Dahil etmek
#Dahil etmek

geçersizsig_handler(intimza){
baskı (' İç işleyici işlevi ');
sinyal(SIGINT,SIG_DFL); // Varsayılan eylem için sinyal işleyiciyi yeniden kaydet
}

intana(){
sinyal(SIGINT,sig_handler); // Sinyal işleyiciyi kaydet
için(intben=1;;ben++){ //Sonsuz döngü
baskı ('%d : Ana işlevin içinde ',ben);
uyku(1); // 1 saniye gecikme
}
dönüş 0;
}

Example3.c çıktısının ekran görüntüsünde, kullanıcı ilk kez Ctrl+C yazdığında, işleyici işlevinin çağrıldığını görebiliriz. İşleyici işlevinde, sinyal işleyici yeniden SIG_DFL sinyalin varsayılan eylemi için. Kullanıcı ikinci kez Ctrl+C yazdığında, varsayılan eylem olan işlem sonlandırılır. SIGINT sinyal.

Sinyal Gönderme:

Bir proses ayrıca kendisine veya başka bir prosese açıkça sinyaller gönderebilir. yükseltme() ve kill() işlevleri sinyal göndermek için kullanılabilir. Her iki işlev de signal.h başlık dosyasında bildirilir.

int artırmak (intimza)

Sinyal göndermek için kullanılan yükseltme () işlevi imza çağırma işlemine (kendisine). Başarılı olursa sıfır, başarısız olursa sıfır olmayan bir değer döndürür.

intöldürmek(pid_t pid, intimza)

Bir sinyal göndermek için kullanılan öldürme işlevi imza tarafından belirtilen bir süreç veya süreç grubuna pid .

SIGUSR1 Sinyal İşleyici Örneği

#Dahil etmek
#Dahil etmek

geçersizsig_handler(intimza){
baskı ('İç işleyici işlevi ');
}

intana(){
sinyal(SIGUSR1,sig_handler); // Sinyal işleyiciyi kaydet
baskı ('Ana işlevin içinde ');
artırmak (SIGUSR1);
baskı ('Ana işlevin içinde ');
dönüş 0;
}

Burada proses, SIGUSR1 sinyalini upgrade() fonksiyonunu kullanarak kendisine gönderir.

Kill Örnek Programı ile Yükselt

#Dahil etmek
#Dahil etmek
#Dahil etmek
geçersizsig_handler(intimza){
baskı ('İç işleyici işlevi ');
}

intana(){
pid_t pid;
sinyal(SIGUSR1,sig_handler); // Sinyal işleyiciyi kaydet
baskı ('Ana işlevin içinde ');
pid=getpid(); //Proses kimliği
öldürmek(pid,SIGUSR1); // SIGUSR1'i kendisine gönder
baskı ('Ana işlevin içinde ');
dönüş 0;
}

Burada, işlem gönderme SIGUSR1 kullanarak kendine sinyal öldürmek() işlev. getpid() kendisinin işlem kimliğini almak için kullanılır.

Bir sonraki örnekte, ebeveyn ve alt süreçlerin nasıl iletişim kurduğunu (Süreçler Arası İletişim) göreceğiz. öldürmek() ve sinyal fonksiyonu.

Sinyallerle Ebeveyn Çocuk İletişimi

#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
geçersizsig_handler_parent(intimza){
baskı ('Ebeveyn: Çocuktan bir yanıt sinyali aldı ');
}

geçersizsig_handler_child(intimza){
baskı ('Çocuk : Ebeveynden bir sinyal aldı ');
uyku(1);
öldürmek(getppid(),SIGUSR1);
}

intana(){
pid_t pid;
Eğer((pid=çatal())<0){
baskı ('Çatal Başarısız ');
çıkış (1);
}
/* Alt Süreç */
Başka Eğer(pid==0){
sinyal(SIGUSR1,sig_handler_child); // Sinyal işleyiciyi kaydet
baskı ('Çocuk: sinyal bekliyor ');
Duraklat();
}
/* Üst Süreç */
Başka{
sinyal(SIGUSR1,sig_handler_parent); // Sinyal işleyiciyi kaydet
uyku(1);
baskı ('Ebeveyn: Çocuğa sinyal gönderme ');
öldürmek(pid,SIGUSR1);
baskı ('Ebeveyn: yanıt bekleniyor ');
Duraklat();
}
dönüş 0;
}

Buraya, çatal() işlev alt süreç oluşturur ve alt sürece sıfır ve üst sürece alt süreç kimliğini döndürür. Bu nedenle, ebeveyn ve çocuk sürecine karar vermek için pid kontrol edildi. Ebeveyn işleminde, alt işlemin sinyal işleyici işlevini kaydedebilmesi ve ebeveynden gelen sinyali bekleyebilmesi için 1 saniye uyur. 1 saniye sonra ebeveyn işlemi gönder SIGUSR1 çocuk sürecine sinyal verin ve çocuktan gelen yanıt sinyalini bekleyin. Çocuk süreçte, önce ebeveynden sinyal beklenir ve sinyal alındığında işleyici işlevi çağrılır. İşleyici işlevinden, alt süreç başka bir işlem gönderir. SIGUSR1 ebeveyne sinyal. Buraya getppid() işlevi, üst işlem kimliğini almak için kullanılır.

Çözüm

Linux'ta sinyal büyük bir konudur. Bu yazıda sinyalin en temelden nasıl ele alınacağını ve sinyalin nasıl üretildiğini, bir prosesin kendisine ve diğer proseslere nasıl sinyal gönderebileceğini, prosesler arası iletişim için sinyalin nasıl kullanılabileceğini öğrendik.