수색…
통사론
- 부호없는 경보 (부호없는 초);
- int kill (pid_t pid, int sig);
매개 변수
함수, 매개 변수, 반환 값 | 기술 |
---|---|
alarm() | 함수 이름 |
unsigned seconds | 경보를 발생시키는 초 또는 보류중인 경보를 취소하기위한 0 |
> = 0 | 보류중인 다른 알람이 없으면 0이고 그렇지 않은 경우에는 보류중인 알람이 아직 열린 시간 (초)입니다. 이 기능은 실패하지 않습니다. |
- | - |
kill() | 함수 이름 |
pid_t pid | . |
int sig | 0 또는 신호 ID |
0, -1 | 성공시 0이 반환되고, errno 를 EINVAL , EPERM 또는 ESRCH 로 설정하면 실패시 -1이 반환됩니다. |
기본 동작으로 SIGALARM 높이기
alarm
사용하여 지정된 간격 후에 SIGALARM
신호를 발생 시키도록 예약 할 수 있습니다. 사용자가이 신호에 대해 명시 적 신호 처리기를 차단, 무시 또는 지정하지 않은 경우이 신호의 기본 동작은 도착시 수행됩니다. 스펙 별 SIGALARM
기본 조치는 프로세스를 종료하는 것입니다.
#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;
}
이 결과는 다음과 같습니다.
Hello!
1
2
3
4
5
[2] 35086 alarm ./a.out
sigaction을 사용하여 신호 처리기 설정 및 raise를 사용하여 신호 높이기
프로그램이 기본 동작을 사용하지 않고 특정 신호에 반응하려면 sigaction
사용하여 사용자 정의 신호 처리기를 설치할 수 있습니다. sigaction
은 3 개의 인자를받습니다 - 행동 할 시그널, sigaction_t
구조체에 대한 포인터, NULL
이 아니라면 새로운 행동과 sigaction_t
포인터를 기술 NULL
이 아니라면 이전 행동으로 채워질 것입니다. raise
프로세스를 사용하면 동일한 프로세스에서 신호를 발생시킬 수 있습니다. 더 많은 제어가 필요한 경우 (다른 프로세스로 신호를 보내려면 kill
또는 pthread_kill
사용할 수 있으며 대상 프로세스 ID 또는 스레드 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;
}
이 결과는 다음과 같습니다.
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
kill ()을 사용하여 자살하는 프로세스
프로세스는 kill()
함수를 사용하여 다른 프로세스로 신호를 보낼 수 있습니다.
이렇게하려면 보내는 프로세스가 수신 프로세스의 PID를 알고 있어야합니다. 경주를 도입하지 않고 프로세스는 자신의 PID (그리고 그 자식의 PID)만이 알 수 있으므로 kill()
의 사용법을 보여주기위한 가장 간단한 예제는 프로세스가 자신에게 신호를 보내는 것입니다.
kill 신호 ( 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.");
}
산출:
Killed
(... 구현에 따라 비슷하게)
thread-safe 방식으로 write ()에 의해 생성 된 SIGPIPE 처리
읽기 끝이 닫혀있는 명명 된 파이프 또는 스트림 소켓에 대해 write()
가 호출되면 다음 두 가지 일이 발생합니다.
-
SIGPIPE
시그널은write()
를 호출 한 프로세스 로 보내진다.
-
SIGPIPE
신호는write()
를 호출 한 쓰레드 로 보내진다.
-
EPIPE
오류는write()
의해 반환됩니다.
SIGPIPE
를 다루는 방법에는 여러 가지가 있습니다.
- 소켓의 경우 Linux의 경우
MSG_NOSIGNAL
, BSD의SO_NOSIGPIPE
와 같은 플랫폼 관련 옵션을 설정하여SIGPIPE
비활성화 할 수 있습니다 (send
전용으로 작동하지만write
에는 사용할 수 없음). 이것은 휴대용이 아닙니다.
- FIFO (명명 된 파이프)의 경우 작성자가
O_WRONLY
대신O_RDWR
사용하면 읽기 끝이 항상 열리도록SIGPIPE
가 생성되지 않습니다. 그러나 이것은EPIPE
도 비활성화합니다.
- 우리는
SIGPIPE
를 무시하거나 글로벌 핸들러를 설정할 수있다. 이것은 좋은 해결책이지만 전체 애플리케이션을 제어하지 않으면 (예 : 라이브러리 작성) 허용되지 않습니다.
- 최근 POSIX 버전에서는
SIGPIPE
가write()
를 호출 한 스레드로 보내고 동기 신호 처리 기술을 사용하여 처리한다는 사실을 사용할 수 있습니다.
아래 코드는 POSIX.1-2004 및 이후 버전에 대한 스레드 안전 SIGPIPE
처리를 보여줍니다.
- 우선,
pthread_sigmask()
사용하여 현재 쓰레드의 시그널 마스크에SIGPIPE
를 추가pthread_sigmask()
. - 이미
sigpending()
사용하여 보류중인SIGPIPE
가 있는지 확인하십시오. -
write()
호출하십시오. 판독 끝이 닫히면 보류중인 신호 마스크에SIGPIPE
가 추가되고EPIPE
가 반환됩니다. -
write()
EPIPE
리턴했고,write()
전에SIGPIPE
가 아직 보류 중이 아니면,sigtimedwait()
사용하여 보류중인 신호 마스크에서 제거하십시오. -
pthread_sigmask()
사용하여 원래의 신호 마스크를 복구하십시오.
소스 코드:
#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;
}