LINUX.ORG.RU

Reentrant/async-signal-safe snprintf() для использования в обработчиках сигналов

 , , ,


1

3

По следам Почему может зависнуть вызов localtime()? понадобилась async-signal-safe реализация snprintf(), чтобы можно было реализовать человеческое логгирование в обработчиках сигналов. Нашел такую вот реализацию с непонятной лицензией: https://github.com/idning/safe_snprintf В принципе, что нужно, но она слишком кастрированная - не знает про ширину полей. Нашел не такую кастрированную reentrant реализацию в newlib но не понятно как её выдрать оттуда. Какие есть ещё идеи/реализации?

★★★★★

Ответ на: комментарий от Deleted

«thread-safe» - это другое.

Async-signal-safe functions
       A signal handler function must be very careful, since processing
       elsewhere may be interrupted at some arbitrary point in the execution
       of the program.  POSIX has the concept of "safe function".  If a
       signal interrupts the execution of an unsafe function, and handler
       either calls an unsafe function or handler terminates via a call to
       longjmp() or siglongjmp() and the program subsequently calls an
       unsafe function, then the behavior of the program is undefined.

       POSIX.1-2004 (also known as POSIX.1-2001 Technical Corrigendum 2)
       requires an implementation to guarantee that the following functions
       can be safely called inside a signal handler:

           _Exit()
           _exit()
           abort()
           accept()
           access()
           aio_error()
           aio_return()
           aio_suspend()
           alarm()
           bind()
           cfgetispeed()
           cfgetospeed()
           cfsetispeed()
           cfsetospeed()
           chdir()
           chmod()
           chown()
           clock_gettime()
           close()
           connect()
           creat()
           dup()
           dup2()
           execle()
           execve()
           fchmod()
           fchown()
           fcntl()
           fdatasync()
           fork()
           fpathconf()
           fstat()
           fsync()
           ftruncate()
           getegid()
           geteuid()
           getgid()
           getgroups()
           getpeername()
           getpgrp()
           getpid()
           getppid()
           getsockname()
           getsockopt()
           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()
           sockatmark()
           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()
           waitpid()
           write()

       POSIX.1-2008 removes fpathconf(), pathconf(), and sysconf() from the
       above list, and adds the following functions:

           execl()
           execv()
           faccessat()
           fchmodat()
           fchownat()
           fexecve()
           fstatat()
           futimens()
           linkat()
           mkdirat()
           mkfifoat()
           mknod()
           mknodat()
           openat()
           readlinkat()
           renameat()
           symlinkat()
           unlinkat()
           utimensat()
           utimes()

       POSIX.1-2008 Technical Corrigendum 1 (2013) adds the following
       functions:

           fchdir()
           pthread_kill()
           pthread_self()
           pthread_sigmask()

http://man7.org/linux/man-pages/man7/signal.7.html

В этом списке, что характерно, нет никаких *printf(). Вот в этом и проблема.

asaw ★★★★★ ()
Ответ на: комментарий от anonymous

Если бы ещё и read() с write() не было в этом списке, тогда вообще было б не понятно как жить.

asaw ★★★★★ ()

Есть ещё реализация в nginx: ngx_snprintf(). Она вызывается самим nginx'ом из его обработчиков сигналов. Но, как многое в nginx, эта функция сделана самобытно, так что форматирование отличается от форматирования printf() (а значит, прощайте, предупреждения в compile time), да и насчёт поддержки ширины полей я не уверен.

Также см. apr_snprintf() из Apache Portable Runtime. В документации не сказано, что она реентерабельна, но если заглянуть в код, то там не видно криминала. Правда, формально она небезопасна, т.к. использует как минимум strlen(), но это ж как надо упороться, чтобы реализация strlen() была небезопасной? В любом случае, никто не мешает вам сделать безопасную реализацию небезопасных функций, используемых apr_snprintf().

Sorcerer ★★★★★ ()
Последнее исправление: Sorcerer (всего исправлений: 1)

Вот такая реализация есть:

C99-snprintf

Это даже не библиотека, просто один файл. Заявлено соотвествие стандарту. Signal safety не заявлена, но судя по коду в snprintf все ок (в asnprintf юзается malloc естественно).

На том же сайте есть ссылки на другие реализации. Бегло поглядел в gnulib - там signal safety нет, потому что malloc вроде юзается даже в snprintf.

gv ()

Какие есть ещё идеи

Может, забить на обработчики сигналов? Работу вынести в отдельный поток, а в основном сидеть на sigwait/sigwaitinfo и ждать сигналов.

i-rinat ★★★★★ ()
Ответ на: комментарий от asaw

В этом списке, что характерно, нет никаких *printf(). Вот в этом и проблема.

Так в этом списке вообще сисколы и простые обёртки над сисколами. snprintf там нет скорее из перестраховки, так как почему всех остальных *printf нет — совершенно понятно: меняется FILE *, скажем stdout. В snprintf FILE с флагом, что это строковый буфер помещен на стеке, так что вряд ли будут проблемы. Но так не пишут, не приветствуется вызов сложных и тормозных функций в обработчике.

А зачем вам write()?

vodz ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.