Если вам требуется доступ к глобальным данным из обработчика сигналов (что и делает большинство обработчиков), оставляйте структуры данных простыми. Хотя достаточно просто безопасно модифицировать отдельный элемент данных, такой как int, более сложные структуры обычно требуют блокировки сигналов. Любые глобальные переменные, которые могут быть модифицированы обработчиками сигналов, должны быть объявлены с ключевым словом volatile
. Это сообщает компилятору, что переменная может быть изменена вне нормального потока программы, и он не должен пытаться оптимизировать доступ к этой переменной.
Другая вещь, с которой нужно соблюдать осторожность в обработчиках сигналов — это вызов других функций, потому что они тоже могут изменять глобальные данные! Библиотека С stdio
пытается облегчить это и не допускает вызовов своих функций из обработчиков сигналов. В табл. 12.2 перечислены функции, которые гарантированно являются безопасными для вызова из обработчиков сигналов[66]; вызовов всех прочих функций следует избегать.
Таблица 12.2. Реентерабельные функции
abort() | accept() | access() |
aio_error() | aio_return() | aio_suspend() |
alarm() | bind() | cfgetispeed() |
cfgetospeed() | cfsetispeed() | cfsetospeed() |
chdir() | chmod() | chown() |
close() | connect() | creat() |
dup() | dup2() | execle() |
execve() | _exit() | fchmod() |
fchown() | fcntl() | fdatasync() |
fork() | fpathconf() | fstat() |
fsync() | getegid() | geteuid() |
getgid() | getgroups() | getpeername() |
getpgrp() | getpid() | getppid() |
getuid() | kill() | link() |
listen() | lseek() | lstat() |
mkdir() | mkfifo() | open() |
pathconf() | pause() | pipe() |
poll() | posix_trace_event() | pselect() |
raise() | read() | readlink() |
recv() | recvfrom() | recvmsg() |
rename() | rmdir() | select() |
sem_post() | send() | sendmsg() |
sendto() | setgid() | setpgid() |
setsid() | setsockopt() | setuid() |
shutdown() | sigaction() | sigaddset() |
sigdelset() | sigemptyset() | sigfillset() |
sigismember() | signal() | sigpause() |
sigpending() | sigprocmask() | sigqueue() |
sigset() | sigsuspend() | sleep() |
socket() | socketpair() | stat() |
symlink() | sysconf() | tcdrain() |
tcflow() | tcflush() | tcgetattr() |
tcgetpgrp() | tcsendbreak() | tcsetattr() |
tcsetpgrp() | time() | timer_getoverrun() |
timer_gettime() | timer_settime() | times() |
umask() | uname() | unlink() |
utime() | wait() | wait3() |
wait4() | waitpid() | write() |
12.5. Повторное открытие журнальных файлов
Большинство системных демонов ведут журнальные файлы, записывая в них все, что они делают. Поскольку многие системы Unix месяцами работают без остановки, эти журнальные файлы могут стать достаточно большими. Простое периодическое удаление (или переименование) журнальных файлов — не самое хорошее решение, потому что демоны будут продолжать записывать в эти файлы, несмотря на их недоступность, а необходимость останавливать и запускать каждый демон для очистки журнальных файлов приводит к недоступности системы (хоть и на незначительное время). Общий способ для демонов справиться с упомянутой ситуацией — перехватывать SIGHUP
и повторно открывать журнальные файлы. Это позволяет организовать ротацию журналов (периодическое открытие новых журнальных файлов при сохранении старых), используя простой сценарий вроде приведенного ниже.
dd /var/log
mv messages messages.old
killall -HUP syslogd
Logrotate (ftp://ftp.redhat.com/pub/redhat/code/logrotate/) — одна из программ, которая использует преимущество такого метода для выполнения безопасной ротации журналов.
Включение этой возможности у большинства демонов достаточно просто. Одним их наиболее легких подходов является использование глобальной переменной, которая индицирует необходимость повторного открытия журналов.
Затем обработчик сигнала SIGHUP
в своем вызове устанавливает эту переменную, и главная часть программы проверяет эту переменную насколько можно часто. Ниже приведен пример соответствующей программы.
1: /*sighup.c*/
2:
3: #include
4: #include
5: #include
6: #include
7: #include
8: