0

我正在通过阅读 NetBSD 源代码来研究“阅读代码”。
(有兴趣的可以看<代码阅读:开源视角>我正在阅读)

我发现了这个功能:

/* convert IP address to a string, but not into a single buffer
*/
char *
naddr_ntoa(naddr a)
{
#define NUM_BUFS 4
    static int bufno;
    static struct {
    char str[16];   /* xxx.xxx.xxx.xxx\0 */
    } bufs[NUM_BUFS];
    char *s;
    struct in_addr addr;

    addr.s_addr = a;
    strlcpy(bufs[bufno].str, inet_ntoa(addr), sizeof(bufs[bufno].str));
    s = bufs[bufno].str;
    bufno = (bufno+1) % NUM_BUFS;
    return s;
#undef NUM_BUFS
}

它引入了 4 个不同的临时缓冲区来包装 inet_ntoa 函数,因为 inet_ntoa 不是可重入的。
但在我看来,这个 naddr_ntoa 函数也不是可重入
的:静态 bufno 变量可以由其他变量操作,因此临时缓冲区在这里似乎没有按预期工作。

那么这是一个潜在的错误吗?

4

2 回答 2

2

是的,这是一个潜在的错误。如果您想要一个最有可能可重入的类似功能,您可以使用例如inet_ntop(顺便处理 IPv6)。

于 2012-09-20T06:11:56.620 回答
0

该代码来自src/sbin/routed/trace.c并且它不是通用库例程,而只是仅在路由程序中使用的自定义黑客。addrname()出于相同的原因,同一文件中的函数使用相同的技巧。它甚至不是 NetBSD 代码本身,而是它最初来自 SGI,由 Vernon Schryver 维护(请参阅The Routed Page)。

允许在同一个表达式中使用多个调用只是一种快速技巧,例如在一个 printf() 调用中使用结果的地方:例如:

printf("addr1->%s, addr2->%s, addr3->%s, addr4->%s\n",
       naddr_ntoa(addr1), naddr_ntoa(addr2), naddr_ntoa(addr3), naddr_ntoa(addr4));

在路由源文件(if.c、input.c、rdisc.c)中有几个类似用途的示例。

这段代码没有错误。路由程序不是多线程的。在这个 hack 中根本没有解决重入问题。这个技巧是为了一个非常特定的目的而设计的,与可重入无关。代码阅读作者将这个技巧与重入联系起来是错误的。

它只是一种将多个结果的保存隐藏在静态变量数组中的方法,而不是在单个表达式需要多个结果时将这些结果从一个静态变量单独复制到调用函数的单独存储中。

请记住,除了标识符的有限范围之外,静态变量具有全局变量的所有属性。当然,在函数中不受保护地使用全局(或静态)变量会使该函数不可重入,但这不是全局变量导致的唯一问题。在路由中使用完全可重入函数是不合适的,因为它实际上会使代码变得比必要的更复杂,而这种 hack 使调用代码保持干净和简单。不过,最好对黑客进行适当的记录,以便未来的维护人员更容易发现何时NUM_BUFS需要进行调整。

于 2012-11-11T02:14:13.947 回答