CS/UNIX

[Error Handling and Reporting] - UNIX errno 값

sliver__ 2022. 10. 10. 15:01
728x90

[The Old errno Value]

 

  • error code를 사용하기 위해서는 아래와 같이 사용한다.
extern int errno;

 

  • 아래는 "makefile" 이 존재하지 않는다면, errno를 참조한 뒤 "Makefile"을 open하는 예제이다.
  • ENOENT : No Such File or Directory
  • open 함수의 return이 error의 이유를 알려주지도 않고 errno라는 외부변수에 값이 적힌다.

 

#include <errno.h>                         /* Defines ENOENT */
extern int errno;                          /* Error code */
int fd;                                    /* File descriptor */

/* Attempt to open makefile */
if ( (fd = open("makefile",O_RDONLY)) == -1 ) {  /* Fail to open? */
    if ( errno == ENOENT )                 /* File does not exist? */
        fd = open("Makefile",O_RDONLY);    /* No, so try Makefile instead */
}

if ( fd == -1 ) {                          /* Did either open(2) fail? */
    /* Yes, report the open failure... */
    ...
}  else {
    /* makefile or Makefile is open on file unit fd */
}

 

[error code는 이름으로 판별]

 

  • 같은 error name macro가 다른 UNIX 플랫폼마다 값이 다를 수 있다. (Ex, ENOMSG is 83 for FreeBSD 3.4, 35 for HPUX, and 42 for Linux)
  • 그래서 define된 macro 이름으로 접근해야한다.
  • 아래의 헤더에서 사용한다.
#include <errno.h>

 

 

[errno 정확하게 사용하기]

  • errno는 함수가 성공하였다고 해서 0(for this convention means success)으로 값이 채워지지 않는다.
  • 실패했을 경우에만 에러값이 적힌다. 
  • 그러므로 이전 함수의 결과값이 적혀있다.
  • 함수 return 성공에 errno를 사용하려면 함수 call 이전에 0으로 초기화하여 사용한다
  • 아래의 경우 open이 실패했을 경우에 errno가 의미있는 경우이다.
extern int errno;              /* Old way of gaining access to errno */
int fd;                        /* File descriptor */

if ( (fd = open("makefile",O_RDONLY)) == -1 ) {
    /* Failed: errno holds a valid error code */
    ...
}  else {
    /* Success: fd holds file descriptor, and errno is meaningless here */
    ...
}

 

[The New errno Value]

 

  •  global 변수인 errno가 여러 thread가 접근하는 상황에서는 제대로 동작하지 않을 수도 있다.
  • global 변수로 사용되지 않고 errno.h에 각자 define되어 thread별로 사용하게 된다.

 

[errno 값 보고하기]

 

  • perror(3) 함수를 사용하여 errno 값에서 메시지를 생성하고 stderr에 보고한다.
  • 제공된 sys_errlist[] 메시지 배열을 사용한다.
  • strerror(3) 함수를 사용하여 함수 인수에 제공된 오류 코드에 대한 메시지를 반환합니다.

 

  • parameter로 오는 string을 stderr(2)에 적는다.
#include <stdio.h>

void perror(const char *s);

 

  • perror 예제는 아래와 같다.
  • errno에 발생한 에러의 macro를 할당하고 perror를 호출하는 시뮬레이션이다.
1:   #include <stdio.h>
2:   #include <errno.h>
3:
4:   int
5:   main(int argc,char **argv) {
6:
7:       errno = EIO;
8:       perror("Test EIO Message");
9:       return 0;
10:  }

 

  • 결과는 아래와 같다.
  • EIO <-> Input/output error와 매칭되어 출력되는것을 확인할 수 있다.
$ make perror
cc -c -D_POSIX_C_SOURCE=199309L -Wall perror.c
cc perror.o -o perror
$ ./perror
Test EIO Message: Input/output error
$

 

  • perror는 아래와 같은 경우에 사용할 수 없다
    • stderr에 출력하지 않는 경우
    • 로그 파일에 기록됨
    • X Window 팝업에 보고됨
    • 다른 형식으로 보고됨
    • 문자열로 저장됨
    • errno로 에러가 적히지 않는 경우

 

[sys_errlist[] 와 sys_nerr]

 

  • sys_errlist[]는 errno를 index로 사용하며 출력해야할 error message값이 들어있다.
  • sys_nerr는 sys_errlist의 range값이다.
#include <stdio.h>     /* Defines sys_errlist[] and sys_nerr */

extern const char *sys_errlist[];
extern const int sys_nerr;

errno = EIO;                    /* Simulate an error */
printf("The EIO Message is '%s'\n",sys_errlist[errno]);

FILE *fp = fopen(pathname,"r");   /* Attempt to open a file */

if ( !fp ) {                       /* Did the open fail? */
    fprintf(stderr,"%s: Unable to open %s for read.\n",
        sys_errlist[errno],       /* The error message text */
        pathname);                /* The file being opened */
    exit(13);
}

 

  • sys_errlist / sys_nerr 를 사용한 예제는 아래와 같다.
  • 정의된 sys_nerr로 범위값이 벗어나는 경우는 pathname을 출력
int fd;                         /* File descriptor */

fd = open(pathname,O_RDONLY);   /* Attempt to open for read */
if ( fd == -1 ) {               /* Did open(2) fail? */
    /* The open(2) call failed: */
    fprintf(stderr,"%s: opening %s for read\n",
        errno < sys_nerr ? sys_errlist[errno] : "?",
        pathname);

 

  • strerror는 아래와 같다.
#include <string.h>

char *strerror(int errnum);
  • strerror(3) 함수는 sys_errlist[] 배열이 제공하는 유연성을 제공하지만 변환되는 오류 코드에 대해 필요한 범위 검사도 수행합니다.
  • 오류 코드가 알려진 오류 코드 목록을 벗어나면 잘못된 포인터 대신 알 수 없는 오류 메시지가 반환됩니다.
  • sys_errlist / sys_nerr를 사용하는 것보다 안전하다. (range check 처리)
  • strerror 는 다음 strerror가 호출되기 이전까지 유효하다.
  • 아래는 유효하지않은 예시이다.
char *eptr1 = strerror(EIO);
char *eptr2 = strerror(ENOENT); /*** value of eptr1 is now invalid ***/

printf("Msg1='%s', msg2='%s'\n",eptr1,eptr2);       /*** INCORRECT ***/
  • etpr2의 strerror가 호출되는 순간 eptr1은 쓰레기값으로 채워진다.
728x90