Печать int из обработчика сигналов с использованием функций записи или асинхронного ввода

Я хочу напечатать номер в журнале или на терминале, используя функцию write (или любую асинхронную функцию) внутри обработчика сигнала. Я бы предпочел не использовать буферизованный ввод-вывод.

Есть ли простой и рекомендуемый способ сделать это?

Например, вместо printf ниже я бы предпочел write (или любую асинную функцию).

 void signal_handler(int sig) { pid_t pid; int stat; int old_errno = errno; while((pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); errno = old_errno; return; } 

Печать строк проста. Вместо printf выше я могу использовать (без печати pid ):

 write(STDOUT_FILENO, "child terminated", 16); 

Если вы действительно настаиваете на выполнении печати с помощью обработчика сигналов, у вас есть в основном 2 варианта:

  1. Блокируйте сигнал, за исключением выделенного streamа, который вы создаете для обработки сигнала. Эта специальная нить может просто выполняться for (;;) pause(); и поскольку pause является безопасным для асинхронного сигнала, обработчик сигнала может использовать любые функции, которые он хочет; он не ограничивается только функциями, защищенными от асинхронного сигнала. С другой стороны, он должен получить доступ к общим ресурсам поточно-безопасным способом, поскольку теперь вы имеете дело с streamами.

  2. Напишите свой собственный код для преобразования целых чисел в десятичные строки. Это просто простой цикл использования %10 и /10 для удаления последней цифры и сохранения их в короткий массив.

Тем не менее, я бы настоятельно рекомендовал получить эту операцию из обработчика сигнала, используя трюк с собственной трубкой или аналогичный.

Внедрите свой собственный snprintf("%d безопасным сигналом snprintf("%d и используйте write

Это не так плохо, как я думал, как преобразовать int в строку в C? имеет несколько реализаций.

Программа POSIX ниже рассчитывает на то, сколько раз она получила SIGINT до сих пор, которую вы можете вызвать с помощью Ctrl + C

Вы можете выйти из программы с помощью Ctrl + \ (SIGQUIT).

main.c:

 #define _XOPEN_SOURCE 700 #include  #include  #include  #include  #include  #include  #include  #include  /* Calculate the minimal buffer size for a given type. * * Here we overestimate and reserve 8 chars per byte. * * With this size we could even print a binary string. * * - +1 for NULL terminator * - +1 for '-' sign * * A tight limit for base 10 can be found at: * https://stackoverflow.com/questions/8257714/how-to-convert-an-int-to-string-in-c/32871108#32871108 * * TODO: get tight limits for all bases, possibly by looking into * glibc's atoi: https://stackoverflow.com/questions/190229/where-is-the-itoa-function-in-linux/52127877#52127877 */ #define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2 /* async-signal-safe implementation of integer to string conversion. * * Null terminates the output string. * * The input buffer size must be large enough to contain the output, * the caller must calculate it properly. * * @param[out] value Input integer value to convert. * @param[out] result Buffer to output to. * @param[in] base Base to convert to. * @return Pointer to the end of the written string. */ char *itoa_safe(intmax_t value, char *result, int base) { intmax_t tmp_value; char *ptr, *ptr2, tmp_char; if (base < 2 || base > 36) { return NULL; } ptr = result; do { tmp_value = value; value /= base; *ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)]; } while (value); if (tmp_value < 0) *ptr++ = '-'; ptr2 = result; result = ptr; *ptr-- = '\0'; while (ptr2 < ptr) { tmp_char = *ptr; *ptr--= *ptr2; *ptr2++ = tmp_char; } return result; } volatile sig_atomic_t global = 0; void signal_handler(int sig) { char buf[ITOA_SAFE_STRLEN(sig_atomic_t)]; enum { base = 10 }; char *end; end = itoa_safe(global, buf, base); *end = '\n'; write(STDOUT_FILENO, buf, end - buf + 1); global += 1; signal(sig, signal_handler); } int main(int argc, char **argv) { /* Unit test itoa_safe. */ { typedef struct { intmax_t n; int base; char out[1024]; } InOut; char result[1024]; size_t i; InOut io; InOut ios[] = { /* Base 10. */ {0, 10, "0"}, {1, 10, "1"}, {9, 10, "9"}, {10, 10, "10"}, {100, 10, "100"}, {-1, 10, "-1"}, {-9, 10, "-9"}, {-10, 10, "-10"}, {-100, 10, "-100"}, /* Base 2. */ {0, 2, "0"}, {1, 2, "1"}, {10, 2, "1010"}, {100, 2, "1100100"}, {-1, 2, "-1"}, {-100, 2, "-1100100"}, /* Base 35. */ {0, 35, "0"}, {1, 35, "1"}, {34, 35, "Y"}, {35, 35, "10"}, {100, 35, "2U"}, {-1, 35, "-1"}, {-34, 35, "-Y"}, {-35, 35, "-10"}, {-100, 35, "-2U"}, }; for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) { io = ios[i]; itoa_safe(io.n, result, io.base); if (strcmp(result, io.out)) { printf("%ju %d %s\n", io.n, io.base, io.out); assert(0); } } } /* Handle the signals. */ if (argc > 1 && !strcmp(argv[1], "1")) { signal(SIGINT, signal_handler); while(1); } return EXIT_SUCCESS; } в #define _XOPEN_SOURCE 700 #include  #include  #include  #include  #include  #include  #include  #include  /* Calculate the minimal buffer size for a given type. * * Here we overestimate and reserve 8 chars per byte. * * With this size we could even print a binary string. * * - +1 for NULL terminator * - +1 for '-' sign * * A tight limit for base 10 can be found at: * https://stackoverflow.com/questions/8257714/how-to-convert-an-int-to-string-in-c/32871108#32871108 * * TODO: get tight limits for all bases, possibly by looking into * glibc's atoi: https://stackoverflow.com/questions/190229/where-is-the-itoa-function-in-linux/52127877#52127877 */ #define ITOA_SAFE_STRLEN(type) sizeof(type) * CHAR_BIT + 2 /* async-signal-safe implementation of integer to string conversion. * * Null terminates the output string. * * The input buffer size must be large enough to contain the output, * the caller must calculate it properly. * * @param[out] value Input integer value to convert. * @param[out] result Buffer to output to. * @param[in] base Base to convert to. * @return Pointer to the end of the written string. */ char *itoa_safe(intmax_t value, char *result, int base) { intmax_t tmp_value; char *ptr, *ptr2, tmp_char; if (base < 2 || base > 36) { return NULL; } ptr = result; do { tmp_value = value; value /= base; *ptr++ = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[35 + (tmp_value - value * base)]; } while (value); if (tmp_value < 0) *ptr++ = '-'; ptr2 = result; result = ptr; *ptr-- = '\0'; while (ptr2 < ptr) { tmp_char = *ptr; *ptr--= *ptr2; *ptr2++ = tmp_char; } return result; } volatile sig_atomic_t global = 0; void signal_handler(int sig) { char buf[ITOA_SAFE_STRLEN(sig_atomic_t)]; enum { base = 10 }; char *end; end = itoa_safe(global, buf, base); *end = '\n'; write(STDOUT_FILENO, buf, end - buf + 1); global += 1; signal(sig, signal_handler); } int main(int argc, char **argv) { /* Unit test itoa_safe. */ { typedef struct { intmax_t n; int base; char out[1024]; } InOut; char result[1024]; size_t i; InOut io; InOut ios[] = { /* Base 10. */ {0, 10, "0"}, {1, 10, "1"}, {9, 10, "9"}, {10, 10, "10"}, {100, 10, "100"}, {-1, 10, "-1"}, {-9, 10, "-9"}, {-10, 10, "-10"}, {-100, 10, "-100"}, /* Base 2. */ {0, 2, "0"}, {1, 2, "1"}, {10, 2, "1010"}, {100, 2, "1100100"}, {-1, 2, "-1"}, {-100, 2, "-1100100"}, /* Base 35. */ {0, 35, "0"}, {1, 35, "1"}, {34, 35, "Y"}, {35, 35, "10"}, {100, 35, "2U"}, {-1, 35, "-1"}, {-34, 35, "-Y"}, {-35, 35, "-10"}, {-100, 35, "-2U"}, }; for (i = 0; i < sizeof(ios)/sizeof(ios[0]); ++i) { io = ios[i]; itoa_safe(io.n, result, io.base); if (strcmp(result, io.out)) { printf("%ju %d %s\n", io.n, io.base, io.out); assert(0); } } } /* Handle the signals. */ if (argc > 1 && !strcmp(argv[1], "1")) { signal(SIGINT, signal_handler); while(1); } return EXIT_SUCCESS; } 

Скомпилировать и запустить:

 gcc -std=c99 -Wall -Wextra -o main main.c ./main 1 

После нажатия Ctrl + C пятнадцать раз терминал показывает:

 ^C0 ^C1 ^C2 ^C3 ^C4 ^C5 ^C6 ^C7 ^C8 ^C9 ^C10 ^C11 ^C12 ^C13 ^C14 

Вот связанная программа, которая создает более сложную строку формата. Как избежать использования printf в обработчике сигналов?

Протестировано на Ubuntu 18.04. GitHub вверх по течению .

Если вы настаиваете на использовании xprintf () внутри обработчика сигнала, вы всегда можете перевернуть свою собственную версию, которая не полагается на буферизованный ввод-вывод:

 #include  /* vsnprintf() */ void myprintf(FILE *fp, char *fmt, ...) { char buff[512]; int rc,fd; va_list argh; va_start (argh, fmt); rc = vsnprintf(buff, sizeof buff, fmt, argh); if (rc < 0 || rc >= sizeof buff) { rc = sprintf(buff, "Argh!: %d:\n", rc); } if (!fp) fp = stderr; fd = fileno(fp); if (fd < 0) return; if (rc > 0) write(fd, buff, rc); return; } 
Давайте будем гением компьютера.