Какой размер я должен разрешить для 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:

ERRORS

The strerror_r() function may fail if:

  • [ERANGE] Insufficient storage was supplied via strerrbuf and buflen to contain the generated message string.

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

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

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

Обратите внимание, что вместо использования функции strerror вы можете использовать syslog со спецификатором %m (которым является POSIX-совместимый). Пример: syslog(LOG_ERR, "Error occured, details: %m"). Прочтите руководство syslog, чтобы узнать больше. К сожалению, я не знаю, является ли %m потокобезопасным, как strerror_r.

patryk.beza 18.09.2016 16:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
34
1
13 971
3

Ответы 3

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

И если вам необходимо использовать символ, я бы предложил BUFSIZ из <stdio.h>

Jonathan Leffler 08.01.2009 09:07

Чтобы прояснить, мне «действительно нужно использовать strerror_r () вместо strerror () для обеспечения безопасности потоков».

mat_geek 08.01.2009 09:52

Обратите внимание, что сообщения могут быть в 3 раза длиннее в неанглийской локали только потому, что используемые в них символы выше U+0800. Для идеографических языков это вряд ли проблема, потому что, хотя каждый символ состоит из 3 байтов вместо 1 байта, слова часто состоят из 1-2 символов вместо 6-12 символов. Но на других языках, отличных от латинского алфавита (особенно в индийских скриптах), я мог видеть сообщения об ошибках, легко достигающие 256 байт.

R.. GitHub STOP HELPING ICE 05.02.2011 22:23

Достаточно большого статического предела, вероятно, достаточно для всех ситуаций. Если вам действительно нужно получить все сообщение об ошибке, вы можете использовать Версия 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;
}

Для GNU strerror_r из предоставленной вами ссылки: (строка может быть обрезана, если buflen слишком мал). Как это решает проблему?

Michael Mior 27.08.2010 04:56

@Michael, совместимый с XSI strerror_r возвращает ошибку, если буфер слишком мал. Мне кажется, что GNU менее полезен.

user25148 29.08.2010 03:10

увеличение размера буфера, на 2 байта за раз, меня не особо впечатляет. попробуйте добавить 256 байт (1/4 оригинала) или удваивать каждый раз ... тогда у вас будет алгоритм.

Thomas W 01.06.2012 04:51

хотя желательность и надежность использования этого подхода вообще сомнительны. вероятно, просто определение подходящего размера фиксированного буфера выигрывает - по соображениям простоты и надежности.

Thomas W 01.06.2012 04:52

Томас, о, я по ошибке использовал += вместо *=; фиксация.

user25148 01.06.2012 08:52

Это отлично работает, спасибо. Это немного смешно, что нам даже нужно об этом беспокоиться. Кто бы ни определил интерфейс POSIX strerror_r, он задумался даже меньше, чем оригинальный strerror. Это пример плохого дизайна, отсутствия инкапсуляции.

Daniel Saner 18.01.2013 19:56

Также не забудьте использовать временную переменную для этого перераспределения. В противном случае может произойти сбой, что приведет к потере исходного указателя.

Steven 16.02.2017 14:06

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

В комментариях к этот ответ есть некоторые обсуждения причин, по которым strerror не может быть потокобезопасным.

Carsten S 28.11.2016 16:26

@CarstenS Спасибо. Я все еще считаю, что локали - плохая причина для отказа от повторного входа. У каждой локали может быть свой собственный набор статических строк, доступных только для чтения. Я предполагаю, что кто-то может захотеть синтезировать локализованные результаты из общих частей, но выполнение такого рода дедупликации может показаться мне крайним случаем преждевременной оптимизации, и это исходит от преждевременного оптимизатора. К сожалению, нет смысла спорить со стандартами.

PSkocik 28.11.2016 17:47

Меня это тоже раздражает. Я думаю, что проблема, на которую намекают комментарии к ответу на этот другой вопрос, заключается в том, что локаль может быть деинсталлирована, пока у кого-то еще есть указатель на строку с ошибкой. Во всяком случае, тоже не мои решения;)

Carsten S 28.11.2016 20:49

@PSkocik - Что касается запуска C в сети, вы можете попробовать TIO или идеона.

owacoder 06.07.2019 04:48

Другие вопросы по теме