programing

malloc()와 free()는 어떻게 동작합니까?

goodcopy 2022. 8. 12. 00:00
반응형

malloc()와 free()는 어떻게 동작합니까?

하는지 malloc ★★★★★★★★★★★★★★★★★」free을 하다

int main() {
    unsigned char *p = (unsigned char*)malloc(4*sizeof(unsigned char));
    memset(p,0,4);
    strcpy((char*)p,"abcdabcd"); // **deliberately storing 8bytes**
    cout << p;
    free(p); // Obvious Crash, but I need how it works and why crash.
    cout << p;
    return 0;
}

가능하다면 메모리 레벨의 깊이 있는 답변을 주시면 감사하겠습니다.

네, malloc에 대한 몇 가지 답변이 이미 게시되어 있습니다.

더 흥미로운 부분은 무료가 어떻게 작동하는가이다(그리고 이 방향에서는 malloc도 더 잘 이해할 수 있다).

많은 malloc/free 구현에서 free를 사용해도 일반적으로 운영체제로 메모리가 반환되지 않습니다(최소한 드문 경우만).그 이유는 힙에 갭이 생겨 2GB 또는 4GB의 가상 메모리를 갭으로 끝내기 때문입니다.가상 메모리가 종료되면 큰 문제가 발생하기 때문에 이 작업은 피해야 합니다.또 다른 이유는 OS가 처리할 수 있는 메모리 청크는 특정 크기 및 정렬된 메모리 청크뿐입니다.구체화: 보통 OS는 가상 메모리 매니저가 처리할 수 있는 블록(대부분 512바이트의 배수, 예를 들어 4KB)만 처리할 수 있습니다.

따라서 OS에 40바이트를 반환해도 동작하지 않습니다.그럼 자유란 무엇일까요?

Free는 메모리 블록을 자체 Free Block 목록에 추가합니다.통상, 주소 공간내의 인접 블록의 결합도 시도합니다.빈 블록 목록은 처음에 관리 데이터가 있는 메모리 청크의 순환 목록일 뿐입니다.이것이 표준 malloc/free로 매우 작은 메모리 요소를 관리하는 것이 효율적이지 않은 이유이기도 합니다.모든 메모리 청크에는 추가 데이터가 필요하며 크기가 작을수록 더 많은 조각화가 발생합니다.

프리리스트는 새로운 메모리 청크가 필요할 때 malloc에서 가장 먼저 검토하는 장소이기도 합니다.OS 로부터 새로운 메모리를 요구하기 전에 스캔 됩니다.필요한 메모리보다 큰 청크가 발견되면 두 부분으로 나뉩니다.1개는 발신자에게 반환되고, 다른 1개는 프리리스트로 돌아옵니다.

이 표준 동작에는 다양한 최적화가 있습니다(예를 들어 작은 메모리 청크).그러나 malloc와 free는 매우 보편적이어야 하므로, 대안을 사용할 수 없을 때 표준 동작은 항상 예비 동작입니다.프리리스트의 처리에도 최적화가 있습니다.예를 들어 청크를 사이즈별로 정렬된 리스트에 보존합니다.그러나 모든 최적화에는 고유한 한계가 있습니다.

코드가 크래쉬하는 이유:

그 이유는 4글자 크기의 영역에 9글자(말미의 늘 바이트를 잊지 마십시오)를 쓰면 데이터 청크의 '뒤'에 있는 다른 메모리 청크에 저장된 관리 데이터를 덮어쓸 수 있기 때문입니다(이 데이터는 대부분의 경우 메모리 청크의 '앞'에 저장되기 때문입니다.그 후 빈 청크를 빈 목록에 넣으려고 하면 이 관리 데이터에 닿아 덮어쓰기 포인터에 걸려 넘어질 수 있습니다.시스템이 크래시 됩니다.

이것은 다소 우아한 행동이다.또한 어딘가에 있는 런어웨이 포인터가 메모리 프리 리스트의 데이터를 덮어쓰고 시스템이 즉시 크래쉬하지 않고 나중에 서브루틴을 실행하는 경우도 볼 수 있었습니다.중간 정도의 복잡도의 시스템에서도 이러한 문제는 디버깅이 매우 어려울 수 있습니다.제가 관여한 한 사례에서는 메모리 덤프가 나타내는 것과는 전혀 다른 장소에 있었기 때문에 크래시의 원인을 찾는 데 며칠이 걸렸습니다.그것은 시한폭탄과 같다.다음 "무료"나 "말록"은 추락할 텐데 왜 그런지 모르잖아!

이것들은 최악의 C/C++ 문제의 일부이며, 포인터가 문제가 될 수 있는 이유 중 하나입니다.

이 포럼 스레드에서 aluser가 말한 바와 같이:

프로세스에는 주소 x에서 주소 y까지 힙이라고 하는 메모리 영역이 있습니다.malloc'd 데이터는 모두 이 영역에 있습니다.malloc()는 힙 내의 모든 빈 공간의 데이터 구조(예를 들어 목록)를 유지합니다.malloc을 호출하면 사용자가 사용할 수 있을 만큼 큰 덩어리가 목록에서 검색되고 포인터가 반환되며 더 이상 비어 있지 않다는 사실과 크기가 기록됩니다.같은 포인터로 free()를 호출하면 free()는 해당 청크의 크기를 검색하여 빈 청크() 목록에 추가합니다.malloc()를 호출하여 힙 내에서 충분한 크기의 청크를 찾을 수 없는 경우 brk() syscall을 사용하여 힙을 확장합니다.즉, 주소 y를 증가시키고 이전 y와 새 y 사이의 모든 주소를 유효한 메모리여야 합니다.brk()는 syscall이어야 하며 사용자 공간에서 동일한 작업을 수행할 수 있는 방법은 없습니다.

malloc()는 시스템/패킷에 의존하기 때문에 구체적인 답변을 드리기가 어렵습니다.단, 기본적으로는 할당되어 있는 메모리를 추적하고 할당되어 있는 방법에 따라서는 프리콜이 실패하거나 성공할 수 있습니다.

malloc() and free() don't work the same way on every O/S.

malloc/free의 1개의 실장은 다음과 같습니다.

  1. sbrk()(Unix 호출)를 통해 OS에서 메모리 블록을 가져옵니다.
  2. 크기, 권한, 다음 및 이전 블록 위치 등의 정보를 사용하여 해당 메모리 블록 주위에 머리글과 바닥글을 만듭니다.
  3. malloc 에의 콜이 착신하면, 적절한 사이즈의 블록을 포인트 하는 리스트가 참조됩니다.
  4. 그런 다음 이 블록이 반환되고 그에 따라 머리글과 바닥글이 업데이트됩니다.

메모리 보호에는 페이지 입도가 있어 커널 상호 작용이 필요합니다.

예제 코드는 기본적으로 예제 프로그램이 트랩되지 않는 이유를 묻습니다. 정답은 메모리 보호는 커널 기능으로 전체 페이지에만 적용되는 반면 메모리 할당기는 라이브러리 기능으로 관리됩니다.강제 적용 없이 임의의 크기의 블록이 생성되며, 이 블록은 페이지보다 훨씬 작을 수 있습니다.

메모리는, 사용의 프로그램으로부터 페이지 단위로 밖에 떼어낼 수 없습니다.이것마저도 관찰될 가능성은 거의 없습니다.

calloc(3) 및 malloc(3)는 필요에 따라 커널과 상호 작용하여 메모리를 가져옵니다.그러나 free(3)의 대부분의 구현에서는 메모리를 커널로1 되돌리는 것이 아니라 해방된 블록을 재사용하기 위해 나중에 참조하는 프리리스트에 메모리를 추가합니다.

free()가 시스템에 메모리를 반환하고 싶어도 커널이 실제로 영역을 보호하려면 적어도1개의 연속 메모리페이지가 필요합니다.따라서 작은 블록을 해제하면 페이지 내의 마지막 작은 블록일 경우에만 보호 변경이 이루어집니다.

네 블록이 저기 있고, 무료 목록에 있어.할당되어 있는 것처럼, 거의 항상 액세스 할 수 있고, 메모리 근처에도 액세스 할 수 있습니다.C는 머신 코드에 직접 컴파일하여 특별한 디버깅 준비가 없으면 로드 및 저장소의 건전성을 검사하지 않습니다.프리 블록에 액세스하려고 하면 라이브러리 구현자에게 부당한 요구를 하지 않기 위해 이 동작은 표준으로 정의되지 않습니다.할당된 블록 외부에서 해방된 메모리 또는 메모리에 액세스하려고 하면 다음과 같은 다양한 문제가 발생할 수 있습니다.

  • 할당자가 개별 메모리 블록을 유지하는 경우도 있고 블록의 바로 앞 또는 뒤에 할당하는 헤더를 사용하는 경우도 있지만("더 작은") 빈 목록을 함께 링크하기 위해 블록 내의 메모리를 사용하는 경우도 있습니다.이 경우 블록 읽기는 정상이지만 내용이 변경될 수 있으며, 블록에 쓰기를 하면 할당자가 잘못되거나 충돌할 수 있습니다.
  • 자연스럽게 블록은 나중에 할당될 수 있으며, 그 후 코드나 라이브러리 루틴에 의해 덮어쓰거나 calloc()에 의해 0으로 덮어쓰게 됩니다.
  • 블록이 재할당되면 블록의 크기가 변경될 수 있으며, 이 경우 다양한 위치에 더 많은 링크 또는 초기화가 작성됩니다.
  • 분명히, 프로그램의 커널을 알고 있는 세그먼트(segment)의 경계를 넘나드는 범위외의 것을 참조할 수 있습니다.이 경우는 트랩을 합니다.

운용 이론

따라서 malloc(3)는 예에서 전체적인 이론으로 거슬러 올라가면 필요할 때 커널에서 메모리를 얻습니다.일반적으로 페이지 단위입니다.이러한 페이지는 프로그램의 요구에 따라 분할 또는 통합됩니다.malloc와 free는 디렉토리 유지를 위해 협력합니다.큰 블록을 제공하기 위해 가능한 경우 인접 빈 블록을 병합합니다.디렉토리에서는, 프리 블록내의 메모리를 사용해 링크 리스트를 작성할 수도 있고, 그렇지 않을 수도 있습니다(대안은, 공유 메모리와 페이징 친화적인 것으로, 디렉토리 전용의 메모리를 할당하는 것입니다).malloc 및 free는 특별하고 옵션인 디버깅코드가 프로그램에 컴파일되어 있는 경우에도 개별 블록에 대한 접근을 강제할 수 있는 기능이 거의 없습니다.


1. free()의 구현이 시스템에 메모리를 되돌리려고 하는 것은 반드시 구현자의 게으름 때문만은 아닙니다.커널과의 상호작용은 단순히 라이브러리 코드를 실행하는 것보다 훨씬 느리고 이점은 작습니다.대부분의 프로그램은 메모리 설치 공간이 일정하거나 증가하므로 반환 가능한 메모리를 찾는 힙 분석 시간이 완전히 낭비됩니다.다른 이유로는 내부 플래그먼테이션으로 인해 페이지 정렬된 블록이 존재할 가능성이 거의 없으며 블록을 반환하면 블록이 어느 쪽으로든 조각화될 수 있습니다.마지막으로 대량의 메모리를 반환하는 소수의 프로그램은 malloc()를 바이패스하여 페이지를 할당하고 해방할 수 있습니다.

이론적으로 malloc은 이 어플리케이션의 운영체제에서 메모리를 가져옵니다.다만, 4바이트 밖에 필요로 하지 않고, OS는 페이지 단위로 동작할 필요가 있기 때문에(종종 4k), malloc는 그 이상의 기능을 합니다.페이지를 작성하고, 그 페이지에 자신의 정보를 격납하고, 그 페이지에 할당되어 해방된 정보를 추적할 수 있습니다.

예를 들어 4바이트를 할당하면 malloc은 4바이트에 대한 포인터를 제공합니다.4바이트 전 메모리는 malloc에 의해 할당된 모든 메모리의 체인을 만들기 위해 사용되고 있다는 것을 깨닫지 못할 수도 있습니다.무료 통화 시 포인터를 사용하여 데이터 위치로 백업한 후 해당 포인터로 작동합니다.

기억을 해방시키면, 말록이 그 기억 블록을 제거해주고...그 메모리를 operating system으로 되돌릴 수도 있고 그렇지 않을 수도 있습니다.이 경우 해당 메모리에 액세스할 수 없습니다.이 경우 해당 위치에 액세스할 수 있는 권한이 OS에 의해 사라지기 때문입니다.malloc가 메모리를 보관하고 있는 경우(그 페이지에 다른 것이 할당되어 있기 때문에 또는 최적화를 위해서), 액세스는 기능합니다.아직 틀리긴 하지만, 효과가 있을지도 몰라요.

면책사항: 제가 설명한 것은 malloc의 일반적인 구현이지만, 결코 가능한 유일한 구현은 아닙니다.

malloc()와 free()의 동작은 사용하는 런타임라이브러리에 따라 달라집니다.일반적으로 malloc()는 운영체제시스템에서 힙(메모리 블록)을 할당합니다.다음으로 malloc()에 대한 각 요구는 이 메모리의 작은 청크를 할당하여 포인터를 발신자에게 반환합니다.메모리 할당 루틴은 힙에서 사용된 메모리와 빈 메모리를 추적할 수 있도록 할당된 메모리 블록에 대한 추가 정보를 저장해야 합니다.이 정보는 종종 malloc()에 의해 반환되는 포인터 직전에 몇 바이트로 저장되며 메모리 블록의 링크 리스트가 될 수 있습니다.

malloc()에 의해 할당된 메모리블록을 덮어쓰면 사용되지 않는 메모리블럭의 다음 블록의 장부정보 중 일부가 파기될 가능성이 높습니다.

버퍼에 너무 많은 문자를 복사했을 때 프로그램도 크래쉬할 수 있습니다.추가 문자가 힙 외부에 있으면 존재하지 않는 메모리에 쓰려고 할 때 액세스 위반이 발생할 수 있습니다.

NUL 터미네이터 때문에 strcpy 행은 8바이트가 아닌9바이트를 저장하려고 합니다.정의되지 않은 동작을 호출합니다.

무료 호출이 크래시되거나 크래시되지 않을 수 있습니다.C 또는 C++의 실장에서는, 할당의 4 바이트 후에 메모리가 다른 용도로 사용되는 경우가 있습니다.만약 그것이 다른 것에 사용된다면, 그 위에 온통 낙서를 하면 그 "다른 것"이 잘못될 수 있지만, 만약 그것이 다른 어떤 것에 사용되지 않는다면, 당신은 그것을 피할 수 있을 것이다."Greating this"는 좋게 들릴 수 있지만 실제로는 좋지 않습니다. 왜냐하면 코드가 정상적으로 실행된다는 것을 의미하기 때문입니다. 하지만 향후 실행에서는 이 문제를 해결하지 못할 수도 있습니다.

디버깅 스타일의 메모리 할당기를 사용하면 특별한 가드 값이 입력되어 있으며, 이 값이 검출되지 않을 경우 해당 값이 프리 체크되고 패닉 상태가 될 수 있습니다.

그렇지 않으면 다음 5바이트에는 아직 할당되지 않은 다른 메모리블록에 속하는 링크노드의 일부가 포함될 수 있습니다.블록을 해제하려면 블록을 사용 가능한 블록 목록에 추가해야 하며, 목록 노드에 낙서를 했기 때문에 해당 작업이 잘못된 값으로 포인터를 참조 해제하여 크래시를 일으킬 수 있습니다.

이 모든 것은 메모리 할당자에 따라 다릅니다.실장마다 사용하는 메커니즘이 다릅니다.

malloc 및 free는 구현에 따라 달라집니다.일반적인 구현에서는 사용 가능한 메모리를 사용 가능한 메모리 블록의 링크 목록인 "사용 가능한 목록"으로 파티셔닝합니다.많은 구현에서는 이를 작은 개체와 큰 개체로 인위적으로 나눕니다.빈 블록은 메모리 블록의 크기와 다음 블록의 위치 등에 대한 정보에서 시작됩니다.

malloc을 실행하면 블록이 프리 리스트에서 꺼집니다.해방되면 블록은 해방 리스트로 돌아갑니다.포인터의 끝을 덮어쓸 때 빈 목록의 블록 머리글에 쓰는 것일 수 있습니다.메모리를 해방하면 free()는 다음 블록을 조사하려고 하며 포인터에 부딪혀 버스 오류가 발생할 수 있습니다.

이것은 malloc와 free와는 특별히 관계가 없습니다.문자열을 복사한 후 프로그램이 정의되지 않은 동작을 보여 줍니다. 이 경우 해당 시점 또는 이후 언제든지 충돌할 수 있습니다.이는 malloc 및 free를 사용하지 않고 스택 또는 정적으로 char 배열을 할당한 경우에도 해당됩니다.

메모리 할당기의 구현과 OS에 따라 달라집니다.

예를 들어 윈도에서는 프로세스가 RAM을 1페이지 이상 요구할 수 있습니다.그런 다음 OS는 이러한 페이지를 프로세스에 할당합니다.단, 어플리케이션에 할당되어 있는 메모리는 아닙니다.CRT 메모리 할당자는 메모리를 연속된 "사용 가능한" 블록으로 표시합니다.그런 다음 CRT 메모리 할당자가 사용 가능한 최소 블록 목록을 실행하고 사용할 수 있는 최소 블록을 찾습니다.그런 다음 필요한 만큼 블록을 가져와 "할당" 목록에 추가합니다.실제 메모리 할당의 선두에는 헤더가 부착됩니다.이 헤더에는 다양한 비트의 정보가 포함되어 있습니다(예를 들어 링크 목록을 형성하기 위해 할당된 다음 블록과 이전 블록을 포함할 수 있습니다).대부분의 경우 할당 크기가 포함됩니다).

Free는 헤더를 삭제하고 빈 메모리 목록에 다시 추가합니다.만약 그것이 주변의 자유 블록과 더 큰 블록을 형성한다면, 이것들은 더 큰 블록을 만들기 위해 함께 추가됩니다.현재 페이지 전체가 비어 있는 경우, 대부분의 경우 할당자는 페이지를 OS로 되돌립니다.

그것은 간단한 문제가 아니다.OS 할당자 부분이 완전히 통제 불능입니다.Doug Lea's Malloc(DLMalloc)와 같은 것을 읽고 꽤 빠른 할당자가 어떻게 작동하는지 이해하는 것이 좋습니다.

편집: 할당량보다 큰 메모리 헤더를 쓰기 때문에 다음 메모리 헤더를 덮어쓰기하기 때문에 크래시가 발생합니다.이렇게 하면 해방되는 것이 정확히 무엇이고 어떻게 다음 블록으로 병합해야 하는지 매우 혼란스러워집니다.이것이 항상 무료에서 바로 충돌의 원인이 되는 것은 아닙니다.나중에 크래시가 발생할 수 있습니다.일반적으로 메모리 덮어쓰기를 피하십시오!

사용자의 것이 아닌 메모리를 사용했기 때문에 프로그램이 크래시됩니다.다른 사람이 사용할 수도 있고 그렇지 않을 수도 있습니다. 운이 좋으면 충돌할 수도 있고, 그렇지 않을 경우 문제가 오랫동안 숨겨져 있다가 나중에 다시 돌아와 여러분을 물 수도 있습니다.

malloc/무료 구현에 관한 한 모든 책이 이 주제에 전념하고 있습니다.기본적으로 할당자는 OS에서 더 큰 메모리 덩어리를 가져와 관리합니다.할당자가 대처해야 할 문제의 일부는 다음과 같습니다.

  • How to get new memory
  • How to store it - ( list or other structure, multiple lists for memory chunks of different size, and so on )
  • What to do if the user requests more memory than currently available ( request more memory from OS, join some of the existing blocks, how to join them exactly, ... )
  • What to do when the user frees memory
  • Debug allocators may give you bigger chunk that you requested and fill it some byte pattern, when you free the memory the allocator can check if wrote outside of the block ( which is probably happening in your case) ...

It's hard to say because the actual behaviour is different between different compilers/runtimes. Even debug/release builds have different behaviour. Debug builds of VS2005 will insert markers between allocations to detect memory corruption, so instead of a crash, it will assert in free().

프로그램 브레이크 포인터를 단순히 움직이면brk그리고.sbrk실제로 메모리를 할당하는 것이 아니라 주소 공간만 설정합니다.예를 들어 Linux에서는 주소 범위에 액세스할 때 실제 물리적 페이지에 의해 메모리가 "백업"되어 페이지 장애가 발생하고 커널이 페이지 할당자를 호출하여 백업 페이지를 얻습니다.

ReferenceURL : https://stackoverflow.com/questions/1119134/how-do-malloc-and-free-work

반응형