4. 시스템 호출과 라이브러리 함수 에러처리 :: YJcode

거의 대부분의 시스템 호출과 라이브러리 함수가 어떤 형태로든 성공 여부를 나타내는 상태값을 리턴한다. 이 상태값은 해당 호출이 성공했는지를 알기 위해 언제나 확인해야한다. 만약 실패했다면 적절한 동작을 취해야 한다. 최소한 프로그램이 뭔가 기대하지 않은 일이 발생했음을 알리는 에러 메시지라도 표시해야 한다.

이런 확인을 생략함으로써 타이핑 시간을 절약하고 싶은 유혹에 빠리기 쉽지만, 그것은 결국 절약이 아니다.

'실패할 리 없는' 시스템 호출이나 라이브러리 함수의 상태값을 확인하지 않은 것 때문에 수많은 디버그 시간이 낭비될 수 있다.

 

 

  • 시스템 호출 에러 처리

 

각 시스템 호출의 메뉴얼 페이지에는 가능한 리턴값들이 나와 있고, 어느 값이 에러를 나타내는지도 나와 있다. 보통 -1을리턴하면 에러를 뜻한다. 따라서 시스템 호출은 다음과 같은 코드로 확인할 수 있다.

 

fd = open(pathname, flags, mode);
if(fd == -1) {
	//에러처리코드
}
. . .
if(close(fd) == -1) {
	//에러처리코드
}

 

시스템 호출이 실패하면 전역 정수 변수 errno를 특정 에러를 나타내는 양수로 설정한다. 헤더 파일 <errno.h>를 사용하면 errno 선언뿐만 아니라 다양한 에러 상수도 포함된다. 이 상수들의 이름은 모두 E로 시작한다. 각 메뉴얼 페이지의 errors 섹션에는 각 시스템 호출이 리턴할 수 있는 errno값의 목록이 나와 있다. 다음은 errno를 사용해서 시스템 호출 에러를 진단하는 간단한 예다.

 

cnt = read(fd, buf, numbytes);
if(cnt == -1) {
	if(errno == EINTR) {
    	fprintf(stderr, "read was interrupted by a signal \n);
    } else {
    	//기타 에러발생
    }
}

 

성공적인 시스템 호출과 라이브러리 함수는 절대 errno를 0으로 설정하지 않으므로, 이 변수는 이전의 함수 호출로 인해 0이 아닌 값을 갖고 있을 수도 있다. 더욱이 SUSv3는 성공적인 함수 호출이 errno를 0이 아닌 값으로 설정하는 것을 허용한다. 따라서 에러를 확인할 때는 언제나 함수의 리턴값이 에러를 나타내는지를 확인하고, 그 경우에만 errno를 통해 에러의 원인을 찾아야 한다.

몇몇 시스템 호출은 성공 시에도 -1을 리턴할 수 있다. 그런 호출에서 에러가 발생했는지를 알기 위해서는 호출 전에 errno를 0으로 설정하고, 호출 후에 다시 확인해야 한다. 호출이 -1을 리턴하고 errno가 0이 아니면, 에러가 발생한 것이다

시스템 호출이 실패한 뒤의 일반적인 동작은 errno 값에 따라 에러 메시지를 출력하는 것이다. 라이브러리 함수 perror()와 strerror()를 사용하면 편리하다.

 

#include <stdio.h>

void perror (const char *msg);

 

시스템 호출의 에러를 처리하는 단순한 방법은 다음과 같다.

 

fd = open(pathname, flags, mode);
if(fd == -1) {
	perror("open");
    exit(EXIT_FAILURE);
}

 

strerror() 함수는 인자 errnum으로 주어진 에러 번호에 해당하는 에러 문자열을 리턴한다.

 

#include <string.h>

char* strerror(int errnum);		//errnum에 해당하는 에러 문자열을 가리키는 포인터를 리턴

strerror()가 리턴한 문자열은 정적으로 할당되어 있을 수 있으므로, 이후의 strerror()에 의해 다른 값으로 바뀔 수 있다.

errnum이 알 수 없는 에러 번호를 담고 있으면, strerror() 는 Unknown errornnn 이라는 형태의 문자열을 리턴한다. 어떤 구현에서는 이런 경우 strerror()가 NULL을리턴하기도 한다.

perror()와 strerror() 함수가 로케일을 고려해서 동작하기 때문에 에러 설명이 현지어로 출력된다.

 

 

  • 라이브러리 함수의 에러처리

 

다양한 라이브러리 함수가 실패를 알리기 위해 여러가지 데이터형의 여러가지 값을 리턴한다. 여기서는 라이브러리 함수를 다름과 같이 분류해봤다.

 

  • 시스템 호출과 똑같이 에러 정보를 리턴하는 라이브러리 함수. -1을 리턴하고 errno에 구체적인 에러번호를 적는다. 이러한 함수의 예로는 파일이나 디렉토리를 삭제하는 remove() 가 있다. 이 함수의 에러는 시스템 호출의 에러와 똑같은 방법으로 진단할 수 있다.
  • 에러 발생 시 -1을 리턴하지는 않지만 errno에 구체적인 에러 번호를 적는 라이브러리 함수, 예를 들어, fopen()은 에러 발생 시 NULL포인터를 리턴하지만 하부의 시스템 호출 결과에 따라 errno를 설정한다. perror() 와 strerror() 함수도 이 에러를 진단할 때 쓸 수 있다.
  • errno를 전혀 쓰지 않는 나머지 라이브러리 함수. 에러의 발생 여부와 원인을 알아내는 방법은 함수마다 다르며 해당 함수의 메뉴얼 페이지에 적혀 있다. 이 함수의 경우 errno나, perror(), strerror()를 써서 에러를 진단하려고 하면 안된다.

+ Recent posts