35

The Linux Standard Base Core Specification 3.1と同様に、OpenGroup POSIX.1-2001 はstrerror_rを定義しています。しかし、エラー メッセージに合理的に期待できる最大サイズへの参照が見つかりません。コードに配置できる定義がどこかにあることを期待していましたが、見つけることができるものはありません。

コードはスレッドセーフである必要があります。これが、strerror ではなく 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;
}

ドキュメントから:

オープン グループ基本仕様第 6 号:

エラー

strerror_r() 関数は、次の場合に失敗する可能性があります:

  • [ERANGE]生成されたメッセージ文字列を格納するには、strerrbuf および buflen によって提供されたストレージが不十分でした。

ソースから:

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

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

3 に答える 3

13

静的制限を十分に大きくすることで、おそらくすべての状況に対応できます。本当にエラー メッセージ全体を取得する必要がある場合は、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;
}
于 2009-01-09T08:57:07.063 に答える
10

私はそれについて心配しません-256のバッファサイズは十分すぎるほどであり、1024はやり過ぎです。エラー文字列を保存する必要がある場合は、strerror()の代わりにを使用strerror_r()し、オプションで結果を使用できます。strdup()ただし、これはスレッドセーフではありません。スレッドセーフstrerror_r()の代わりに本当に使用する必要がある場合は、256のサイズを使用してください。では、最長のエラーメッセージ文字列は50文字です(「無効または不完全なマルチバイトまたはワイド文字」)。将来のエラーメッセージが大幅に長くなるとは思いません(最悪の場合、数バイト長くなります)。strerror()glibc-2.7

于 2009-01-08T04:46:25.510 に答える
5

このプログラム (オンラインで (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 での 2 つのパフォーマンスの違いは最小限です。

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いずれにせよ、スレッドアンセーフであることさえ許されているのは非常に奇妙だと思います。これらの返される文字列は、文字列リテラルへのポインターである必要があります。(教えてください。実行時に合成する必要があるケースは考えられません)。また、文字列リテラルは定義上読み取り専用であり、読み取り専用データへのアクセスは常にスレッド セーフです。

于 2016-07-20T13:13:30.187 に答える