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:
- Sygnał
SIGPIPE
jest wysyłany do procesu, który wywołałwrite()
- Sygnał
SIGPIPE
jest wysyłany do wątku, który wywołałwrite()
- Błąd
EPIPE
jest zwracany przezwrite()
Istnieje kilka sposobów radzenia sobie z SIGPIPE
:
- W przypadku gniazd
SIGPIPE
można wyłączyć, ustawiając opcje specyficzne dla platformy, takie jakMSG_NOSIGNAL
w Linuksie iSO_NOSIGPIPE
w BSD (działa tylko dlasend
, ale nie dlawrite
). To nie jest przenośne.
- W przypadku FIFO (nazwanych potoków)
SIGPIPE
nie zostanie wygenerowany, jeśli program zapisujący użyjeO_RDWR
zamiastO_WRONLY
, więc koniec odczytu jest zawsze otwarty. Jednak to również wyłączaEPIPE
.
- 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 iEPIPE
zostanie zwrócony. - Jeśli
write()
zwróciłoEPIPE
, aSIGPIPE
nie oczekiwał jeszcze przedwrite()
, 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;
}