Recherche…


Syntaxe

  • alarme non signée (secondes non signées);
  • int kill (pid_t pid, int sig);

Paramètres

Fonction, paramètre (s), valeur de retour La description
alarm() nom de la fonction
unsigned seconds Secondes pour déclencher une alarme ou 0 pour annuler toute alarme en attente
> = 0 0 si aucune autre alarme n'était en attente, sinon le nombre de secondes pendant lesquelles l'alarme en attente était encore ouverte. Cette fonction n'échouera pas.
- -
kill() nom de la fonction
pid_t pid .
int sig 0 ou ID du signal
0, -1 En cas de succès, 0 est renvoyé, -1 en cas d'échec avec la définition d' errno sur EINVAL , EPERM ou ESRCH .

Relever SIGALARM avec l'action par défaut

En utilisant l' alarm , l'utilisateur peut programmer le signal SIGALARM pour qu'il soit levé après l'intervalle spécifié. Si l'utilisateur n'a pas bloqué, ignoré ou spécifié un gestionnaire de signal explicite pour ce signal, l'action par défaut pour ce signal sera effectuée à l'arrivée. L'action par défaut par spécification pour SIGALARM consiste à terminer le processus:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char** argv)
{
    printf("Hello!\n");

    // Set the alarm for five second
    alarm(5); // Per POSIX, this cannot fail

    // Now sleep for 15 seconds
    for (int i = 1; i <= 15; i++)
    {
        printf("%d\n", i);
        sleep(1);
    }

    // And print the message before successful exit
    printf("Goodbye!\n");

    return EXIT_SUCCESS;
}

Cela produit:

Hello!
1
2
3
4
5
[2]    35086 alarm      ./a.out

Réglage du gestionnaire de signaux à l'aide de sigaction et de signaux de relance à l'aide de raise

Pour qu'un programme réagisse à un certain signal, autre qu'une action par défaut, un gestionnaire de signal personnalisé peut être installé à l'aide de sigaction . sigaction reçoit trois arguments - signal sur lequel agir, pointeur sur la structure sigaction_t qui, sinon NULL , décrit le nouveau comportement et le pointeur sur sigaction_t qui, si NULL ne sera pas rempli avec l'ancien comportement (on peut donc le restaurer). Augmenter les signaux dans le même processus peut être fait avec la méthode raise . Si plus de contrôle est nécessaire (pour envoyer le signal à un autre processus, kill ou pthread_kill peut être utilisé, qui accepte l'identifiant du processus de destination ou l'identifiant du thread).

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Signals are numbered from 1, signal 0 doesn't exist
volatile sig_atomic_t last_received_signal = 0;

// Signal handler, will set the global variable
// to indicate what is the last signal received.
// There should be as less work as possible inside
// signal handler routine, and one must take care only
// to call reentrant functions (in case of signal arriving
// while program is already executing same function)
void signal_catcher(int signo, siginfo_t *info, void *context)
{
    last_received_signal = info->si_signo;
}

int main (int argc, char** argv)
{
    // Setup a signal handler for SIGUSR1 and SIGUSR2
    struct sigaction act;
    memset(&act, 0, sizeof act);

    // sigact structure holding old configuration
    // (will be filled by sigaction):
    struct sigaction old_1;
    memset(&old_1, 0, sizeof old_1);
    struct sigaction old_2;
    memset(&old_2, 0, sizeof old_2);

    act.sa_sigaction = signal_catcher;
    // When passing sa_sigaction, SA_SIGINFO flag
    // must be specified. Otherwise, function pointed
    // by act.sa_handler will be invoked
    act.sa_flags = SA_SIGINFO;

    if (0 != sigaction(SIGUSR1, &act, &old_1))
    {
        perror("sigaction () failed installing SIGUSR1 handler");
        return EXIT_FAILURE;
    }

    if (0 != sigaction(SIGUSR2, &act, &old_2))
    {
        perror("sigaction() failed installing SIGUSR2 handler");
        return EXIT_FAILURE;
    }

    // Main body of "work" during which two signals
    // will be raised, after 5 and 10 seconds, and which
    // will print last received signal
    for (int i = 1; i <= 15; i++)
    {
        if (i == 5)
        {
            if (0 != raise(SIGUSR1))
            {
                perror("Can't raise SIGUSR1");
                return EXIT_FAILURE;
            }
        }

        if (i == 10)
        {
            if (0 != raise(SIGUSR2))
            {
                perror("Can't raise SIGUSR2");
                return EXIT_FAILURE;
            }
        }

        printf("Tick #%d, last caught signal: %d\n",
            i, last_received_signal);

        sleep(1);
    }

    // Restore old signal handlers
    if (0 != sigaction(SIGUSR1, &old_1, NULL))
    {
        perror("sigaction() failed restoring SIGUSR1 handler");
        return EXIT_FAILURE;
    }

    if (0 != sigaction(SIGUSR2, &old_2, NULL))
    {
        perror("sigaction() failed restoring SIGUSR2 handler");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Cela produit:

Tick #1, last caught signal: 0
Tick #2, last caught signal: 0
Tick #3, last caught signal: 0
Tick #4, last caught signal: 0
Tick #5, last caught signal: 30
Tick #6, last caught signal: 30
Tick #7, last caught signal: 30
Tick #8, last caught signal: 30
Tick #9, last caught signal: 30
Tick #10, last caught signal: 31
Tick #11, last caught signal: 31
Tick #12, last caught signal: 31
Tick #13, last caught signal: 31
Tick #14, last caught signal: 31
Tick #15, last caught signal: 31

Un processus qui se suicide en utilisant kill ()

Un processus peut (essayer de) envoyer un signal à tout autre processus en utilisant la fonction kill() .

Pour ce faire, le processus d'envoi doit connaître le PID du processus de réception. Comme, sans introduire de race, un processus ne peut être sûr que de son propre PID (et des PID de ses enfants), l'exemple le plus simple pour démontrer l'utilisation de kill() consiste à envoyer un signal à lui-même.

Ci-dessous, un exemple de processus initiant sa propre terminaison en s'envoyant un kill-signal ( SIGKILL ):

#define _POSIX_C_SOURCE 1

#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>


int main (void)
{
  pid_t pid = getpid(); /* Get my iown process ID. */

  kill(pid, SIGKILL); /* Send myself a KILL signal. */

  puts("Signal delivery initiated.");  /* Although not guaranteed, 
                                  practically the program never gets here. */

  pause(); /* Wait to die. */

  puts("This never gets printed.");
}

Sortie:

Killed

(... ou similaire, selon l'implémentation)

Gérer SIGPIPE généré par write () d'une manière thread-safe

Lorsque write() est appelé pour un socket ou un socket de flux nommé ou non nommé dont la fin de lecture est fermée, deux choses se produisent:

POSIX.1-2001
  1. SIGPIPE signal SIGPIPE est envoyé au processus appelé write()
POSIX.1-2004
  1. SIGPIPE signal SIGPIPE est envoyé au thread qui a appelé write()
  1. EPIPE erreur EPIPE est renvoyée par write()

Il y a plusieurs façons de traiter avec SIGPIPE :

  • Pour les sockets, SIGPIPE peut être désactivé en définissant des options spécifiques à la plate-forme telles que MSG_NOSIGNAL sous Linux et SO_NOSIGPIPE dans BSD (fonctionne uniquement pour l' send , mais pas pour l' write ). Ce n'est pas portable.
  • Pour les FIFO (canaux nommés), SIGPIPE ne sera pas généré si writer utilise O_RDWR au lieu de O_WRONLY , afin que la fin de la lecture soit toujours ouverte. Cependant, cela désactive EPIPE aussi.
  • Nous pouvons ignorer SIGPIPE ou définir le gestionnaire global. C'est une bonne solution, mais ce n'est pas acceptable si vous ne contrôlez pas toute l'application (par exemple, vous écrivez une bibliothèque).
  • Avec les versions récentes de POSIX, nous pouvons utiliser le fait que SIGPIPE est envoyé au thread qui a appelé write() et le gère en utilisant la technique de traitement du signal synchrone.

Le code ci-dessous illustre la gestion SIGPIPE sans risque pour POSIX.1-2004 et les versions ultérieures.

C'est inspiré par ce post :

  • Tout d'abord, ajoutez SIGPIPE au signal de masque du thread en cours en utilisant pthread_sigmask() .
  • Vérifiez si SIGPIPE est déjà en attente à l'aide de sigpending() .
  • Appelez write() . Si la fin de la lecture est fermée, SIGPIPE sera ajouté au masque de signaux en attente et EPIPE sera renvoyé.
  • Si write() renvoyé EPIPE et que SIGPIPE n'était pas déjà en attente avant write() , supprimez-le du masque de signaux en attente à l'aide de sigtimedwait() .
  • Restaurer le masque de signal original en utilisant pthread_sigmask() .

Code source:

#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <sys/signal.h>

ssize_t safe_write(int fd, const void* buf, size_t bufsz)
{
    sigset_t sig_block, sig_restore, sig_pending;

    sigemptyset(&sig_block);
    sigaddset(&sig_block, SIGPIPE);

    /* Block SIGPIPE for this thread.
     *
     * This works since kernel sends SIGPIPE to the thread that called write(),
     * not to the whole process.
     */
    if (pthread_sigmask(SIG_BLOCK, &sig_block, &sig_restore) != 0) {
        return -1;
    }

    /* Check if SIGPIPE is already pending.
     */
    int sigpipe_pending = -1;
    if (sigpending(&sig_pending) != -1) {
        sigpipe_pending = sigismember(&sig_pending, SIGPIPE);
    }

    if (sigpipe_pending == -1) {
        pthread_sigmask(SIG_SETMASK, &sig_restore, NULL);
        return -1;
    }

    ssize_t ret;
    while ((ret = write(fd, buf, bufsz)) == -1) {
        if (errno != EINTR)
            break;
    }

    /* Fetch generated SIGPIPE if write() failed with EPIPE.
     *
     * However, if SIGPIPE was already pending before calling write(), it was
     * also generated and blocked by caller, and caller may expect that it can
     * fetch it later. Since signals are not queued, we don't fetch it in this
     * case.
     */
    if (ret == -1 && errno == EPIPE && sigpipe_pending == 0) {
        struct timespec ts;
        ts.tv_sec = 0;
        ts.tv_nsec = 0;

        int sig;
        while ((sig = sigtimedwait(&sig_block, 0, &ts)) == -1) {
            if (errno != EINTR)
                break;
        }
    }

    pthread_sigmask(SIG_SETMASK, &sig_restore, NULL);
    return ret;
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow