Какой размер я должен разрешить для strerror_r?

OpenGroup POSIX.1-2001 определяет strerror_r, как и Стандартная базовая базовая спецификация Linux 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;
}

Из документов:

Базовые спецификации Open Group, выпуск 6:

ОШИБКИ

Функция strerror_r () может завершиться ошибкой, если:

  • [ERANGE] Недостаточно памяти для хранения сгенерированной строки сообщения через strerrbuf и buflen.

Из источника:

glibc-2.7 / glibc-2.7 / строка / strerror.c: 41:

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

person mat_geek    schedule 08.01.2009    source источник
comment
Обратите внимание, что вместо использования функции strerror вы можете использовать _ 2_ со спецификатором %m (который является POSIX-compliant). Пример: syslog(LOG_ERR, "Error occured, details: %m"). Чтобы узнать больше, прочтите руководство syslog. К сожалению, я не знаю, является ли %m потокобезопасным, поскольку _7 _ есть.   -  person patryk.beza    schedule 18.09.2016


Ответы (3)


Достаточно большого статического предела, вероятно, достаточно для всех ситуаций. Если вам действительно нужно получить все сообщение об ошибке, вы можете использовать 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;
}
person Community    schedule 09.01.2009
comment
Для GNU strerror_r из предоставленной вами ссылки: (строка может быть обрезана, если buflen слишком мал). Как это решает проблему? - person Michael Mior; 27.08.2010
comment
@Michael, совместимый с XSI strerror_r возвращает ошибку, если буфер слишком мал. Мне кажется, что GNU менее полезен. - person ; 29.08.2010
comment
увеличение размера буфера, на 2 байта за раз, меня не особо впечатляет. попробуйте добавить 256 байт (1/4 оригинала) или удваивать каждый раз ... тогда у вас будет алгоритм. - person Thomas W; 01.06.2012
comment
хотя желательность и надежность использования этого подхода вообще сомнительны. вероятно, просто определение подходящего размера фиксированного буфера выигрывает - по соображениям простоты и надежности. - person Thomas W; 01.06.2012
comment
Томас, о, я по ошибке использовал += вместо *=; фиксация. - person ; 01.06.2012
comment
Это отлично работает, спасибо. Это немного смешно, что нам даже нужно об этом беспокоиться. Кто бы ни определил интерфейс POSIX strerror_r, он задумался еще меньше, чем исходный strerror. Это пример плохого дизайна, отсутствия инкапсуляции. - person Daniel Saner; 18.01.2013
comment
Также не забудьте использовать временную переменную для этого перераспределения. В противном случае может произойти сбой, что приведет к потере исходного указателя. - person Steven; 16.02.2017

Я бы не стал беспокоиться об этом - размера буфера 256 более чем достаточно, а 1024 - это слишком много. Вы можете использовать strerror() вместо strerror_r(), а затем, при желании, strdup() результат, если вам нужно сохранить строку ошибки. Однако это не является потокобезопасным. Если вам действительно нужно использовать strerror_r() вместо strerror() для обеспечения безопасности потоков, просто используйте размер 256. В glibc-2.7 самая длинная строка сообщения об ошибке составляет 50 символов («Недействительный или неполный многобайтовый или широкий символ»). Я бы не ожидал, что будущие сообщения об ошибках будут значительно длиннее (в худшем случае - на несколько байтов).

person Adam Rosenfield    schedule 08.01.2009
comment
И если вам необходимо использовать символ, я бы предложил BUFSIZ из ‹stdio.h› - person Jonathan Leffler; 08.01.2009
comment
Чтобы уточнить, мне действительно нужно использовать strerror_r () вместо strerror () для обеспечения безопасности потоков. - person mat_geek; 08.01.2009
comment
Обратите внимание, что сообщения могут быть в 3 раза длиннее в неанглийской локали только потому, что используемые в них символы выше U+0800. Для идеографических языков это вряд ли проблема, потому что, хотя каждый символ состоит из 3 байтов вместо 1 байта, слова часто состоят из 1-2 символов вместо 6-12 символов. Но на других языках, отличных от латинского алфавита (особенно в индийских скриптах), я мог видеть сообщения об ошибках, легко достигающие 256 байт. - person R.. GitHub STOP HELPING ICE; 05.02.2011

Эта программа (работает онлайн (как 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 даже может быть небезопасным для потоков. Эти возвращенные строки должны быть указателями на строковые литералы. (Пожалуйста, просветите меня, но я не могу придумать случая, когда бы они были синтезированы во время выполнения). Строковые литералы по определению доступны только для чтения, а доступ к данным только для чтения всегда потокобезопасен.

person PSkocik    schedule 20.07.2016
comment
В комментариях к этому ответу обсуждаются причины, по которым strerror может быть небезопасным для потоков. - person Carsten S; 28.11.2016
comment
@CarstenS Спасибо. Я все еще думаю, что локали - плохая причина для отказа от повторного входа. У каждой локали может быть свой собственный набор статических строк, доступных только для чтения. Я предполагаю, что кто-то может захотеть синтезировать локализованные результаты из общих частей, но выполнение такого рода дедупликации может показаться мне крайним случаем преждевременной оптимизации, и это исходит от преждевременного оптимизатора. К сожалению, нет смысла спорить со стандартами. - person PSkocik; 28.11.2016
comment
Меня это тоже раздражает. Я думаю, что проблема, на которую намекают комментарии к ответу на этот другой вопрос, заключается в том, что локаль может быть деинсталлирована, пока у кого-то еще есть указатель на строку с ошибкой. Во всяком случае, тоже не мои решения;) - person Carsten S; 28.11.2016
comment
@PSkocik - Что касается запуска C в сети, вы можете попробовать TIO или ideone. - person owacoder; 06.07.2019