淺說iOS為什么會上棧溢出

簡介

本文介紹了如下內(nèi)容

  • 棧的概念
  • 為什么會發(fā)生棧溢出
  • 棧溢出的幾種栗子
  • 怎么預(yù)防和發(fā)現(xiàn)棧溢出。

什么是棧?

  • 從數(shù)據(jù)結(jié)構(gòu)來說:棧(stack)又名堆棧,它是一種運算受限的線性表。限定僅在表尾進行插入和刪除操作的線性表。這一端被稱為棧頂,相對地,把另一端稱為棧底。向一個棧插入新元素又稱作進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素;從一個棧刪除元素又稱作出?;蛲藯?,它是把棧頂元素刪除掉,使其相鄰的元素成為新的棧頂元素。

  • 在計算機系統(tǒng)中:棧則是一個具有以上屬性的動態(tài)內(nèi)存區(qū)域。程序可以將數(shù)據(jù)壓入棧中,也可以將數(shù)據(jù)從棧頂彈出。在i386機器中,棧頂由稱為esp的寄存器進行定位。壓棧的操作使得棧頂?shù)牡刂窚p小,彈出的操作使得棧頂?shù)牡刂吩龃蟆?/p>

  • 棧在程序的運行中有著舉足輕重的作用。最重要的是棧保存了一個函數(shù)調(diào)用時所需要的維護信息,這常常稱之為堆棧幀或者活動記錄。堆棧幀一般包含如下幾方面的信息:

    • 臨時變量:包括函數(shù)的非靜態(tài)局部變量以及編譯器自動生成的其他臨時變量。
    • 函數(shù)的返回地址和參數(shù)

什么是棧溢出?

棧溢出就是緩沖區(qū)溢出的一種。 由于緩沖區(qū)溢出而使得有用的存儲單元被改寫,往往會引發(fā)不可預(yù)料的后果。程序在運行過程中,為了臨時存取數(shù)據(jù)的需要,一般都要分配一些內(nèi)存空間,通常稱這些空間為緩沖區(qū)。如果向緩沖區(qū)中寫入超過其本身長度的數(shù)據(jù),以致于緩沖區(qū)無法容納,就會造成緩沖區(qū)以外的存儲單元被改寫,這種現(xiàn)象就稱為緩沖區(qū)溢出。緩沖區(qū)長度一般與用戶自己定義的緩沖變量的類型有關(guān)。棧溢出就是緩沖區(qū)溢出的一種。

iOS/Mac棧的大小是多少?

  • iOS上主線程??臻g大小為1MB
  • iOS上子線程棧空間大小為512KB
  • Mac OS上主線程棧大小為8MB
  • 對于子線程,線程的棧大小是在線程創(chuàng)建的時候就創(chuàng)建好的,但是只有實際使用到的時候才會分配到具體內(nèi)存;同時,子線程能夠允許的最小棧大小為16KB,且棧的大小必須是4KB的整數(shù)倍。

哪些情況會造成棧溢出

  • 棧上變量直接分配內(nèi)存長度超過棧空間大小,如下
   int buf[1024*1024] = {0};

對應(yīng)的崩潰日志,一般情況下遇見有Stack Guard的關(guān)鍵字,就標明棧溢出了

image

  • 間接使用操作棧上內(nèi)存超限的函數(shù),包括但不限于以下函數(shù)
void *memcpy(void *__dst, const void *__src, size_t __n);
void *memmove(void *__dst, const void *__src, size_t __len);
char *strcpy(char *__dst, const char *__src);
char *strncpy(char *__dst, const char *__src, size_t __n);

舉個栗子:

void function1(char *str){
    int32_t maxsize = 100*1024*1024;
    char buffer[maxsize];
    //strcpy(buffer, str);
    memcpy(buffer, str, maxsize);
}

   char *a = malloc(1024*1024);
   function1(a);

來個崩潰日志,關(guān)鍵字還是Stack Guard哦。

image

  • 無限遞歸調(diào)用,見如下斐波那契數(shù)列函數(shù)遞歸實現(xiàn)
int  fibonacci(int n){
    if (n == 1) {
       return 1;
    }
    if (n == 2) {
        return 2;
    }
    return  fibonacci(n) * fibonacci(n - 1);
}

再來個崩潰日志給大家瞧瞧,不要錢哦,關(guān)鍵字還是Stack Guard

image

_

怎么避免棧溢出崩潰?

  • 棧上申請內(nèi)存不要超過512KB,建議超過100KB以上的內(nèi)存申請,都使用堆上的內(nèi)存分配方式,malloc,calloc
  • 使用操作內(nèi)存讀寫的系統(tǒng)函數(shù)時,保證大內(nèi)存的內(nèi)存操作在堆上進行
  • 避免使用遞歸,所有的遞歸都可以使用循環(huán)實現(xiàn)。即使不得不使用遞歸,也要對遞歸調(diào)用層次做優(yōu)化和控制(感謝@老青菜提出的寶貴意見)。

參考文獻

作者:落葉情思 鏈接:https://juejin.cn/post/6902668468991524871

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容