2. Установка обработчика сигнала SIGINT (генерируется при нажатии Ctrl+C). Обработчик вызывает функцию crypt() для зашифровки строки текста, предоставленной в качестве второго аргумента.
3. Вход в бесконечный цикл for, в котором используется функция crypt() для зашифровки строки текста в первом аргументе командной строки и осуществление проверки того, что возвращенная строка совпадает со строкой, сохраненной при выполнении шага 1.
Строки в шаге 3 всегда будут совпадать при отсутствии сигнала. Другое дело, если поступает сигнал SIGINT и выполнение обработчика прерывает выполнение основной программы сразу же после вызова функции crypt() в цикле for и перед осуществлением проверки совпадения строк. В этом случае программа сообщит о несовпадении. При запуске программы мы увидим следующее:
$ ./non_reentrant abc def
Mismatch on call 109871 (mismatch=1 handled=1)
Mismatch on call 128061 (mismatch=2 handled=2)
Mismatch on call 727935 (mismatch=149 handled=156)
Mismatch on call 729547 (mismatch=150 handled=157)
Quit (core dumped)
Если мы сравним значения mismatch и handled в вышеприведенном выводе, то увидим, что в большинстве случаев при инициализации сигнала происходит перезапись статически выделенного буфера между вызовом функции crypt() и сравнением строк в main().
Листинг 21.1. Вызов нереентерабельной функции из main() и обработчика сигнала
signals/nonreentrant.c
#define _XOPEN_SOURCE 600
#include
#include
#include
#include "tlpi_hdr.h"
static char *str2; /* Устанавливается из argv[2] */
static int handled = 0; /* Счетчик вызовов обработчика */
static void
handler(int sig)
{
crypt(str2, "xx");
handled++;
}
int
main(int argc, char *argv[])
{
char *cr1;
int callNum, mismatch;
struct sigaction sa;
if (argc!= 3)
usageErr("%s str1 str2\n", argv[0]);
str2 = argv[2]; /* Сделать argv[2] доступным обработчику */
cr1 = strdup(crypt(argv[1], "xx")); /* Скопировать статически
выделенную строку в иной буфер */
if (cr1 == NULL)
errExit("strdup");
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = handler;
if (sigaction(SIGINT, &sa, NULL) == –1)
errExit("sigaction");
/* Многократно вызывать crypt() с использованием argv[1]. Если прервано
обработчиком сигнала, то статическое хранилище, возвращенное crypt(),
будет перезаписано результатами зашифровки argv[2], а strcmp()
обнаружит несовпадение со значением 'cr1'. */
for (callNum = 1, mismatch = 0;; callNum++) {
if (strcmp(crypt(argv[1], "xx"), cr1)!= 0) {
mismatch++;
printf("Mismatch on call %d (mismatch=%d handled=%d)\n",
callNum, mismatch, handled);
}
}
}
signals/nonreentrant.c
Стандартные функции, безопасные для асинхронных сигналов
Функция, безопасная для асинхронных сигналов, — это такая функция, реализация которой гарантирует безопасность при вызове из обработчика. Функция может быть безопасной для асинхронного сигнала либо благодаря тому, что она реентерабельна, либо потому, что она непрерываема обработчиком.
Безопасность перечисленных в табл. 21.1 функций для асинхронных сигналов требуется различными стандартами. Если после имени в таблице не указано
Таблица 21.1. Функции, которые должны быть безопасными для асинхронных сигналов согласно стандартам POSIX.1-1990, SUSv2 и SUSv3
_Exit() (v3)
_exit()
abort() (v3)
accept() (v3)
access()
aio_error() (v2)
aio_return() (v2)
aio_suspend() (v2)
alarm()
bind() (v3)
cfgetispeed()
cfgetospeed()
cfsetispeed()
cfsetospeed()
chdir()
chmod()
chown()
clock_gettime() (v2)
close()
connect() (v3)
getpid()
getppid()
getsockname() (v3)
getsockopt() (v3)
getuid()
kill()
link()
listen() (v3)
lseek()
lstat() (v3)
mkdir()
mkfifo()
open()
pathconf()
pause()
pipe()
poll() (v3)
posix_trace_event() (v3)
pselect() (v3)
raise() (v2)
sigdelset()
sigemptyset()
sigfillset()
sigismember()
signal() (v2)
sigpause() (v2)
sigpending()
sigprocmask()
sigqueue() (v2)
sigset() (v2)
sigsuspend()
sleep()
socket() (v3)
sockatmark() (v3)
socketpair() (v3)
stat()
symlink() (v3)
sysconf()
tcdrain()
tcflow()
creat()
dup()
dup2()
execle()
execve()
fchmod() (v3)
fchown() (v3)
fcntl()
fdatasync() (v2)
fork()
fpathconf() (v2)
fstat()
fsync() (v2)
ftruncate() (v3)
getegid()