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:
-
SIGPIPE
signal skickas till processen som kallaswrite()
-
SIGPIPE
signal skickas till tråden som kallaswrite()
-
EPIPE
felet returneras avwrite()
Det finns flera sätt att hantera SIGPIPE
:
- För uttag kan
SIGPIPE
inaktiveras genom att ställa in plattformspecifika alternativ somMSG_NOSIGNAL
i Linux ochSO_NOSIGPIPE
i BSD (fungerar endast försend
, men inte förwrite
). Detta är inte bärbart.
- För FIFO: er (namngivna rör) kommer
SIGPIPE
inte att genereras om författaren använderO_RDWR
istället förO_WRONLY
, så attO_WRONLY
alltid öppnas. Men detta inaktiverarEPIPE
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 kallaswrite()
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 medpthread_sigmask()
. - Kontrollera om
SIGPIPE
redan finns i väntande medsigpending()
. - Ring
write()
. Om lässlutet är stängt läggsSIGPIPE
till pågåendeEPIPE
ochEPIPE
returneras. - Om
write()
returneradeEPIPE
, ochSIGPIPE
inte redan väntade innanwrite()
, ta bort den från väntandesigtimedwait()
medsigtimedwait()
. - Å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;
}