programing

에러노 스레드는 안전합니까?

itsource 2022. 8. 17. 21:48
반응형

에러노 스레드는 안전합니까?

에서errno.h이 변수는 다음과 같이 선언됩니다.extern int errno;그래서 제 질문은, 이 모든 것을 체크하는 것이 안전한가입니다.errnoperror()를 사용합니다.나사산 안전 변수입니까?만약 그렇지 않다면, 그 대안은 무엇인가?

x86 아키텍처에서 gcc와 함께 Linux를 사용하고 있습니다.

네, 스레드 세이프입니다.리눅스의 경우 글로벌 errno 변수는 스레드마다 다릅니다.POSIX에서는 errno가 스레드 세이프여야 합니다.

http://www.unix.org/whitepapers/reentrant.html 를 참조해 주세요.

POSIX.1에서는 errno는 외부 글로벌 변수로 정의되어 있습니다.그러나 멀티스레드 환경에서는 이 정의를 사용할 경우 비결정적인 결과가 발생할 수 있기 때문에 이 정의를 사용할 수 없습니다.문제는 2개 이상의 스레드에 오류가 발생하여 모두 동일한 에러노를 설정할 수 있다는 것입니다.이 경우 스레드는 이미 다른 스레드에 의해 갱신된 후 errno를 체크하게 될 수 있습니다.

결과적으로 발생하는 비결정론을 피하기 위해 POSIX.1c는 다음과 같이 스레드 단위의 에러 번호에 액세스 할 수 있는 서비스로서 errno 를 재정의합니다(ISO/IEC 9945:1-1996, 「2.4」).

일부 함수는 errno 기호를 통해 액세스하는 변수에 오류 번호를 제공할 수 있습니다.기호 errno는 C 표준에서 지정된 대로 헤더를 포함하여 정의됩니다.프로세스의 각 스레드에 대해 errno 값은 함수 호출 또는 다른 스레드에 의한 errno 할당의 영향을 받지 않습니다.

http://linux.die.net/man/3/errno 도 참조해 주세요.

errno는 스레드 로컬입니다.한 스레드로 설정해도 다른 스레드 값은 영향을 받지 않습니다.

기계에서 간단한 프로그램을 실행하면 확인할 수 있습니다.

#include <stdio.h>                                                                                                                                             
#include <pthread.h>                                                                                                                                           
#include <errno.h>                                                                                                                                             
#define NTHREADS 5                                                                                                                                             
void *thread_function(void *);                                                                                                                                 

int                                                                                                                                                            
main()                                                                                                                                                         
{                                                                                                                                                              
   pthread_t thread_id[NTHREADS];                                                                                                                              
   int i, j;                                                                                                                                                   

   for(i=0; i < NTHREADS; i++)                                                                                                                                 
   {
      pthread_create( &thread_id[i], NULL, thread_function, NULL );                                                                                            
   }                                                                                                                                                           

   for(j=0; j < NTHREADS; j++)                                                                                                                                 
   {                                                                                                                                                           
      pthread_join( thread_id[j], NULL);                                                                                                                       
   }                                                                                                                                                           
   return 0;                                                                                                                                                   
}                                                                                                                                                              

void *thread_function(void *dummyPtr)                                                                                                                          
{                                                                                                                                                              
   printf("Thread number %ld addr(errno):%p\n", pthread_self(), &errno);                                                                                       
}

이 프로그램을 실행하면 각 스레드에서 다른 에러 주소를 확인할 수 있습니다.내 기계에서 실행한 결과 출력은 다음과 같습니다.

Thread number 140672336922368 addr(errno):0x7ff0d4ac0698                                                                                                       
Thread number 140672345315072 addr(errno):0x7ff0d52c1698                                                                                                       
Thread number 140672328529664 addr(errno):0x7ff0d42bf698                                                                                                       
Thread number 140672320136960 addr(errno):0x7ff0d3abe698                                                                                                       
Thread number 140672311744256 addr(errno):0x7ff0d32bd698 

주소는 모든 스레드에 대해 다릅니다.

, errno man 페이지 및 기타 응답에서 설명하듯이 errno는 스레드 로컬 변수입니다.

하지만, 쉽게 잊혀질 수 있는 우스꽝스러운 세부 사항이 있습니다.프로그램은 시스템 콜을 실행하는 모든 신호 핸들러의 에러노를 저장하고 복원해야 합니다.이는 신호가 값을 덮어쓸 수 있는 프로세스 스레드 중 하나에 의해 처리되기 때문입니다.

따라서 신호 핸들러는 errno를 저장하고 복원해야 합니다.예를 들어 다음과 같습니다.

void sig_alarm(int signo)
{
 int errno_save;

 errno_save = errno;

 //whatever with a system call

 errno = errno_save;
}

네.


Errno는 더 이상 단순한 변수가 아닙니다. 특히 스레드 세이프를 위해 배후에서 복잡합니다.

것은, 을 참조하십시오.$ man 3 errno:

ERRNO(3)                   Linux Programmer’s Manual                  ERRNO(3)

NAME
       errno - number of last error

SYNOPSIS
       #include <errno.h>

DESCRIPTION

      ...
       errno is defined by the ISO C standard to be  a  modifiable  lvalue  of
       type  int,  and  must not be explicitly declared; errno may be a macro.
       errno is thread-local; setting it in one thread  does  not  affect  its
       value in any other thread.

다시 확인할 수 있습니다.

$ cat > test.c
#include <errno.h>
f() { g(errno); }
$ cc -E test.c | grep ^f
f() { g((*__errno_location ())); }
$ 

은 거는에서 온 것입니다.<sys/errno.h> † Mac:

#include <sys/cdefs.h>
__BEGIN_DECLS
extern int * __error(void);
#define errno (*__error())
__END_DECLS

★★★★★★★★★★★★★★★★★.errno 함수가 되었습니다.__error()이 기능은 스레드 세이프가 되도록 구현되어 있습니다.

errno.h에서는 이 변수는 extern int errno로 선언됩니다.

C 표준에는 다음과 같이 기술되어 있습니다.

「」errno개체 식별자일 필요는 없습니다.호출에 값를 들어 "l" "d" 등될 수 .*errno()를 참조해 주세요.

「일부러」errno는 현재 스레드의 에러 번호 주소를 반환하는 함수를 호출하여 참조하는 매크로입니다.

Linux에서 사용하는 /usr/include/bits/errno는 다음과 같습니다.h:

/* Function to get address of global `errno' variable.  */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif

최종적으로, 다음과 같은 종류의 코드를 생성합니다.

> cat essai.c
#include <errno.h>

int
main(void)
{
    errno = 0;

    return 0;
}
> gcc -c -Wall -Wextra -pedantic essai.c
> objdump -d -M intel essai.o

essai.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
   0: 55                    push   ebp
   1: 89 e5                 mov    ebp,esp
   3: 83 e4 f0              and    esp,0xfffffff0
   6: e8 fc ff ff ff        call   7 <main+0x7>  ; get address of errno in EAX
   b: c7 00 00 00 00 00     mov    DWORD PTR [eax],0x0  ; store 0 in errno
  11: b8 00 00 00 00        mov    eax,0x0
  16: 89 ec                 mov    esp,ebp
  18: 5d                    pop    ebp
  19: c3                    ret

내 생각에 답은 "그것은 다르다"이다.스레드 세이프 C 런타임 라이브러리는 보통 올바른 플래그를 사용하여 스레드 코드를 빌드할 경우 함수 호출(함수로 매크로 확장)로서 errno를 구현합니다.

시스템에서 Unix를 사용하여 하는 -D_REENTRANT 할 수 있습니다.errno스레드 세이프입니다.

예를 들어 다음과 같습니다.

#if defined(_REENTRANT) || _POSIX_C_SOURCE - 0 >= 199506L
extern int *___errno();
#define errno (*(___errno()))
#else
extern int errno;
/* ANSI C++ requires that errno be a macro */
#if __cplusplus >= 199711L
#define errno errno
#endif
#endif  /* defined(_REENTRANT) */

언급URL : https://stackoverflow.com/questions/1694164/is-errno-thread-safe

반응형