Segmentation Fault

프로그램이 허용되지 않은 메모리 영역에 접근을 시도하거나, 허용되지 않은 방법으로 메모리 영역에 접근을 시도할 때 발생한다. OS에서 메모리를 보호하기 위해서 사용하는 기법 중에 하나로, 컴파일은 잘 되지만 실행에서 오류가 발생한다.

  • 이미 메모리 해제된 변수 영역에 접근하는 경우
  • Read-only로 설정된 메모리 영역에 작성하려고 하는 경우
  • NULL 값을 가리키는 포인터에 접근하는 경우

등이 있다. 하지만 내 에러 같은 경우에는 위의 사항에 포함되지 않는 것 같았다. 메모리를 해제하지 않았으며 Read-only로 작성되지 않았고 NULL 값을 가르키지도 않는다고 생각했다. 구글링하여 찾은 해결법을 다 적용해보아도 에러를 해결할 수 없었다. 해결법을 찾지 못하던 와중에 SegFault를 발생시켰던 더블 포인터 변수를 동적 할당이 아닌 정적 할당으로 사용하면 에러가 발생하지 않음에 실마리를 얻었다.


메모리 영역

Code

기계어를 포함한 작성한 코드가 들어가는 부분으로, CPU는 코드 영역에 저장된 명령어를 가져가서 처리한다. Read-only 부분이기 때문에 쓰기 작업이 불가능하다.

Data

프로그램의 전역 변수와 정적 변수가 저장되는 영역으로, 프로그램 시작과 함께 할당되며 프로그램 종료 시 소멸된다.

Stack

지역 변수와 매개 변수가 저장된다. 함수의 호출과 함께 할당되며 함수의 호출이 종료됨과 동시에 소멸한다.

Heap

사용자에 의해 동적으로 메모리 공간이 할당되고 해제된다. C언어에선 malloc()free()를 이용한다.

위의 구조가 흔히 알고 있는 메모리 영역이지만 하나를 더 알아야 한다.

BSS

  • 초기값 없는 전역변수, 배열
  • Static으로 선언된 변수

초기화가 0 또는 NULL로 되지 않으면 Data 영역에 저장된다. 이렇게 나눠서 관리하는 이유는 프로그램 크기를 작게 만들기 위해서다. BSS는 loader에 의해 0으로 초기화된다.


Static

  • 프로그램이 시작될 때 할당되고 프로그램이 종료될 때 소멸됨
  • 초기화하지 않아도 0으로 초기화
  • 프로그램이 끝날 때까지 메모리에 남아있음

결론은, 전역변수로 선언되었던 더블포인터 변수에 static을 붙임으로써 에러를 해결할 수 있었다. static 변수를 사용하여 선언한 변수가 BSS에 들어가 0으로 초기화가 된다. static을 사용하지 않았을 때는 동일한 변수가 Stack에 할당되는데, 초기화되지 않은 스택 변수는 임의의 값을 포함할 수 있고 이 변수에 접근했기 때문에 에러가 발생한 것이었다. 즉, 선언한 변수를 초기화하지 않아서 발생한 문제였고 static을 사용하지 않고 선언 시 NULL 로초기화를 해주어도 에러가 발생하지 않았다. 포인터 변수이고, 동적 할당 후에 0으로 초기화를 해주었기 때문에 초기화 문제때문이라고 생각하지 못했다. 아무튼 덕분에 메모리 구조도 공부하고 Seg Fault 발생 위치를 찾기 위해서 GDB도 사용해볼 수 있는 좋은 기회였다.


Reference