strerror线程安全分析

106 篇文章 2 订阅

导读

strerror是否线程安全了? 1

errno是否线程安全? 1

1strerror源码 2

2__strerror_r源码 2

 

strerror是否线程安全了?

答案是NO,但它有个线程安全的版本:strerror_r。借助Linuxman,即可看到详情:

#include <string.h>

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 <stdio.h>

 

int main()

{

        #ifdef _GNU_SOURCE

                printf("_GNU_SOURCE defined\n");

        #else

                printf("_GNU_SOURCE not defined\n");

        #endif

 

        #ifdef _LIBC_REENTRANT

                printf("_LIBC_REENTRANT defined\n");

        #else

                printf("_LIBC_REENTRANT not defined\n");

        #endif

 

        #ifdef _LIBC

                printf("_LIBC defined\n");

        #else

                printf("_LIBC not defined\n");

        #endif

 

        return 0;

}

 

1strerror源码

// glibc-2.14\string\strerror.c

#include <errno.h>

 

/* 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.14\string\_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 < 0;

 

      numbuf[20] = '\0';

      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 < buflen)

{

  *q++ = '-';

  ++unklen;

}

      if (unklen < buflen)

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

 

      /* Terminate the string in any case.  */

      if (buflen > 0)

buf[buflen - 1] = '\0';

 

      return buf;

    }

 

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

}

weak_alias (__strerror_r, strerror_r)

libc_hidden_def (__strerror_r)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Windows 平台上,strerror 函数不是标准 C 函数,而是 Windows API 中的一个函数。 在 Windows API 中,strerror 函数被称为 FormatMessage 函数,它可以将系统错误代码转换为相应的错误消息。FormatMessage 函数的语法如下: ```c++ DWORD WINAPI FormatMessage( _In_ DWORD dwFlags, _In_opt_ LPCVOID lpSource, _In_ DWORD dwMessageId, _In_ DWORD dwLanguageId, _Out_ LPWSTR lpBuffer, _In_ DWORD nSize, _In_opt_ va_list *Arguments ); ``` 其中,dwFlags 参数可以是以下值之一: - FORMAT_MESSAGE_ALLOCATE_BUFFER:函数将为输出消息分配内存。调用者必须使用 LocalFree 函数释放返回的缓冲区。 - FORMAT_MESSAGE_FROM_SYSTEM:函数将从系统消息表中查找消息。 - FORMAT_MESSAGE_IGNORE_INSERTS:函数将忽略 Arguments 参数。 dwMessageId 参数指定要获取的消息的标识符。可以使用 GetLastError 函数获取最近一次发生的错误代码,并将其传递给 FormatMessage 函数。 lpBuffer 参数指向要接收消息字符串的缓冲区。如果 dwFlags 参数设置为 FORMAT_MESSAGE_ALLOCATE_BUFFER,则 lpBuffer 必须是一个 LPWSTR 指针,指向指向接收缓冲区的指针。在这种情况下,函数将为消息字符串分配内存,并将指针存储在 lpBuffer 中。 nSize 参数指定缓冲区的大小,以字符为单位。 如果函数成功,则返回写入缓冲区的字符数(不包括空终止符)。如果函数失败,则返回 0。 下面是一个简单的示例,展示如何使用 FormatMessage 函数将错误代码转换为错误消息: ```c++ #include <windows.h> #include <iostream> int main() { DWORD errorCode = GetLastError(); LPWSTR errorBuffer = nullptr; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&errorBuffer, 0, nullptr); std::wcout << L"Error message: " << errorBuffer << std::endl; LocalFree(errorBuffer); return 0; } ``` 在上面的示例中,我们首先使用 GetLastError 函数获取最近一次发生的错误代码。然后,我们使用 FormatMessage 函数将错误代码转换为错误消息,并将其输出到控制台。最后,我们使用 LocalFree 函数释放返回的缓冲区。 希望这个简单的示例可以帮助您了解在 Windows 平台上将错误代码转换为错误消息的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值