使用write或async-safe函数从信号处理程序中打印int

我想在信号处理程序内使用write (或任何async-safe函数)将数字打印到日志或终端中。 我宁愿不使用缓冲I / O.

是否有一种简单而推荐的方法可以做到这一点?

例如,代替printf ,我更喜欢write (或任何asyn安全function)。

 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; } 

打印字符串很容易。 我可以使用(无需打印pid )代替上面的printf

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

如果你真的坚持从信号处理程序进行打印,你基本上有两个选择:

  1. 除了您为处理信号而创建的专用线程外,阻止信号。 这个特殊的线程可以简单地执行for (;;) pause(); 由于pause是异步信号安全的,因此允许信号处理程序使用它想要的任何function; 它不仅限于异步信号安全function。 另一方面,它必须以线程安全的方式访问共享资源,因为您现在正在处理线程。

  2. 编写自己的代码将整数转换为十进制字符串。 这只是一个简单的循环,使用%10/10剥离最后一个数字并将它们存储到一个短数组。

但是,我强烈建议使用自管技巧或类似方法将此操作从信号处理程序中取出。

实现自己的async-signal-safe snprintf("%d并使用write

它没有我想象的那么糟糕, 如何在C中将int转换为字符串? 有几个实现。

下面的POSIX程序计算stdout到目前为止收到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; } 

编译并运行:

 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(),你总是可以滚动自己的版本而不依赖于缓冲的I / O:

 #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; }