LISTORY

[윈도우즈 시스템 프로그래밍] 구조적 예외처리(SEH) 기법(2) 본문

IT/윈도우 프로그래밍

[윈도우즈 시스템 프로그래밍] 구조적 예외처리(SEH) 기법(2)

LiStoryTeller 2018. 10. 20. 21:29


뇌를 자극하는 윈도우즈 시스템 프로그래밍 책 관련 유투브 강의 내용 정리이다.


저번 시간에 이어 구조적 예외처리(SEH) 기법에 대해 정리하겠다.


YouTube 주소 : 구조적 예외처리(SEH) 기법(2)



구조적 예외처리(SEH) 기법


⊙ SEH(Structured Exception Handler)


뜻은 구조적인 예외 핸들러이다.


보통 예외를 처리하는 함수를 가리켜 핸들러라고 한다.


그런데 여기서는 고유명사처럼 사용된다.


SEH는 윈도우즈의 예외처리 매커니즘이다. 즉, 함수를 이야기하는 것이 아니다.



예외 처리의 필요성


지극히 S/W적으로 설명해보겠다.


▶ 예외처리 전 코드



예외 처리 전 코드이다. 파일을 READ 모드로 오픈하려는 코드이다.


하지만 이 파일이 존재하지 않을 경우, 즉 예외적인 상황이 존재한다.


이는 발생할 수도 있고, 발생 빈도가 적을 수도 있고, 발생이 안할 수도 있다. 일반적이진 않을 것이다.


밑에는 메모리 공간을 할당받고 있다. 또, 할당이 제대로 됐는지 확인하고 있다.


또 데이터를 읽고 있다. 그리고 읽은 갯수가 10개가 되는지 안되는지 확인하고 있다.


여기서 보면, 예외적인 상황 3개를 보여주고 있다.


이 S/W를 코드적으로 보면 보기 거추장스럽다는 말이 나오기도 한다. 


다시 말하자면 프로그램의 실제 흐름과 예외처리 하는 부분을 나누고 싶어한다.


▶ 예외처리 후 코드



프로그램의 실제 흐름과 예외처리 하는 부분을 나누고 싶어한는 것이 SEH에 반영되었다.


프로그램의 흐름동안에 예외와 예외를 처리하는 영역을 완전히 나눌 수 있다.


보통 if문이 등장하면 예외인지 알고 건너 뛸 수 있을까? 그렇지 않다.


예외를 처리하는 용도의 if인지, 메인 로직인지 항상 확인해야 한다.


근데 완전히 나눠버리면 코드를 봤을 때 편하게 프로그램의 흐름을 정확히 파악할 수 있다.


보편적으로 C++, JAVA, C# 모두 SEH와 상당히 유사한 예외처리 매커니즘을 가지고 있다.


예외처리 매커니즘은 코드의 가독성에 많은 도움을 준다.



⊙ 예외와 에러의 차이점


S/W 개발 관점에서 예외와 에러는 차이가 있다.


보통은 혼용을 해서 쓴다. 둘다 오류이지만, 차이점은 있다.


컴파일 타임 오류 : 에러

-> 실수적인 성격이 강하다.


런타임 오류 : 일반적으로 예외

-> 예외는 처리되어야 한다.


런타임에 발생되는 오류는 일반적으로 예외라 하지만 두가지 경우가 있을 수 있다.


int *p;

...

*p = 100;


메모리 할당을 하지 않고 초기화를 하지 않는 실수를 했다. 이는 예외적인 상황이라 평가할 수 없다. 


이건 런타임에 발생하지만 에러이다.


이러한 상황이 아니라, 프로그램을 실행하다보면 S/W 가정을 벗어나 생기는 상황을 예외라고 한다.


좋은 프로그램은 이런 일반적이지 않은 상황, 예외가 발생했을 때 프로그램을 종료하는 것이 아니라 적절한 처리를 해주어야 한다.


우리는 예외를 어떻게 처리할 것인가, SEH를 이용하여 얼마나 부드럽게 처리할 것인가를 생각해야 한다.



⊙ 하드웨어 예외와 소프트웨어 예외


▶ 하드웨어 예외

- 하드웨어에서 문제시 삼는 상황

- 예로 0으로 정수를 나누는 행위

▶ 소프트웨어 예외

- 운영체제 개발자나, 프로그래머가 정의한 예외

▶ SEH 매커니즘 동작

- 소프트웨어 발생 시, Windows는 예외처리 매커니증 동작시킨다.

- 하드웨어 예외가 발생되었음에도 Windows에 의해 인식되어서 예외처리 매커니즘을 동작시킨다.




종료 핸들러(Termination Handler)


SEH 매커니즘은 어떻게 우리가 편하게 개발할 수 있을까에 대해 다양한 것들을 제공한다.


그래서 추가적으로 등장한게 종료핸들러이다.


이걸 이용하면 프로그램을 더 안정적으로 개발할 수 있다.



⊙ 종료 핸들러의 기본 구성과 동작 원리



위의 코드를 보면 __try, __finally 블럭이 있다.


이 두개의 의미는, __try 블럭에 일단 한번 들어오면, 빠져 나갈 때 무조건 __finally 블럭을 실행해라 하는 뜻이다.


프로그램 실제 개발은 오른쪽과 같다.


__try 블럭에 들어가 실행하면, 어떤 상황이 되도 __finally 블럭은 실행된다.


오른쪽에 __try 블럭을 보면 b가 0일 경우 프로그램을 종료시키겠다는 코드가 있다.


이 코드로 인해 프로그램이 끝나게 되는데, 그럼에도 불구하고 그 전에 __finally 블럭을 실행하게 된다.


그럼 이걸 어디다 쓸까? 예외적인 상황을 처리할 경우 쓰는 것이 아니다.


만일 메모리를 동적할당했는데, 중간에 문제가 생겨서 프로그램을 종료해야 한다.


그럼에도 불구하고 동적할당 해준 것을 반환해주어야 한다.


이러한 잔업무들, 파일을 닫아준다던가, 동적할당한 것을 반환한다던가 등 꼭 해야하는 업무를 __finally에 넣으면, 이 코드는 반드시 실행이 된다는 것이다.








Comments