Какой размер я должен разрешить для strerror_r?
OpenGroup POSIX.1-2001 определяет strerror_r, а также Стандартная базовая спецификация Linux 3.1.. Но я не могу найти ссылку на максимальный размер, который можно было бы разумно ожидать для сообщения об ошибке. Я ожидал, что некоторые определите где-нибудь, что я могу поместить в свой код, но нет никого, что я могу найти.
Код должен быть потокобезопасным. Вот почему strerror_r используется, а не страус.
Знает ли кто-нибудь символ, который я могу использовать? Я должен создать свой собственный?
Пример
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;
}
Из документов:
Базовые спецификации Open Group Issue 6:
ОШИБКИ
Функция strerror_r() может выйти из строя, если:
- [ERANGE] Недостаточно места хранения было поставлено через strerrbuf и buflen до содержат сгенерированную строку сообщения.
Из источника:
Glibc-2.7/Glibc-2.7/строка/strerror.c: 41:
char *
strerror (errnum)
int errnum;
{
...
buf = malloc (1024);
Ответы
Ответ 1
Наличие достаточно большого статического предела, вероятно, достаточно хорошо для всех ситуаций.
Если вам действительно нужно получить все сообщение об ошибке, вы можете использовать версию GNU 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;
}
Ответ 2
Я бы не стал беспокоиться об этом - размер буфера 256 был намного более чем достаточным, а 1024 - излишним. Вы можете использовать strerror()
вместо strerror_r()
, а затем необязательно strdup()
результат, если вам нужно сохранить строку ошибки. Однако это не является потокобезопасным. Если вам действительно нужно использовать strerror_r()
вместо strerror()
для безопасности потоков, просто используйте размер 256. В glibc-2.7
самая длинная строка сообщения об ошибке составляет 50 символов ( "Недопустимый или неполный многобайтовый или широкий символ" ). Я бы не ожидал, что будущие сообщения об ошибках будут значительно длиннее (в худшем случае, на несколько байт больше).
Ответ 3
Эта программа (работает онлайн (как С++) здесь (кстати, кто-нибудь знает, как скомпилировать чистый 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;
}
перечисляет и печатает все ошибки в системе и отслеживает максимальную длину. По внешнему виду длина не превышает 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
даже разрешено быть небезопасным. Те возвращенные строки должны быть указателями на строковые литералы. (Пожалуйста, просветите меня, но я не могу придумать случай, когда они должны быть синтезированы во время выполнения). И строковые литералы по определению доступны только для чтения, и доступ к данным только для чтения всегда безопасен по потоку.