Sök…


Syntax

  • osignerat larm (osignerade sekunder);
  • int kill (pid_t pid, int sig);

parametrar

Funktion, parameter (er), returvärde Beskrivning
alarm() funktionsnamn
unsigned seconds Sekunder för att ta upp ett larm eller 0 för att avbryta alla väntande larm
> = 0 0 om inget annat larm väntar, annars antalet sekunder som det väntande larmet fortfarande hade öppnat. Denna funktion misslyckas inte.
- -
kill() funktionsnamn
pid_t pid .
int sig 0 eller signal-ID
0, -1 Vid framgång returneras 0, -1 vid fel med inställning av errno till EINVAL , EPERM eller ESRCH .

Att höja SIGALARM med standardåtgärden

Med alarm kan användaren schemalägga SIGALARM signal som ska höjas efter det angivna intervallet. Om användaren inte blockerade, ignorerade eller specificerade uttrycklig signalhanterare för denna signal, kommer standardåtgärden för denna signal att utföras vid ankomst. Per specifikation standardåtgärd för SIGALARM är att avsluta processen:

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

Denna utgångar:

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

Ställa in signalhanterare med hjälp av signering och höja signaler med höjning

För att ett program ska reagera på en viss signal, annat än att använda standardåtgärder, kan anpassad signalhanterare installeras med sigaction . sigaction får tre argument - signal att agera på, pekare till sigaction_t struktur som, om inte NULL , beskriver nytt beteende och pekare till sigaction_t som, om inte NULL kommer att fyllas med det gamla beteendet (så man kan återställa det). Att höja signaler i samma process kan göras med raise . Om mer kontroll behövs (för att skicka signalen till någon annan process kan kill eller pthread_kill användas, som accepterar destinationsprocess-id eller tråd-id).

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

Denna utgångar:

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

En process som begår självmord med kill ()

En process kan (försöka) skicka en signal till någon annan process med funktionen kill() .

För att göra det måste sändningsprocessen kända mottagningsprocessen PID. Eftersom, utan att införa ett lopp, kan en process bara vara säker på sin egen PID (och dess barns PID), det enklaste exemplet för att demonstrera användningen av kill() är att låta en process skicka en signal till sig själv.

Nedanför ett exempel på en process som inleder sin egen avslutning genom att skicka sig själv en 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.");
}

Produktion:

Killed

(... eller lika, beroende på implementeringen)

Hantera SIGPIPE som genererats genom att skriva () på ett tråd-säkert sätt

När write() krävs för ett namngivet eller icke namngivet rör eller strömuttag vars läsänd är stängd, händer två saker:

POSIX.1-2001
  1. SIGPIPE signal skickas till processen som kallas write()
POSIX.1-2004
  1. SIGPIPE signal skickas till tråden som kallas write()
  1. EPIPE felet returneras av write()

Det finns flera sätt att hantera SIGPIPE :

  • För uttag kan SIGPIPE inaktiveras genom att ställa in plattformspecifika alternativ som MSG_NOSIGNAL i Linux och SO_NOSIGPIPE i BSD (fungerar endast för send , men inte för write ). Detta är inte bärbart.
  • För FIFO: er (namngivna rör) kommer SIGPIPE inte att genereras om författaren använder O_RDWR istället för O_WRONLY , så att O_WRONLY alltid öppnas. Men detta inaktiverar EPIPE också.
  • Vi kan ignorera SIGPIPE eller ställa in global hanterare. Detta är en bra lösning, men det är inte acceptabelt om du inte kontrollerar hela applikationen (t.ex. skriver du ett bibliotek).
  • Med de senaste POSIX-versionerna kan vi använda det faktum att SIGPIPE skickas till tråden som kallas write() och hanterar den med hjälp av synkron signalhanteringsteknik.

Koden nedan visar SIGPIPE hantering för POSIX.1-2004 och senare.

Det är inspirerat av det här inlägget :

  • Lägg först SIGPIPE till signalmask för aktuell tråd med pthread_sigmask() .
  • Kontrollera om SIGPIPE redan finns i väntande med sigpending() .
  • Ring write() . Om lässlutet är stängt läggs SIGPIPE till pågående EPIPE och EPIPE returneras.
  • Om write() returnerade EPIPE , och SIGPIPE inte redan väntade innan write() , ta bort den från väntande sigtimedwait() med sigtimedwait() .
  • Återställ original signalmask med pthread_sigmask() .

Källkod:

#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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow