segment fault
段錯誤是在編程報錯中經常出現的,特別是在c語言編程中,尤其常見,其原因本質上上是訪問了非法(不屬於這個程序)的內存地址空間,具體來說有以下幾種情況:
- 局部變量定義中,使用了過大的局部變量,大於了系統給之的棧(stack)的大小,因此報錯。比如以下代碼在linux環境下,就可能出現段錯誤報錯:
void foo(){
float vars[10000][10000];
}
代碼很簡單,就是在棧上劃出了一個很大的連續內存空間,翻譯成彙編如:
mov $100000016, %rsp
# 還有多出的16個字節是上下文切換需要的內存,frame pointer %rbp, return address等,
# 同時如果用gcc等編譯的,還要考慮其內存對齊的要求,即是其是16字節的倍數。
這個棧大小可能超出了系統給定每個程序的棧的大小,可以通過shell命令ulimit -s
進行查看系統給定的棧大小,比如筆者的就是:
user@ubuntu: ulimit -s
8192
注意這裏都是以1024字節(1KB)爲單位的,因此默認的就是8MB的棧大小,如果你的程序需要更大的棧空間,那麼可以通過
ulimit -s 1000000
類似這樣的命令去重定義最大的棧大小。
stack overflow
棧溢出
C語言是沒有對數組的邊界檢測的,這樣在實際應用中常常會導致越界的問題,我們知道,程序的棧如下圖所示:
考察以下代碼:
void foo(){
int vars[100];
vars[100] = 0;
vars[101] = 0;
vars[102] = 0;
vars[103] = 0;
}
當然,我們知道,我們對該數組vars
只能檢索到vars[99]
,因爲其索引範圍是[0,99]
,但是這種超出了理論上的索引邊界(越界)的“低級錯誤”確是在實際代碼中經常出現的bug的根源之一。從上圖的程序棧我們可以看出,如果棧中的變量邊界溢出,那麼可能會對一些上下文信息,比如return address進行期望之外的修改,導致難以預料的錯誤(比如無法返回到調用函數,或者返回到不該返回的地址,這裏容易被黑客進行棧溢出攻擊),因此操作系統一般會檢測這種段錯誤,同時報錯segment fault
。