Zoeken…


Syntaxis

  • niet-ondertekend alarm (niet-ondertekende seconden);
  • int kill (pid_t pid, int sig);

parameters

Functie, parameter (s), retourwaarde Beschrijving
alarm() functienaam
unsigned seconds Seconden om een alarm te activeren of 0 om een wachtend alarm te annuleren
> = 0 0 als er geen ander alarm in behandeling was, anders was het aantal seconden dat het alarm nog in behandeling was. Deze functie zal niet falen.
- -
kill() functienaam
pid_t pid .
int sig 0 of signaal-ID
0, -1 Bij succes wordt 0 geretourneerd, -1 bij mislukking met errno op EINVAL , EPERM of ESRCH .

SIGALARM verhogen met de standaardactie

Met behulp van een alarm kan de gebruiker plannen dat het SIGALARM signaal na een gespecificeerd interval wordt verhoogd. In het geval dat de gebruiker de signaalhandler voor dit signaal niet heeft geblokkeerd, genegeerd of gespecificeerd, wordt de standaardactie voor dit signaal bij aankomst uitgevoerd. Per specificatie is de standaardactie voor SIGALARM om het proces te beëindigen:

#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;
}

Dit geeft uit:

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

Signaalhandler instellen met sigaction en signalen verhogen met raise

Om een programma te laten reageren op een bepaald signaal, anders dan het gebruik van standaardactie, kan aangepaste signaalhandler worden geïnstalleerd met behulp van sigaction . sigaction ontvangt drie argumenten - signaal om naar te handelen, aanwijzer naar sigaction_t structuur die, zo niet NULL , nieuw gedrag beschrijft en pointer naar sigaction_t die, zo niet NULL met het oude gedrag wordt gevuld (zodat iemand het kan herstellen). Het verhogen van signalen in hetzelfde proces kan worden gedaan met de raise methode. Als er meer controle nodig is (om het signaal naar een ander proces te sturen, kan kill of pthread_kill worden gebruikt, die het bestemmingsproces-ID of thread-ID accepteren).

#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;
}

Dit geeft uit:

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

Een proces waarbij zelfmoord wordt gepleegd met kill ()

Een proces kan (proberen) een signaal naar een ander proces te sturen met de functie kill() .

Om dit te doen, moet het verzendproces de PID van het ontvangende proces kennen. Omdat, zonder een race te introduceren, een proces alleen zeker kan zijn van zijn eigen PID (en de PID's van zijn kinderen), is het meest eenvoudige voorbeeld om het gebruik van kill() aan te tonen, dat een proces een signaal naar zichzelf stuurt.

Hieronder een voorbeeld van een proces dat zijn eigen beëindiging initieert door zichzelf een kill-signaal ( SIGKILL ) te sturen:

#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.");
}

Output:

Killed

(... of gelijk, afhankelijk van de implementatie)

SIGPIPE gegenereerd door write () verwerken op een threadveilige manier

Wanneer write() wordt genoemd voor een pijp met een naam of een naamloze pijp of stream waarvan het einde van de lezing is gesloten, gebeuren er twee dingen:

POSIX.1-2001
  1. SIGPIPE signaal wordt verzonden naar het proces dat write()
POSIX.1-2004
  1. SIGPIPE signaal wordt verzonden naar de thread die write()
  1. EPIPE fout wordt geretourneerd door write()

Er zijn verschillende manieren om met SIGPIPE om te gaan:

  • Voor sockets kan SIGPIPE worden uitgeschakeld door platformspecifieke opties in te stellen, zoals MSG_NOSIGNAL in Linux en SO_NOSIGPIPE in BSD (werkt alleen voor send , maar niet voor write ). Dit is niet draagbaar.
  • Voor FIFO's (genaamd pipes) wordt SIGPIPE niet gegenereerd als writer O_RDWR gebruikt O_RDWR plaats van O_WRONLY , zodat het einde van de lezing altijd wordt geopend. Dit schakelt EPIPE ook uit.
  • We kunnen SIGPIPE negeren of een globale handler instellen. Dit is een goede oplossing, maar het is niet acceptabel als u niet de hele toepassing beheert (u schrijft bijvoorbeeld een bibliotheek).
  • Met recente POSIX-versies kunnen we het feit gebruiken dat SIGPIPE wordt verzonden naar de thread die write() en dit afhandelen met behulp van synchrone signaalverwerkingstechnieken.

De onderstaande code toont de SIGPIPE afhandeling voor POSIX.1-2004 en hoger.

Het is geïnspireerd op dit bericht :

  • Voeg eerst SIGPIPE aan het masker van de huidige thread met behulp van pthread_sigmask() .
  • Controleer of er reeds aanhangig SIGPIPE gebruik sigpending() .
  • Roep write() . Als het einde van het lezen is gesloten, wordt SIGPIPE toegevoegd aan het wachtende EPIPE en wordt EPIPE geretourneerd.
  • Als write() EPIPE heeft geretourneerd en SIGPIPE nog niet in behandeling was vóór write() , verwijder dit dan uit het wachtende sigtimedwait() met sigtimedwait() .
  • Herstel het originele signaalmasker met pthread_sigmask() .

Broncode:

#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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow