Szukaj…


Składnia

  • niepodpisany alarm (niepodpisane sekundy);
  • int kill (pid_t pid, int sig);

Parametry

Funkcja, parametry, zwracana wartość Opis
alarm() nazwa funkcji
unsigned seconds Sekundy, aby podnieść alarm lub 0, aby anulować dowolny alarm w toku
> = 0 0, jeśli żaden inny alarm nie był w toku, w przeciwnym razie liczba sekund, przez które alarm w toku nadal był otwarty. Ta funkcja nie zawiedzie.
- -
kill() nazwa funkcji
pid_t pid .
int sig 0 lub identyfikator sygnału
0, -1 W przypadku powodzenia zwracane jest 0, -1 w przypadku niepowodzenia z ustawieniem errno na EINVAL , EPERM lub ESRCH .

Podnoszenie SIGALARM z domyślną akcją

Za pomocą alarm użytkownik może zaplanować SIGALARM sygnału SIGALARM po określonym czasie. W przypadku, gdy użytkownik nie zablokował, zignorował lub nie określił jawnej procedury obsługi sygnału dla tego sygnału, domyślne działanie dla tego sygnału zostanie wykonane po przybyciu. Zgodnie ze specyfikacją domyślną czynnością dla SIGALARM jest zakończenie procesu:

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

To daje:

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

Ustawianie obsługi sygnału za pomocą sigaction i podnoszenie sygnałów za pomocą raise

Aby program reagował na określony sygnał, inny niż domyślna akcja, można zainstalować niestandardową procedurę obsługi sygnału za pomocą sigaction . sigaction otrzymuje trzy argumenty - sygnał do działania, wskaźnik do struktury sigaction_t , która, jeśli nie NULL , opisuje nowe zachowanie i wskaźnik do sigaction_t który, jeśli nie NULL zostanie wypełniony starym zachowaniem (aby można je było przywrócić). Podnoszenie sygnałów w tym samym procesie można wykonać metodą raise . Jeśli potrzebna jest większa kontrola (aby wysłać sygnał do innego procesu, można użyć metody kill lub pthread_kill , która akceptuje docelowy identyfikator procesu lub identyfikator wątku).

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

To daje:

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

Proces popełniający samobójstwo za pomocą kill ()

Proces może (próbować) wysłać sygnał do dowolnego innego procesu za pomocą funkcji kill() .

Aby to zrobić, proces wysyłania musi znać PID procesu odbierającego. Ponieważ bez wprowadzania rasy proces może być jedynie pewien swojego PID (i PID jego dzieci), najprostszym przykładem do zademonstrowania użycia kill() jest wysłanie przez proces sygnału do siebie.

Poniżej przykład procesu inicjującego własne zakończenie przez wysłanie sobie sygnału „zabicia” ( 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.");
}

Wynik:

Killed

(... lub podobne, w zależności od wdrożenia)

Obsługa SIGPIPE generowanego przez write () w sposób bezpieczny dla wątków

Gdy wywoływana jest funkcja write() dla nazwanego lub nienazwanego gniazda potoku lub strumienia, którego koniec odczytu jest zamknięty, zdarzają się dwie rzeczy:

POSIX.1-2001
  1. Sygnał SIGPIPE jest wysyłany do procesu, który wywołał write()
POSIX.1-2004
  1. Sygnał SIGPIPE jest wysyłany do wątku, który wywołał write()
  1. Błąd EPIPE jest zwracany przez write()

Istnieje kilka sposobów radzenia sobie z SIGPIPE :

  • W przypadku gniazd SIGPIPE można wyłączyć, ustawiając opcje specyficzne dla platformy, takie jak MSG_NOSIGNAL w Linuksie i SO_NOSIGPIPE w BSD (działa tylko dla send , ale nie dla write ). To nie jest przenośne.
  • W przypadku FIFO (nazwanych potoków) SIGPIPE nie zostanie wygenerowany, jeśli program zapisujący użyje O_RDWR zamiast O_WRONLY , więc koniec odczytu jest zawsze otwarty. Jednak to również wyłącza EPIPE .
  • Możemy zignorować SIGPIPE lub ustawić globalny moduł obsługi. To dobre rozwiązanie, ale nie do przyjęcia, jeśli nie kontrolujesz całej aplikacji (np. Piszesz bibliotekę).
  • W najnowszych wersjach POSIX możemy wykorzystać fakt, że SIGPIPE jest wysyłany do wątku, który wywołał write() i obsługuje go przy użyciu techniki synchronicznej obsługi sygnałów.

Poniższy kod przedstawia bezpieczną dla wątków obsługę SIGPIPE dla POSIX.1-2004 i późniejszych.

Inspiruje go ten post :

  • Najpierw dodaj SIGPIPE do maski sygnału bieżącego wątku za pomocą pthread_sigmask() .
  • Sprawdź, czy istnieje już SIGPIPE za pomocą sigpending() .
  • Wywołanie write() . Jeśli koniec odczytu jest zamknięty, SIGPIPE zostanie dodany do maski sygnałów oczekujących i EPIPE zostanie zwrócony.
  • Jeśli write() zwróciło EPIPE , a SIGPIPE nie oczekiwał jeszcze przed write() , usuń go z maski sygnałów oczekujących za pomocą sigtimedwait() .
  • Przywróć oryginalną maskę sygnału za pomocą pthread_sigmask() .

Kod źródłowy:

#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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow