OpenGroup POSIX.1-2001与strerror_r一样定义The Linux Standard Base Core Specification 3.1。但是我找不到错误消息可以合理预期的最大大小的引用。我希望可以在代码中定义一些地方,但是找不到。
该代码必须是线程安全的。这就是为什么使用strerror_r而不是strerror的原因。
有人知道我可以使用的符号吗?我应该创建自己的吗?
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;
}
来自文档:
开放团体基本规范第6期:
错误
strerror_r()函数可能在以下情况下失败:
- [ERANGE]通过strerrbuf和buflen提供的存储空间不足包含生成的消息字符串。
来自来源:
glibc-2.7 / glibc-2.7 / string / strerror.c:41:
char *
strerror (errnum)
int errnum;
{
...
buf = malloc (1024);
具有足够大的静态限制可能对所有情况都足够好。如果确实需要获取完整的错误消息,则可以使用GNU version of strerror_r,也可以使用标准版本并使用依次更大的缓冲区对其进行轮询,直到获得所需的内容为止。例如,您可以使用类似下面的代码。
#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;
}
我不会担心的-256的缓冲区大小远远不够,而1024则过大。如果需要存储错误字符串,则可以使用strerror()
而不是strerror_r()
,然后选择使用strdup()
结果。但是,这不是线程安全的。如果您确实需要使用strerror_r()
而不是strerror()
来确保线程安全,则使用256的大小。在glibc-2.7
中,最长的错误消息字符串为50个字符(“无效或不完整的多字节或宽字符”)。我不希望将来的错误消息会更长(在最坏的情况下,会长几个字节)。
此程序(run online (as C++) here):
#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;
}
列出并打印系统上的所有错误,并跟踪最大长度。从外观上看,长度不超过49个字符(纯净的strlen
,不带最后的\0
),因此在留有余地的情况下,64-100应该足够了。
我很好奇,如果不能仅通过返回结构来避免整个缓冲区大小协商,以及是否存在不返回结构的根本原因。因此,我进行了基准测试:
#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;
}
并且两者在-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%).
无论如何,我甚至认为strerror
甚至是线程不安全的,这也很奇怪。这些返回的字符串应该是指向字符串文字的指针。 (请启发我,但是我想不起来应该在运行时将它们综合的情况)。根据定义,字符串文字是只读的,对只读数据的访问始终是线程安全的。