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