Translate

2024/05/22

segmentation fault

segmentation fault

segmentation1 fault2는 다른 프로세스의 메모리 영역에 쓰기 시도를 하다 실패하여 그 이전상태로 메모리를 보전하고 죽거나 어떤 처리를 수행했다는 의미이다. 특히, C/C++와 같은 언어를 다루다 보면 메모리를 다루는 과정에서 segmentation fault를 흔하게 볼 수 있다. 대부분의 경우, 코드 작성자의 잘못으로 잘못된 크기의 배열이나 포인터를 선언하고 해제하거나, NULL값을 조회하는 경우 발생하며, 간단히 말하면 잘못된 메모리 영역을 참조하기 때문에 발생 한다.

segmentation fault의 원인을 알아보고, 간단한 해결방법에 대해 소개하고자 한다.



segmentation fault 원인

보다 자세히 설명을 하면 운영체제가 메모리를 관리할 때는 '메모리 보호’라는 기법을 사용하는데, '메모리 보호’란 각각의 프로세스 마다 자신이 소유한 메모리 영역만을 엑세스할 수 있고, 다른 프로세스의 메모리 영역에는 침범할 수 없도록 하는 기법이다.

이러한 이유로 **어떤 프로세스가 운영체제로부터 자신의 메모리 영역이 아닌 배정 받지 않은 메모리 영역을 침범하는 것을 운영체제에서 막아주는 것을 세그먼테이션 폴트(segmentation fault)**라고 한다.

  • Initialization of pointer : 메모리 번지 초기화 必
  • Array out of bound index
  • Checking malloc of memory : 메모리 할당 후 반드시 검사 필요. ex) run with NULL FILE pointer
  • limit of stack size : 할당한 메모리 영역을 초과하는 경우
  • Function prototyping : 함수 선언, 변수 등으로
  • Compiler Optimization


해결


1. 디버깅으로 해결

보통은 디버깅을 하는 과정에서 자신의 실수를 발견하고 이를 해결하면 된다.
gdb, valgrind와 같은 디버깅툴을 사용하면 좋지만 잘 모르겠다면, printf 를 사용하면 된다.

printf를 코드 이곳저곳에 넣고 에러가 발생하는 부분을 영역을 좁혀가며 찾는 것이다.

많이 발생하는 부분은,

  • 보통은 free와 같이 메모리를 해제할 경우 발생하는 경우가 많다.
  • 반복문 안에서 배열의 크기보다 큰 영역을 조회할 경우 (비슷하게 배열을 너무 작게 잡은 경우)
  • NULL값인 변수를 조회할 경우

가 있다.


2. Stack 크기를 조절하여 해결

1번의 경우 코드 작성자가 문제였지만, 코드엔 문제가 없지만 에러가 발생하는 경우도 있다. 원인은 바로…
동적 배열로 작성하지 않고, 정적 배열로 큰 배열로 선언하는 경우이다.

double arr3D[10][2048][2048];

int main(void) {
   printf("Hello, World~");
   return 0;
}

위 코드는 예시로 작성한 내용이다. 이런 식으로 3차원 배열을 크게 잡은 경우 코드 상에서는 문제가 없기 때문에 컴파일도 정상적으로 가능하지만 실행 시 segmentation fault가 발생할 수 있다. stack 크기와 관련된 문제로 stack의 크기를 초과해 변수가 선언되었기 때문에 발생하는 문제이다. 시스템 상에서 아래 명령어로 그 크기를 확인할 수 있다.

$ ulimit -a
...
stack size                  (kbytes, -s) 8192
...

가장 좋은 방법은 정적으로 선언된 배열을 동적으로 할당 하여 필요할 때마다 선언하고 해제하는 방법이고, 차선(좋지 않은 방법)으로는 stack의 크기를 바꾸는 것이다.

# ulimit -s unlimited 

와 같이 크기를 무제한으로 설정하면 된다. (다시 말하지만 좋은 방법은 아니다)



결론

segmentation fault는 프로그램이 허용된 메모리 영역을 벗어나 다른 프로세스의 메모리에 접근하려 할 때 운영체제에서 발생시키는 오류입니다. 이는 메모리 보호 기법에 의해 발생하며, 주요 원인으로는 포인터 초기화 실패, 배열 범위 벗어남, 메모리 할당 실패, 스택 크기 초과 등이 있습니다.
해결을 위해서는 먼저 디버깅 과정에서 printf 등으로 오류 지점을 찾아내는 것이 중요합니다. gdb, valgrind와 같은 디버거 툴을 사용하면 더 수월합니다.
또한 코드상 문제가 없는데도 발생하는 경우에는 스택 크기를 확인하고 필요시 ulimit 명령어로 스택 크기를 늘리는 것도 해결책이 될 수 있습니다. 하지만 근본적인 해결을 위해서는 대규모 배열을 동적 할당하는 것이 바람직합니다.
segmentation fault는 메모리 관리 오류로 인해 빈번히 발생하므로, 디버깅과 메모리 동적 할당 등의 노력으로 제대로 해결해야 합니다.



참고

https://adnoctum.tistory.com/387
http://ddiri01.tistory.com/75
http://heart4u.co.kr/tblog/357


  1. 메모리 관리의 종류로 메모리를 가변길이로 나누어 관리. (페이징: 메모리를 고정크기로 나누어 관리) ↩︎
  2. Exeption의 종류로 현재 명령라인이 실패하면, 폴트처리루틴(fault handler)을 수행한 후, 이전라인으로 복귀. (trap: 현재 명령라인에서 다른 수행루틴을 호출, abort: 현재 명령라인이 실패하면 중지) ↩︎

댓글 없음:

댓글 쓰기

Template by Aliya H.