Sigaction ve signal arasındaki fark nedir?


143

Burada sahip olduğumuz bir uygulamaya ekstra bir sinyal işleyici eklemek üzereydim ve yazarın sigaction()diğer sinyal işleyicileri kurmak için kullandığını fark ettim . Kullanacaktım signal(). Sözleşmeyi takip etmek için kullanmalıyım sigaction()ama sıfırdan yazıyor olsaydım hangisini seçmeliyim?

Yanıtlar:


167

Kullan sigaction()bunu yapmak için değil çok zorlayıcı sebepler var sürece.

signal()Arabirim, lehine antik (ve dolayısıyla kullanılabilirlik) sahiptir ve C standardında tanımlanmıştır. Bununla birlikte, eski davranışı sadakatle simüle etmesine izin vermek için sigaction()açıkça eklenen bayrakları kullanmazsanız, kaçınan bir dizi istenmeyen özelliğe sahiptir .sigaction()signal()

  1. signal()Fonksiyonu (zorunlu olarak) mevcut işleyici yürütülürken gelen diğer sinyaller bloke etmez; sigaction()geçerli işleyici geri dönünceye kadar diğer sinyalleri engelleyebilir.
  2. signal()Fonksiyonu (genellikle) sinyal eylem geri sıfırlar SIG_DFLneredeyse tüm sinyaller için (varsayılan). Bu, signal()işleyicinin ilk eylemi olarak kendini yeniden kurması gerektiği anlamına gelir . Ayrıca, sinyalin algılanması ile işleyicinin yeniden yüklenmesi arasında, bir sinyalin ikinci bir örneği gelirse, varsayılan davranış (genellikle sonlandırma, bazen önyargı ile - yani çekirdek dökümü) meydana gelen bir güvenlik açığı penceresi açar.
  3. signal()Sistemlerin tam davranışları sisteme göre değişir - standartlar bu değişikliklere izin verir.

Bunlar sigaction()yerine kullanmak için genellikle iyi nedenlerdir signal(). Ancak, arayüzü sigaction()inkar edilemez derecede daha zordur.

Kullandığınız iki hangisi gibi alternatif sinyal arayüzleri tarafından cazip değil sighold(), sigignore(), sigpause()ve sigrelse(). Nominal olarak alternatiflerdir sigaction(), ancak sadece standart değildirler ve ciddi kullanımdan ziyade geriye dönük uyumluluk için POSIX'te bulunurlar. POSIX standardının çok iş parçacıklı programlardaki davranışlarının tanımsız olduğunu söylediğini unutmayın.

Çok iş parçacıklı programlar ve sinyaller bir diğer karmaşık hikayedir. AFAIK, hem signal()ve sigaction()çok kanallı uygulamalarda Tamam bulunmaktadır.

Cornstalks gözlemler :

Linux man sayfası signal()şöyle diyor:

  Etkileri signal()bir çok evreli işlemde tanımlanmamış bulunmaktadır.

Böylece, sigaction()çok iş parçacıklı bir süreçte güvenle kullanılabilecek tek şey bence .

İlginç. Bu durumda Linux kılavuz sayfası POSIX'ten daha kısıtlayıcıdır. POSIX signal()şunları belirtir :

İşlem çok iş parçacıklıysa veya işlem tek iş parçacıklıysa ve aşağıdakilerin dışında bir sinyal işleyici yürütülürse:

  • İşlem arama abort(), raise(), kill(), pthread_kill(), ya da sigqueue()engellenmeyen bir sinyal üretmek için
  • Beklemede olan bir engelleme kaldırılıyor ve engellemeyi kaldırmadan çağrıdan önce gönderiliyor

sinyal işleyici, olarak errnobildirilen bir nesneye değer atamak dışında statik depolama süresi dışında herhangi bir nesneye başvuruyorsa volatile sig_atomic_tveya sinyal işleyicisi, bu standartta tanımlanan işlevlerden biri dışında başka bir işlevi çağırıyorsa , davranış tanımsızdır . Sinyal Kavramları .

POSIX, signal()çok iş parçacıklı bir uygulamadaki davranışını açıkça belirtir .

Bununla birlikte, sigaction()esasen her durumda tercih edilmelidir - ve taşınabilir çok iş parçacıklı kod, bunu yapamamanın çok sigaction()büyük bir nedeni olmadığı sürece kullanılmalıdır ("yalnızca Standart C tarafından tanımlanan işlevleri kullanın" gibi - ve evet, C11 kodu çok olabilir -threaded). Temel olarak bu cevabın açılış paragrafı da bunu söylüyor.


12
Bu tanım signalaslında Unix System V davranışına aittir. POSIX, bu davranışa veya çok daha aklı başında BSD davranışına izin verir, ancak hangisini alacağınızdan emin olamayacağınız için, yine de kullanmak en iyisidir sigaction.
R .. GitHub BUZA YARDIMCI DURDUR

1
eski sinyal () davranışını sadakatle simüle etmesine izin vermek için sigaction () öğesine açıkça eklenen bayrakları kullanmazsanız. Bunlar hangi bayraklar (özellikle) olurdu?
ChristianCuevas

@AlexFritz: Öncelikle SA_RESETHAND, aynı zamanda SA_NODEFER.
Jonathan Leffler

2
@BulatM. Kullanamıyorsanız sigaction(), esas olarak için Standart C spesifikasyonunu kullanmak zorundasınız signal(). Ancak, bu, yapabileceğiniz şeyler için son derece fakirleştirilmiş bir seçenekler kümesi sunar. Şunları yapabilirsiniz: türdeki değişkenleri (dosya kapsamı) değiştirebilir volatile sig_atomic_t; 'hızlı çıkış' işlevlerinden birini çağırın ( _Exit(), quick_exit()) veya abort(); signal()sinyal bağımsız değişkeni olarak geçerli sinyal numarası ile çağrı ; dönüş. Ve bu kadar. Başka hiçbir şeyin taşınabilir olduğu garanti edilmez. Bu o kadar katı ki, çoğu insan bu kuralları görmezden geliyor - ancak ortaya çıkan kod tehlikeli.
Jonathan Leffler

1
sigaction()GCC'nin kendilerinden mükemmel demo: gnu.org/software/libc/manual/html_node/… ; ve GCC'den mükemmel signal()demo: gnu.org/software/libc/manual/html_node/… . signalDemoda SIG_IGN, daha önce kasıtlı olarak ayarlanmışsa işleyicinin ignore ( ) işlevini değiştirmekten kaçındıklarına dikkat edin .
Gabriel Staples


5

İşletim sisteminin sinyal tesisleri için farklı arayüzlerdir. Sinyal () uygulama tanımlı (genellikle yarış eğilimli) davranışa sahip olduğundan ve Windows, OS X, Linux ve diğer UNIX sistemlerinde farklı davranış gösterdiğinden, mümkünse sinyal vermek için sigaction kullanmayı tercih etmelisiniz.

Ayrıntılar için bu güvenlik notuna bakın.


1
Ben sadece glibc kaynak kodu baktım ve sinyal () sadece sigaction () çağırır. Ayrıca yukarıda MacOS kılavuz sayfasının aynı iddiada bulunduğu bölüme bakın.
bmdhacks

bunu bilmek güzel. Ben sadece sinyal işleyicileri çıkmadan önce düzgünce şeyleri kapatmak için kullanılan gördüm, bu yüzden genellikle işleyiciyi yeniden yükleme ile ilgili davranış güveniyor olmazdı.
Matthew Smith

5

signal () standart C, sigaction () değildir.

İkisinden birini kullanabiliyorsanız (yani, bir POSIX sistemindesiniz), sigaction (); signal () öğesinin işleyiciyi sıfırlayıp sıfırlamadığı belirtilmez, yani taşınabilir olması için işleyicinin içinde tekrar sinyal () çağırmanız gerekir. Daha da kötüsü, bir yarış olması: hızlı bir şekilde art arda iki sinyal alırsanız ve ikincisi, işleyiciyi yeniden yüklemeden önce teslim edilirse, muhtemelen işleminizi öldürecek olan varsayılan eyleme sahip olursunuz. sigaction () ise “güvenilir” sinyal semantiği kullanacağı garantilidir. İşleyiciyi yeniden takmanız gerekmez, çünkü asla sıfırlanmaz. SA_RESTART ile, otomatik olarak yeniden başlaması için bazı sistem çağrıları da alabilirsiniz (böylece EINTR'yi manuel olarak kontrol etmeniz gerekmez). sigaction () daha fazla seçeneğe sahiptir ve güvenilirdir, bu nedenle kullanımı teşvik edilir.

Psst ... kimseye bunu söylediğimi söyleme, ancak POSIX'in şu anda sinyal () gibi davranan ancak BSD anlambilimi veren bsd_signal () işlevi var, bu da güvenilir olduğu anlamına geliyor. Ana kullanımı güvenilir sinyaller alan eski uygulamaları taşımaktır ve POSIX bunu kullanmanızı önermez.


POSIX'in bir işlevi yoktur bsd_signal()- bazı POSIX uygulamalarının işlevi olabilir, ancak POSIX'in kendisi böyle bir işlevi içermez (bkz. POSIX ).
Jonathan Leffler

4

Kısacası:

sigaction()iyi ve iyi tanımlanmış, ancak bir Linux işlevidir ve bu nedenle yalnızca Linux'ta çalışır. signal()Kötü ve kötü tanımlanmış, ancak C standart bir işlevdir ve bu nedenle her şey üzerinde çalışır.

Linux kılavuz sayfaları bu konuda ne söylüyor?

man 2 signal( buradan çevrimiçi görün ):

Signal () davranışı UNIX sürümleri arasında değişiklik gösterir ve aynı zamanda tarihsel olarak farklı Linux sürümleri arasında da değişiklik gösterir. Kullanımından kaçının: sigaction(2)bunun yerine kullanın. Aşağıdaki Taşınabilirlik konusuna bakın.

Taşınabilirlik Signal () 'nın tek taşınabilir kullanımı, bir sinyalin düzenini SIG_DFL veya SIG_IGN olarak ayarlamaktır. Bir sinyal işleyici oluşturmak için signal () kullanılırken semantik sistemler arasında değişiklik gösterir (ve POSIX.1 bu varyasyona açıkça izin verir); bu amaçla kullanmayın.

Başka bir deyişle: kullanma signal(). sigaction()Bunun yerine kullanın !

GCC ne düşünüyor?

Uyumluluk Not: Yukarıda belirtildiği gibi signal, bu işlevden mümkün olduğunca kaçınılmalıdır. sigactiontercih edilen yöntemdir.

Kaynak: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

Peki, hem Linux hem de GCC signal()kullanmamayı sigaction()değil, kullanmayı tercih ederse, bu şu soruyu akla getiriyor: Bu kafa karıştırıcı sigaction()şeyi nasıl kullanıyoruz !?

Kullanım örnekleri:

GCC'nin MÜKEMMEL signal()örneğini buradan okuyun : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

Ve sigaction()buradaki MÜKEMMEL örneği: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

Bu sayfaları okuduktan sonra aşağıdaki tekniği buldum sigaction():

1. sigaction(), yukarıda açıklandığı gibi bir sinyal işleyici takmanın doğru yolu olduğundan:

#include <errno.h>  // errno
#include <signal.h> // sigaction()
#include <stdio.h>  // printf()
#include <string.h> // strerror()

#define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char *
#define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "

/// @brief      Callback function to handle termination signals, such as Ctrl + C
/// @param[in]  signal  Signal number of the signal being handled by this callback function
/// @return     None
static void termination_handler(const int signal)
{
    switch (signal)
    {
    case SIGINT:
        printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
        break;
    case SIGTERM:
        printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", signal);
        break;
    case SIGHUP:
        printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
        break;
    default:
        printf("\nUnk signal (%i) caught.\n", signal);
        break;
    }

    // DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.


    exit(signal);
}

/// @brief      Set a new signal handler action for a given signal
/// @details    Only update the signals with our custom handler if they are NOT set to "signal ignore" (`SIG_IGN`),
///             which means they are currently intentionally ignored. GCC recommends this "because non-job-control
///             shells often ignore certain signals when starting children, and it is important for children
///             to respect this." See
///             https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
///             and https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
///             Note that termination signals can be found here:
///             https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
/// @param[in]  signal  Signal to set to this action
/// @param[in]  action  Pointer to sigaction struct, including the callback function inside it, to attach to this signal
/// @return     None
static inline void set_sigaction(int signal, const struct sigaction *action)
{
    struct sigaction old_action;

    // check current signal handler action to see if it's set to SIGNAL IGNORE
    sigaction(signal, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN)
    {
        // set new signal handler action to what we want
        int ret_code = sigaction(signal, action, NULL);
        if (ret_code == -1)
        {
            printf(LOG_FORMAT_STR "sigaction failed when setting signal to %i;\n"
                   "  errno = %i: %s\n", LOG_LOCATION, signal, errno, strerror(errno));
        }
    }
}

int main(int argc, char *argv[])
{
    //...

    // Register callbacks to handle kill signals; prefer the Linux function `sigaction()` over the C function
    // `signal()`: "It is better to use sigaction if it is available since the results are much more reliable."
    // Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
    // and /programming/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
    // See here for official gcc `sigaction()` demo, which this code is modeled after:
    // https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

    // Set up the structure to specify the new action, per GCC's demo.
    struct sigaction new_action;
    new_action.sa_handler = termination_handler; // set callback function
    sigemptyset(&new_action.sa_mask);
    new_action.sa_flags = 0;

    // SIGINT: ie: Ctrl + C kill signal
    set_sigaction(SIGINT, &new_action);
    // SIGTERM: termination signal--the default generated by `kill` and `killall`
    set_sigaction(SIGTERM, &new_action);
    // SIGHUP: "hang-up" signal due to lost connection
    set_sigaction(SIGHUP, &new_action);

    //...
}

2. Ve signal(), yukarıda açıklandığı gibi, bir sinyal işleyici takmanın iyi bir yolu olmasa da, nasıl kullanılacağını bilmek hala iyidir.

İşte GCC gösteri kodu kopyalandı, çünkü alacağı kadar iyi:

#include <signal.h>

void
termination_handler (int signum)
{
  struct temp_file *p;

  for (p = temp_file_list; p; p = p->next)
    unlink (p->name);
}

int
main (void)
{
  
  if (signal (SIGINT, termination_handler) == SIG_IGN)
    signal (SIGINT, SIG_IGN);
  if (signal (SIGHUP, termination_handler) == SIG_IGN)
    signal (SIGHUP, SIG_IGN);
  if (signal (SIGTERM, termination_handler) == SIG_IGN)
    signal (SIGTERM, SIG_IGN);
  
}

Dikkat edilmesi gereken ana bağlantılar:

  1. Standart Sinyaller: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
    1. Sonlandırma Sinyalleri: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
  2. Resmi GCC signal()kullanım örneği dahil olmak üzere Temel Sinyal İşleme : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
  3. Resmi GCC sigaction()kullanım örneği: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
  4. Sinyal setleri, sigemptyset()ve dahil sigfillset(); Bunları hala tam olarak anlayamıyorum, ancak önemli olduklarını biliyorum: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html

Ayrıca bakınız:

  1. Nokta C ++ Sinyal İşleme [mükemmel demo koduyla]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
  2. https://www.tutorialspoint.com/c_standard_library/signal_h.htm

2

Gönderen signal(3)adam sayfası:

AÇIKLAMA

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

Her ikisi de aynı temel tesisi çağırıyor. Muhtemelen yanıtı tek bir sinyalin her ikisiyle de manipüle etmemelisiniz, ancak karıştırmak hiçbir şeyin bozulmasına neden olmamalıdır ...


Bu benim adam sayfamda değil! Tüm aldığım "AÇIKLAMA signal () sistem çağrısı, sayı işareti olan sinyal için yeni bir sinyal işleyici yükler." Yararlı kılavuz sayfaları paketine yükseltmem gerekiyor .
Matthew Smith

1
Bu Mac OS X 10.5 sayfalarının dışında.
dmckee --- eski moderatör kedi yavrusu

Ayrıca glibc kaynak kodundan da doğrulanmıştır. signal () sadece sigaction () çağırır
bmdhacks

2
Ancak bu, tüm sinyal uygulamalarında doğru değildir. "Sigaction" davranışını zorunlu kılmak istiyorsanız, bu varsayımı dikkate almayın.
Ben Burns

1

Ayrıca sinyal () üzerinden sigaction () kullanmanızı öneririm ve bir nokta daha eklemek istiyorum. sigaction () size ölmüş olan işlemin pid'i gibi daha fazla seçenek sunar (siginfo_t yapısını kullanarak mümkün).


0

En azından teoride daha taşınabilir olduğu için signal () kullanırdım. POSIX uyumluluk katmanı olmayan ve sinyali () destekleyen modern bir sistem bulabilecek yorumcuları oylayacağım.

GLIBC belgelerinden alıntı :

Hem sinyal hem de sigaction işlevlerini tek bir programda kullanmak mümkündür, ancak dikkatli olmalısınız çünkü biraz garip yollarla etkileşime girebilirler.

Sigaction işlevi sinyal işlevinden daha fazla bilgi belirtir, bu nedenle sinyalden dönüş değeri tüm sigaction olasılıklarını ifade edemez. Bu nedenle, bir eylemi kaydetmek ve daha sonra yeniden oluşturmak için sinyal kullanırsanız, bu, işaretleme ile kurulan bir işleyiciyi düzgün bir şekilde yeniden kuramayabilir.

Sonuç olarak sorun yaşamamak için, programınız hiç iletişim kullanıyorsa her zaman bir işleyiciyi kaydetmek ve geri yüklemek için sigaction kullanın. Sigaction daha genel olduğundan, orijinal olarak sinyal veya sigaction ile kurulmuş olup olmadığına bakılmaksızın herhangi bir eylemi düzgün bir şekilde kaydedebilir ve yeniden kurabilir.

Bazı sistemlerde, sinyal içeren bir eylem oluşturursanız ve ardından bu iletiyi inceleyerek incelerseniz, aldığınız işleyici adresi sinyalle belirttiğiniz adresle aynı olmayabilir. Hatta sinyalli bir eylem argümanı olarak kullanım için uygun olmayabilir. Ama bunu bir sansasyon argümanı olarak kullanmaya güvenebilirsiniz. Bu sorun GNU sisteminde asla olmaz.

Yani, mekanizmalardan birini veya diğerini tek bir programda tutarlı bir şekilde kullanmanız daha iyi olur.

Taşınabilirlik Not: Temel sinyal işlevi, ISO C'nin bir özelliğidir, sigaction POSIX.1 standardının bir parçasıdır. POSIX olmayan sistemlere taşınabilirlik konusunda endişeleriniz varsa, bunun yerine sinyal işlevini kullanmalısınız.

Telif Hakkı (C) 1996-2008 Özgür Yazılım Vakfı, Inc.

Bu dokümanı GNU Özgür Dokümantasyon Lisansı, Sürüm 1.2 veya Özgür Yazılım Vakfı tarafından yayınlanan herhangi bir sonraki sürüm uyarınca kopyalama, dağıtma ve / veya değiştirme izni verilir; Değişmez Bölümler olmadan, Ön Kapak Metinleri ve Arka Kapak Metinleri olmadan. Lisansın bir kopyası "GNU Özgür Belgeleme Lisansı" başlıklı bölümde yer almaktadır.


0

Kılavuz sayfa sinyalinden (7)

İşleme yönlendirilmiş bir sinyal, halihazırda sinyali bloke edilmemiş olan ipliklerden herhangi birine verilebilir. Birden fazla iş parçacığının sinyali engellenmemişse, çekirdek sinyali iletmek için isteğe bağlı bir iş parçacığı seçer.

Ve bu "sorunun" sinyal (2) ve sigaction (2) için var olduğunu söyleyebilirim . Bu yüzden sinyallere ve pthreads'ye dikkat edin.

... ve sinyal (2) Linux'un altında glibc ile sigaction (2) olarak görünüyor .

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.