ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Segmentations
    Legacy/OS 2014. 12. 15. 01:20
    728x90

    Segmentation fault

    메모리 관리
    1) 세그멘테이션 : 메모리를 가변길이로 나누어 관리
    2) 페이징 : 메모리를 고정크기로 나누어 관리
    이렇게 2가지로 크게 나누어 볼수 있습니다.
    둘다에 대한 상세한 설명은 피하기로 하고 주관심사인 segmentation fault에 대해서만 언급하겠습니다.

    운영체제가 메모리를 관리할때 여러가지 작업을 수행해야 하며 특히, 메모리 보호를 하게 되어 있습니다.
    메모리 보호란, 각각의 프로세스가 자신이 소유한 메모리에 대해서만 액세스하도록 하는것입니다. 즉, 다른 프로세스(심지어는 운영체제영역)의 영역에는 침범할 수 없다는 것이지요. 마치 파일시스템에서 자기 소유가 아닌 디렉터리나 파일에는 쓰기, 읽기등이 안되거나 하는 것같은 메커니즘이지요. 
    A라는 프로그램이 메모리 영역 100번지에서 200번지 까지를 사용하도록 운영체제에 의해 허가되었다고 가정합시다(모든 메모리자원은 운영체제에게 할당받아 사용해야함) 그런데, A라는 프로그램내부에서 10번지나, 210번지등 자신의 메모리 영역이 아닌곳을 액세스하고자 하면, 운영체제는 해당 프로그램 즉 A를 죽여버립니다.(살벌하지요?) 왜 그러냐면 210번지를 B라는 다른 프로그램이 사용하고 있었다면, A는 B의 영역을 침범한것이 되지요? 이것을 운영체제 수준에서 막아주게 되어 있습니다. 어떻게요? 죽여버리지요.
    즉, 자신의 영역이 아닌 부분을 액세스(read또는 write)하려고 했기 때문입니다. 
    침범하도록 허용한다면? 문제가 있겠지요? A때문에 B도 죽을 수 있기 때문입니다. 만일 B 가 운영체제라면 시스템이 다운되겠지요? 구한말의 도스처럼.

    이렇게 어떤 프로그램이 자신이 운영체제로 부터 배정받지 못한 영역(메모리)에 대해 침범하는 것을 운영체제 수준에서 막아주는 것이 세그멘테이션 폴트에러 입니다.
    그럼 왜 이를 메모리 에러 등의 메시지로 안했냐구요? 이는 일종의 전통이라고 볼수 있습니다. 관습이랄까요? 최초 유닉스 설계자의 문구가 지금도 사용되는 것이라고 볼수 있습니다. 이를테면 core dump도 있는데, core메모리 이거 지금은 쓰는데가 있을라나? 다 RAM계열을 사용하지! 요즘 말로 이것도 바꿔본다면, 메모리덤프, 또는 RAM덤프등으로 써야 하겠지요?

    또 한가지, 요즘의 마이크로프로세서(하드웨어, CPU라고 흔히불리는...)들은 대부분 기본적으로 메모리 관리 메카니즘을 내장하고 있습니다. 인텔계열의 경우 386프로세서 이상에서 부터 지원되었지만요. 이를 운영체제 만드는 사람들이 이용하면 보다 쉽게 메모리 보호등 메모리 관리 루틴을 만들 수 있지요. 하드웨어가 메모리 보호를 지원하지 않아도 구현이야 할 수 있습니다만, 프로그램 수행이 좀 (아니 아주많이) 느려지겠지요? 

    끝으로, 프로그래머가 세그멘테이션 폴트를 발생시키려고 하지 않았는데, 발생하는 주 이유는 무엇일까?에 대해 말씀드리겠습니다.
    여러 이유가 있겠지만, 첫번째는 초기화 입니다.
    char *pointer; //알수없는 번지값이 들어가 있음, 무슨 값이 들어가 있는지는 며느리도 모름

    pointer = (char *)0;
    이런식으로 초기화 하지 않으면, pointer라는 변수에 이상한 값(즉, 번지)이 들어가게 되고 해당 번지는 알수 없는 번지가 되지요. 그리고는 그번지를 액세스하는 순간, 에러가 발생하겠지요?

    또다른 이유는 메모리 할당 성공 실패 검사입니다.
    메모리할당은 유닉스의 경우 실패할 경우가 거의 없겠지만, 그래도 메모리 할당후 검사는 필수적입니다.
    pointer = (char *)malloc(10);
    if ( pointer == (char *)0)
    {
    // 메모리 할당에러
    }
    이런 루틴을 추가하는 것은 기본입니다.

    또한가지는 할당한 메모리를 초과하는 영역을 액세스하는 경우입니다.
    10바이트 할당한 포인터에 다음과 같이 하는 경우이지요.
    *(pointer+100) = 'a';
    당근으로 죽겠지요.

    또다른 한가지는 배열에서도 발생할 수 있습니다.
    int a[10];

    a[100] = 8;
    물론 100이라는 값을 인위적으로 쓸리는 없지만, 루프를 사용하고 100이라는 index(subscript)를 변수에 넣어 사용할때는 흔히 실수가 나오기도 합니다.
    a[cnt++] = 8;
    모 이런식으로 하다보면 흔히 실수하지요.

    ============================================================================================================
    폴트에 대해 얘기가 빠졌네요.
    Fault란 무엇일까?
    사전적의미로 봐서는 안되고요. 용어로 보시면됩니다.
    Exception에는 Trap, Fault, Abort가 있습니다.
    1)trap : 현재 명령라인에서 다른 수행루틴을 호출
    2)fault : 현재 명령라인이 실패하면, 폴트처리루틴(fault handler)을 수행한후, 이전라인으로 복귀
    3)abort : 현재 명령라인이 실패하면 중지

    세그멘테이션 폴트 즉, 다른 프로세스 영역에 쓰기를 시도했다면, 쓰고 나서 죽어서는 안되겠지요? 쓰기 이전상태로 메모리는 보전되어야 하고, 그 후 죽거나 어떤 처리를 수행해야 겠지요? 그래서 폴트라는 것을 사용합니다.
    참고로, 폴트는 가상메모리(Virtual Memory Management)에서도 사용되는 메커니즘입니다.




    [펌] :  http://terapi.tistory.com/ 여기서 퍼왔습니다.^^

    'Legacy > OS' 카테고리의 다른 글

    Fragmentation, Paging, Segmentation  (0) 2014.12.15
Designed by Tistory.