深入剖析Sgementation fault原理前言我們在日常的編程當中,我們很容易遇到的一個程序崩潰的錯誤就是segmentation fault,在本篇文章當中將主要分析段錯誤發(fā)生的原因!
Sgementation fault發(fā)生的原因發(fā)生Sgementation fault的直接原因是,程序收到一個來自內(nèi)核的SIGSEGV信號,如果是你的程序?qū)е碌膬?nèi)核給進程發(fā)送這個信號的話,那么就是你的程序正在讀或者寫一個沒有分配的頁面或者你沒有讀或者寫的權(quán)限 。這個信號的來源有兩個:
- 程序的非法訪問,自身程序的指令導致的Sgementation fault 。
- 另外一種是由別的程序直接發(fā)送SIGSEGV信號給這個進程 。
1) SIGHUP2) SIGINT3) SIGQUIT4) SIGILL5) SIGTRAP 6) SIGABRT7) SIGBUS8) SIGFPE9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+338) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+843) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+1348) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-1253) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-758) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-263) SIGRTMAX-1 64) SIGRTMAX當一個程序發(fā)生 segmentation fault 的時候,這個程序的退出碼 exitcode 等于 139!
文章插圖
發(fā)生 segmentation fault 的一個主要的原因是我們自己的程序發(fā)生非法訪問內(nèi)存,同時別的程序給這個進程發(fā)送 SIGSGEV 信號也會導致我們的程序發(fā)生 segmentation fault 錯誤 。
比如下面的程序就是自己發(fā)生的段錯誤(發(fā)生了越界訪問):
#include <stdio.h>int main() {int arr[10];arr[1 << 20] = 100; // 會導致 segmentation faultprintf("arr[12] = %d\n", arr[1 << 20]); // 會導致 segmentation faultreturn 0;}下面是一個別的程序給其他程序發(fā)送SIGSGEV信號會導致其他進程出現(xiàn)段錯誤(下面的終端給上面終端的進程號等于504092的程序發(fā)送了一個信號值等于11(就是SIGGSGEV)信號,讓他發(fā)生段錯誤):
文章插圖
自定義信號處理函數(shù)【深入剖析Sgementation fault原理】操作系統(tǒng)允許我們自己定義函數(shù),當某些信號被發(fā)送到進程之后,進程就會去執(zhí)行這些函數(shù),而不是系統(tǒng)默認的程序(比如說SIGSEGV默認函數(shù)是退出程序) 。下面來看我們重寫SIGINT信號的處理函數(shù),當一個程序在終端執(zhí)行的時候我們按下ctrl+c,這個正在執(zhí)行的程序就會收到一個來自內(nèi)核的SIGINT信號:
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <signal.h>#include <string.h>void sig(int n) { // 參數(shù) n 表示代表信號的數(shù)值char* str = "signal number = %d\n";char* out = malloc(128);sprintf(out, str, n);write(STDOUT_FILENO, out, strlen(out));free(out);}int main() {signal(SIGINT, sig); // 這行代碼就是注冊函數(shù) 當進程收到 SIGINT 信號的時候就執(zhí)行 sig 函數(shù)printf("pid = %d\n", getpid());while (1){sleep(1);}return 0;}
文章插圖
首先我們需要知道,當我們在終端啟動一個程序之后,如果我們在終端按下ctrl+c終端會給當前正在運行的進程以及他的子進程發(fā)送SIGINT信號,SIGINT信號的默認處理函數(shù)就是退出程序,但是我們可以捕獲這個信號,重寫處理函數(shù) 。在上面的程序當中我們就自己重寫了SIGINT的處理函數(shù),當進程接收到 SIGINT 信號的時候就會觸發(fā)函數(shù) sig。上面程序的輸出印證了我們的結(jié)果 。
經(jīng)驗總結(jié)擴展閱讀
- VS Code For Web 深入淺出 -- 進程間通信篇
- Java程序員必會Synchronized底層原理剖析
- VS Code For Web 深入淺出 -- 導讀篇
- Spring 深入——IoC 容器 01
- vue2雙向綁定原理:深入響應式原理defineProperty、watcher、get、set
- vue2.x核心源碼深入淺出,我還是去看源碼了
- 深入理解AQS--jdk層面管程實現(xiàn)【管程詳解的補充】
- 面對愛情總是淺嘗即止,不會深入的星座
- 深入底層C源碼 Redis核心設計原理
- flutter系列之:深入理解布局的基礎(chǔ)constraints
