strerror_r应该允许多大?

时间:2021-01-18 07:10:54

The OpenGroup POSIX.1-2001 defines strerror_r, as does The Linux Standard Base Core Specification 3.1. But I can find no reference to the maximum size that could be reasonably expected for an error message. I expected some define somewhere that I could put in my code but there is none that I can find.

OpenGroup posix .1. 2001定义了strerror_r, Linux标准基础核心规范3.1也是如此。但是我找不到对错误消息可以合理预期的最大大小的引用。我希望有一些定义可以放到代码中,但是我找不到。

The code must be thread safe. Which is why strerror_r is used and not strerror.

代码必须是线程安全的。这就是为什么使用strerror_r而不是strerror的原因。

Does any one know the symbol I can use? I should I create my own?

有人知道我可以用什么符号吗?我应该自己创造吗?


Example

int result = gethostname(p_buffy, size_buffy);
int errsv = errno;
if (result < 0)
{
    char buf[256];
    char const * str = strerror_r(errsv, buf, 256);
    syslog(LOG_ERR,
             "gethostname failed; errno=%d(%s), buf='%s'",
             errsv,
             str,
             p_buffy);
     return errsv;
}

From the documents:

从文档:

The Open Group Base Specifications Issue 6:

开放组基本规格问题6:

ERRORS

错误

The strerror_r() function may fail if:

如果:strerror_r()函数可能会失败:

  • [ERANGE] Insufficient storage was supplied via strerrbuf and buflen to contain the generated message string.
  • [ERANGE]通过strerrbuf和buflen提供的存储不足,无法包含生成的消息字符串。

From the source:

从源:

glibc-2.7/glibc-2.7/string/strerror.c:41:

glibc 2.7 - 2.7 / glibc /字符串/ strerror.c:41:

    char *
    strerror (errnum)
         int errnum;
    {
        ...
        buf = malloc (1024);

3 个解决方案

#1


10  

Having a sufficiently large static limit is probably good enough for all situations. If you really need to get the entire error message, you can use the GNU version of strerror_r, or you can use the standard version and poll it with successively larger buffers until you get what you need. For example, you may use something like the code below.

具有足够大的静态限制可能对所有情况都足够好。如果您确实需要获取整个错误消息,您可以使用strerror_r的GNU版本,或者您可以使用标准版本,并使用连续较大的缓冲区轮询它,直到您获得所需的内容。例如,您可以使用如下代码。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Call strerror_r and get the full error message. Allocate memory for the
 * entire string with malloc. Return string. Caller must free string.
 * If malloc fails, return NULL.
 */
char *all_strerror(int n)
{
    char *s;
    size_t size;

    size = 1024;
    s = malloc(size);
    if (s == NULL)
        return NULL;

    while (strerror_r(n, s, size) == -1 && errno == ERANGE) {
        size *= 2;
        s = realloc(s, size);
        if (s == NULL)
            return NULL;
    }

    return s;
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; ++i) {
        int n = atoi(argv[i]);
        char *s = all_strerror(n);
        printf("[%d]: %s\n", n, s);
        free(s);
    }

    return 0;
}

#2


9  

I wouldn't worry about it - a buffer size of 256 is far more than sufficient, and 1024 is overkill. You could use strerror() instead of strerror_r(), and then optionally strdup() the result if you need to store the error string. This isn't thread-safe, though. If you really need to use strerror_r() instead of strerror() for thread safety, just use a size of 256. In glibc-2.7, the longest error message string is 50 characters ("Invalid or incomplete multibyte or wide character"). I wouldn't expect future error messages to be significantly longer (in the worst case, a few bytes longer).

我不会担心它——一个256的缓冲区大小远远超过了它的数量,1024是过量的。如果需要存储错误字符串,可以使用strerror()而不是strerror_r(),然后选择strdup()结果。这不是线程安全的。如果您确实需要使用strerror_r()而不是strerror()来保证线程安全,只需使用256大小。在glibc-2.7中,最长的错误消息字符串是50个字符(“无效或不完整的多字节或宽字符”)。我不认为将来的错误消息会长很多(在最坏的情况下,会多几个字节)。

#3


3  

This program (run online (as C++) here (btw, anybody knows how to compile pure C online?):

这个程序(以c++运行)在这里(顺便说一下,有人知道如何在线编译纯C吗?)

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(){
        const int limit = 5;
        int unknowns = 0;
        int maxlen = 0;
        int i=0; char* s = strerror(i);
        while(1){
            if (maxlen<strlen(s)) maxlen = strlen(s);
            if (/*BEGINS WITH "Unknown "*/ 0==strncmp("Unknown ", s , sizeof("Unknown ")-1) )
                unknowns++;
            printf("%.3d\t%s\n", i, s);
            i++; s=strerror(i);
            if ( limit == unknowns ) break;
        }
        printf("Max: %d\n", maxlen);
        return 0;
}

lists and prints all the errors on the system and keeps track of the maximum length. By the looks of it, the length does not exceed 49 characters (pure strlen's without the final \0) so with some leeway, 64–100 should be more than enough.

列出并打印系统上的所有错误并跟踪最大长度。根据它的外观,长度不超过49个字符(纯strlen的没有最后的\0),所以有一些余地,64-100应该足够了。

I got curious if the whole buffer size negotiation couldn't simply be avoided by returning structs and whether there was a fundamental reason for not returning structs. So I benchmarked:

我很好奇,是否整个缓冲区大小协商不能通过返回结构来简单地避免,以及是否存在不返回结构的根本原因。所以我基准测试:

#define _POSIX_C_SOURCE 200112L //or else the GNU version of strerror_r gets used
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char data[64]; } error_str_t;
error_str_t strerror_reent(int errn) __attribute__((const));
error_str_t strerror_reent(int errn){
    error_str_t ret;
    strerror_r(errn, ret.data, sizeof(ret));
    return ret;
}


int main(int argc, char** argv){
    int reps = atoi(argv[1]);
    char buf[64];
    volatile int errn = 1;
    for(int i=0; i<reps; i++){
#ifdef VAL
        error_str_t err = strerror_reent(errn);
#else
        strerror_r(errn, buf, 64);
#endif
    }
    return 0;
}

and the performance difference between the two at -O2 is minimal:

而两者在-O2时的性能差异最小:

gcc -O2 : The VAL version is slower by about 5%
g++ -O2 -x c++ : The VAL version is faster by about 1% than the standard version compiled as C++ and by about 4% faster than the standard version compiled as C (surprisingly, even the slower C++ version beats the faster C version by about 3%).

In any case, I think it's extremely weird that strerror is even allowed to be thread unsafe. Those returned strings should be pointers to string literals. (Please enlighten me, but I can't think of a case where they should be synthesized at runtime). And string literals are by definition read only and access to read only data is always thread safe.

无论如何,我认为strerror甚至被允许为线程不安全是极其奇怪的。这些返回的字符串应该是指向字符串文字的指针。(请给我点化一下,但我想不出一个在运行时合成它们的情况)。根据定义,字符串文本是只读的,访问只读数据总是线程安全的。

#1


10  

Having a sufficiently large static limit is probably good enough for all situations. If you really need to get the entire error message, you can use the GNU version of strerror_r, or you can use the standard version and poll it with successively larger buffers until you get what you need. For example, you may use something like the code below.

具有足够大的静态限制可能对所有情况都足够好。如果您确实需要获取整个错误消息,您可以使用strerror_r的GNU版本,或者您可以使用标准版本,并使用连续较大的缓冲区轮询它,直到您获得所需的内容。例如,您可以使用如下代码。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Call strerror_r and get the full error message. Allocate memory for the
 * entire string with malloc. Return string. Caller must free string.
 * If malloc fails, return NULL.
 */
char *all_strerror(int n)
{
    char *s;
    size_t size;

    size = 1024;
    s = malloc(size);
    if (s == NULL)
        return NULL;

    while (strerror_r(n, s, size) == -1 && errno == ERANGE) {
        size *= 2;
        s = realloc(s, size);
        if (s == NULL)
            return NULL;
    }

    return s;
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; ++i) {
        int n = atoi(argv[i]);
        char *s = all_strerror(n);
        printf("[%d]: %s\n", n, s);
        free(s);
    }

    return 0;
}

#2


9  

I wouldn't worry about it - a buffer size of 256 is far more than sufficient, and 1024 is overkill. You could use strerror() instead of strerror_r(), and then optionally strdup() the result if you need to store the error string. This isn't thread-safe, though. If you really need to use strerror_r() instead of strerror() for thread safety, just use a size of 256. In glibc-2.7, the longest error message string is 50 characters ("Invalid or incomplete multibyte or wide character"). I wouldn't expect future error messages to be significantly longer (in the worst case, a few bytes longer).

我不会担心它——一个256的缓冲区大小远远超过了它的数量,1024是过量的。如果需要存储错误字符串,可以使用strerror()而不是strerror_r(),然后选择strdup()结果。这不是线程安全的。如果您确实需要使用strerror_r()而不是strerror()来保证线程安全,只需使用256大小。在glibc-2.7中,最长的错误消息字符串是50个字符(“无效或不完整的多字节或宽字符”)。我不认为将来的错误消息会长很多(在最坏的情况下,会多几个字节)。

#3


3  

This program (run online (as C++) here (btw, anybody knows how to compile pure C online?):

这个程序(以c++运行)在这里(顺便说一下,有人知道如何在线编译纯C吗?)

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(){
        const int limit = 5;
        int unknowns = 0;
        int maxlen = 0;
        int i=0; char* s = strerror(i);
        while(1){
            if (maxlen<strlen(s)) maxlen = strlen(s);
            if (/*BEGINS WITH "Unknown "*/ 0==strncmp("Unknown ", s , sizeof("Unknown ")-1) )
                unknowns++;
            printf("%.3d\t%s\n", i, s);
            i++; s=strerror(i);
            if ( limit == unknowns ) break;
        }
        printf("Max: %d\n", maxlen);
        return 0;
}

lists and prints all the errors on the system and keeps track of the maximum length. By the looks of it, the length does not exceed 49 characters (pure strlen's without the final \0) so with some leeway, 64–100 should be more than enough.

列出并打印系统上的所有错误并跟踪最大长度。根据它的外观,长度不超过49个字符(纯strlen的没有最后的\0),所以有一些余地,64-100应该足够了。

I got curious if the whole buffer size negotiation couldn't simply be avoided by returning structs and whether there was a fundamental reason for not returning structs. So I benchmarked:

我很好奇,是否整个缓冲区大小协商不能通过返回结构来简单地避免,以及是否存在不返回结构的根本原因。所以我基准测试:

#define _POSIX_C_SOURCE 200112L //or else the GNU version of strerror_r gets used
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char data[64]; } error_str_t;
error_str_t strerror_reent(int errn) __attribute__((const));
error_str_t strerror_reent(int errn){
    error_str_t ret;
    strerror_r(errn, ret.data, sizeof(ret));
    return ret;
}


int main(int argc, char** argv){
    int reps = atoi(argv[1]);
    char buf[64];
    volatile int errn = 1;
    for(int i=0; i<reps; i++){
#ifdef VAL
        error_str_t err = strerror_reent(errn);
#else
        strerror_r(errn, buf, 64);
#endif
    }
    return 0;
}

and the performance difference between the two at -O2 is minimal:

而两者在-O2时的性能差异最小:

gcc -O2 : The VAL version is slower by about 5%
g++ -O2 -x c++ : The VAL version is faster by about 1% than the standard version compiled as C++ and by about 4% faster than the standard version compiled as C (surprisingly, even the slower C++ version beats the faster C version by about 3%).

In any case, I think it's extremely weird that strerror is even allowed to be thread unsafe. Those returned strings should be pointers to string literals. (Please enlighten me, but I can't think of a case where they should be synthesized at runtime). And string literals are by definition read only and access to read only data is always thread safe.

无论如何,我认为strerror甚至被允许为线程不安全是极其奇怪的。这些返回的字符串应该是指向字符串文字的指针。(请给我点化一下,但我想不出一个在运行时合成它们的情况)。根据定义,字符串文本是只读的,访问只读数据总是线程安全的。