strerror執行緒安全分析

一見藍天發表於2014-04-16
img_e25d4fb2f8de1caf41a735ec53088516.pngstrerror執行緒安全分析.pdf

導讀

strerror是否執行緒安全了? 1

errno是否執行緒安全? 1

1strerror原始碼 2

2__strerror_r原始碼 2

 

strerror是否執行緒安全了?

答案是NO,但它有個執行緒安全的版本:strerror_r。藉助Linuxman,即可看到詳情:

#include 

char *strerror(int errnum);

int strerror_r(int errnum, char *buf, size_t buflen); /* GNU-specific */

 

那麼,在多執行緒中使用strerror是否安全了?答案是不一定,一定情況下也是非常安全的。

不安全會造成記憶體違規訪問嗎?也就是會發生SIGSEGV嗎?答案是NO,仍是記憶體安全的,但是可能會返回錯亂的字串。

 

那麼,在多執行緒程式中,什麼情況下使用strerror是絕對安全的了?如果引數errnum是一個已知的errno,則使用strerror是絕對安全的,也就是會返回期待的字串,而不會出現亂碼

對比strerror原始碼,是因為strerror會在下述直接返回:

  if (__builtin_expect (ret != NULL, 1))

    return ret;

而這走的是_strerror_r中的分支:

return (char *) _(_sys_errlist_internal[errnum]);

errno是否執行緒安全?

答案是不一定,取決於編譯巨集:

#  if !defined _LIBC || defined _LIBC_REENTRANT

/* When using threads, errno is a per-thread value.  */

#   define errno (*__errno_location ())

#  endif

 

可以通過下段小程式碼,來確定預設時是否有定義,如果沒有,則需要在編譯時加上:

#include 

 

int main()

{

        #ifdef _GNU_SOURCE

                printf(“_GNU_SOURCE defined
“);

        #else

                printf(“_GNU_SOURCE not defined
“);

        #endif

 

        #ifdef _LIBC_REENTRANT

                printf(“_LIBC_REENTRANT defined
“);

        #else

                printf(“_LIBC_REENTRANT not defined
“);

        #endif

 

        #ifdef _LIBC

                printf(“_LIBC defined
“);

        #else

                printf(“_LIBC not defined
“);

        #endif

 

        return 0;

}

 

1strerror原始碼

// glibc-2.14stringstrerror.c

#include 

 

/* Return a string describing the errno code in ERRNUM.

   The storage is good only until the next call to strerror.

   Writing to the storage causes undefined behavior.  */

libc_freeres_ptr (static char *buf);

 

char *

strerror (errnum)

     int errnum;

{

  char *ret = __strerror_r (errnum, NULL, 0);

  int saved_errno;

 

  if (__builtin_expect (ret != NULL, 1))

    return ret;

 

  saved_errno = errno;

  if (buf == NULL)

    buf = malloc (1024);

  __set_errno (saved_errno);

  if (buf == NULL)

    return _(“Unknown error”);

  return __strerror_r (errnum, buf, 1024);

}

2__strerror_r原始碼

// glibc-2.14string\_strerror.c

/* Return a string describing the errno code in ERRNUM.  */

char *

__strerror_r (int errnum, char *buf, size_t buflen)

{

  if (__builtin_expect (errnum 0 || errnum >= _sys_nerr_internal

|| _sys_errlist_internal[errnum] == NULL, 0))

    {

      /* Buffer we use to print the number in.  For a maximum size for

 `int` of 8 bytes we never need more than 20 digits.  */

      char numbuf[21];

      const char *unk = _(“Unknown error “);

      size_t unklen = strlen (unk);

      char *p, *q;

      bool negative = errnum 

 

      numbuf[20] = ` `;

      p = _itoa_word (abs (errnum), &numbuf[20], 10, 0);

 

      /* Now construct the result while taking care for the destination

 buffer size.  */

      q = __mempcpy (buf, unk, MIN (unklen, buflen));

      if (negative && unklen 

{

  *q++ = `-`;

  ++unklen;

}

      if (unklen 

memcpy (q, p, MIN ((size_t) (&numbuf[21] – p), buflen – unklen));

 

      /* Terminate the string in any case.  */

      if (buflen > 0)

buf[buflen – 1] = ` `;

 

      return buf;

    }

 

  return (char *) _(_sys_errlist_internal[errnum]);

}

weak_alias (__strerror_r, strerror_r)

libc_hidden_def (__strerror_r)

 

 


相關文章