使用sigaction处理内核信号
文章目錄
- 函數(shù)描述
- 函數(shù)使用
- 抓取發(fā)送信號(hào)的進(jìn)程信息
mark一次獲取內(nèi)核信號(hào),并作相應(yīng)處理的手段
linux內(nèi)核中斷機(jī)制的一個(gè)重要實(shí)現(xiàn)就是信號(hào)。信號(hào)使得內(nèi)核和用戶態(tài)的交互更加便捷,這個(gè)便捷對(duì)開發(fā)者來說可以更好的利用系統(tǒng)原生內(nèi)核來處理信息。
《深入理解unix內(nèi)核》中對(duì)信號(hào)作用的描述如下:
- 讓進(jìn)程知道已經(jīng)發(fā)生了一個(gè)特定事件
- 強(qiáng)迫進(jìn)程執(zhí)行它自己代碼中的信號(hào)處理程序
這里主要描述一下借用sigaction系統(tǒng)調(diào)用,對(duì)信號(hào)進(jìn)行注冊(cè)并做出相應(yīng)的處理
函數(shù)描述
包含頭文件:#include <signal.h>
函數(shù)原型:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
參數(shù)含義如下:
signum表示特例化信號(hào)代表的數(shù)字,其中SIGKILL和SIGSTOP信號(hào)不能被注冊(cè)struct sigaction該數(shù)據(jù)結(jié)構(gòu)如下
這里一般是我們?cè)诔绦蛑蟹庋b該sigaction數(shù)據(jù)結(jié)構(gòu),將我們想要的參數(shù)封裝好傳入進(jìn)去,再通過系統(tǒng)調(diào)用執(zhí)行處理。struct sigaction {void (*sa_handler)(int); /*表示要執(zhí)行操作的類型,它的值可以是指向信號(hào)處理程序的一個(gè)指針,SIG_DFL(值為0,指定缺省操作, 或者SIG_IBN(值為1,指定忽略信號(hào))*/void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask; /*這是一個(gè)標(biāo)志集,制定必須怎樣處理信號(hào)。*/int sa_flags; /*這個(gè)是類型為sigset_t的變量,指定當(dāng)運(yùn)行信號(hào)處理程序時(shí)要屏蔽的信號(hào),一般為SA_SIGINFO,來獲取處理程序的附件信息*/void (*sa_restorer)(void); };
其中SA_SIGINFO數(shù)據(jù)結(jié)構(gòu)包含如下參數(shù),直接通過siginfo_t 的結(jié)構(gòu)體變量來獲取siginfo_t {int si_signo; /* Signal number */int si_errno; /* An errno value */int si_code; /* Signal code */int si_trapno; /* Trap number that causedhardware-generated signal(unused on most architectures) */pid_t si_pid; /* Sending process ID */uid_t si_uid; /* Real user ID of sending process */int si_status; /* Exit value or signal */clock_t si_utime; /* User time consumed */clock_t si_stime; /* System time consumed */sigval_t si_value; /* Signal value */int si_int; /* POSIX.1b signal */void *si_ptr; /* POSIX.1b signal */int si_overrun; /* Timer overrun count; POSIX.1b timers */int si_timerid; /* Timer ID; POSIX.1b timers */void *si_addr; /* Memory location which caused fault */long si_band; /* Band event (was int inglibc 2.3.2 and earlier) */int si_fd; /* File descriptor */short si_addr_lsb; /* Least significant bit of address(since Linux 2.6.32) */ }const struct sigaction *act該參數(shù)非空的,存儲(chǔ)最新的一個(gè)action的信息的數(shù)據(jù)結(jié)構(gòu)struct sigaction *oldact該參數(shù)也為空,同時(shí)存儲(chǔ)上一個(gè)action的信息的數(shù)據(jù)結(jié)構(gòu)
函數(shù)使用
查看如下代碼
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
void get_unlegal_memory()
{ char *a = NULL;/*未分配空間,模擬非法訪問內(nèi)存*/printf("get the unmalloc memmory %c!\n",a[0]);
}
void print_stacktrace()
{int size = 16,i=0;void * array[16];int stack_num = backtrace(array, size); char ** stacktrace = backtrace_symbols(array, stack_num); for (; i < stack_num; ++i) {printf("%s\n", stacktrace[i]); }free(stacktrace);
}
void sig_op(int signo, siginfo_t* info, void* context)
{print_stacktrace();printf("get the kernel signal\n");printf("sig signo is %d\n",info->si_signo);exit(0);
}
int main(int argc,char** argv)
{struct sigaction act;struct sigaction oact;pid_t pid;pid=getpid();sigemptyset(&act.sa_mask);/*注冊(cè)信號(hào)處理函數(shù)如下,sig_op,用來打印函數(shù)調(diào)用棧以及信號(hào)集中發(fā)出該信號(hào)的進(jìn)程詳細(xì)信息*/act.sa_handler=sig_op; act.sa_flags=SA_SIGINFO;/*注冊(cè)了信號(hào)SIGSEGV用來獲取非法內(nèi)存訪問*/if(sigaction(SIGSEGV,&act,&oact)== -1)printf("%d","install error~!\n",SIGSEGV);printf("the pid is %d\n",pid);get_unlegal_memory();return 0;
}
編譯方式如下
gcc test.c -rdynamic -g -o test
這里增加了打印函數(shù)調(diào)用棧,需要將所有符號(hào)連接到二進(jìn)制文件,所以需要-rdynamic參數(shù),這里想要進(jìn)一步了解打印函數(shù)調(diào)用棧,可以參考關(guān)于ceph源碼 backtrace 打印函數(shù)調(diào)用棧
執(zhí)行結(jié)果如下可以看到函數(shù)調(diào)用棧如下,sig_op函數(shù)是由系統(tǒng)c庫(kù)發(fā)出
./test
the pid is 7754
./test_memory(print_stacktrace+0x32) [0x400b2b]
./test_memory(sig_op+0x1d) [0x400ba5]
/lib64/libc.so.6(+0x35a00) [0x7f470de32a00]
./test_memory(get_unlegal_memory+0x20) [0x400ae0]
./test_memory(main+0x9c) [0x400c6c]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f470de1eaf5]
./test_memory() [0x4009f9]
get the kernel signal
sig signo is 11
抓取發(fā)送信號(hào)的進(jìn)程信息
使用以上獲取內(nèi)核命令的方式,我們可以在今后抓取終止當(dāng)前進(jìn)程的某個(gè)進(jìn)程(/proc/p_id/status 文件)信息,舉例如下
改代碼注冊(cè)了鍵盤中斷信號(hào)(SIGINT)以及終止信號(hào)(SIGTERM),同時(shí)將發(fā)送該信號(hào)的進(jìn)程信息打印出來(包括進(jìn)程名稱,進(jìn)程p_id,占用內(nèi)存,所處內(nèi)存地址等信息)
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h> /*打印當(dāng)前時(shí)間*/
void printDatetime()
{ time_t now;struct tm *tm_now;time(&now);tm_now = localtime(&now);printf("now datetime: %d-%d-%d %d:%d:%d\n", tm_now->tm_year, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);fflush(stdout);
}#define BUF_SIZE 1024
/*打印發(fā)送信號(hào)進(jìn)程的詳細(xì)信息,即從/proc/p_id/status 文件目錄下獲取*/
void printTaskStatusByPid(int pid) { char proc_pid_path[BUF_SIZE]={0}; char buf[BUF_SIZE]={0}; sprintf(proc_pid_path, "/proc/%d/status", pid); FILE* fp = fopen(proc_pid_path, "r"); if(NULL != fp){while(fgets(buf, BUF_SIZE-1, fp)!= NULL){printf("%s\n",buf);} fclose(fp); }else{printf("NULL\n");}
} /*打印函數(shù)調(diào)用棧*/
void print_stacktrace()
{int size = 16,i=0;void * array[16];int stack_num = backtrace(array, size); char ** stacktrace = backtrace_symbols(array, stack_num); for (; i < stack_num; ++i) {printf("%s\n", stacktrace[i]); }free(stacktrace);
}/*內(nèi)核信號(hào)處理函數(shù),打印發(fā)送該信號(hào)的進(jìn)程信息*/
void sig_op(int signo, siginfo_t* info, void* context)
{print_stacktrace();printf("the signo is %d\n",signo);printf("sig pid is %d\n", (int)(info->si_pid));printf("sig uid is %d\n", (int)(info->si_uid));printf("sig signo is %d\n",info->si_signo);printTaskStatusByPid((int)(info->si_pid));fflush(stdout);
}int main(int argc,char** argv)
{struct sigaction act;struct sigaction oact;pid_t pid;pid=getpid();printDatetime(); printf("the pid is %d\n",pid);fflush(stdout);/*初始化sigset_t變量中的位*/sigemptyset(&act.sa_mask);act.sa_handler=sig_op; act.sa_flags=SA_SIGINFO;/*注冊(cè)信號(hào)*/if(sigaction(SIGINT,&act,&oact)==-1)printf("%s","install error~!\n");if(sigaction(SIGTERM,&act,&oact)==-1)printf("%s","install error~!\n");if(sigaction(SIGSEGV,&act,&oact)== -1)printf("%d","install error~!\n",SIGSEGV);/*死循環(huán)來打印時(shí)間,當(dāng)遇到注冊(cè)的內(nèi)核信息,此時(shí)打印出進(jìn)程詳細(xì)信息*/while(1){sleep(1);printDatetime();fflush(stdout);}return 0;
}
輸出結(jié)果如下:
now datetime: 119-6-13 19:44:29
now datetime: 119-6-13 19:44:30
#執(zhí)行了killall操作,獲取到了SIGTERM信號(hào),打印出來的信息
./test(print_stacktrace+0x32) [0x400e86]
./test(sig_op+0x1d) [0x400f00]
/lib64/libc.so.6(+0x35a00) [0x7f18223b9a00]
/lib64/libc.so.6(nanosleep+0x10) [0x7f1822441890]
/lib64/libc.so.6(sleep+0xd4) [0x7f1822441744]
./test(main+0x109) [0x401083]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f18223a5af5]
./test() [0x400c49]
the signo is 15
sig pid is 20863
sig uid is 0
sig signo is 15
#可以看到發(fā)送SIGTERM信號(hào)的進(jìn)程名稱如下
Name: killall
State: R (running)
Tgid: 20863
Ngid: 0
Pid: 20863
PPid: 15099
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256
Groups: 0
VmPeak: 116760 kB
VmSize: 116760 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 924 kB
VmRSS: 924 kB
VmData: 212 kB
VmStk: 136 kB
VmExe: 20 kB
VmLib: 2508 kB
VmPTE: 64 kB
VmSwap: 0 kB
Threads: 1
SigQ: 1/125771
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
Seccomp: 0
Cpus_allowed: ff
Cpus_allowed_list: 0-7
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 1
nonvoluntary_ctxt_switches: 1
now datetime: 119-6-13 19:44:30
now datetime: 119-6-13 19:46:45
now datetime: 119-6-13 19:46:46
#執(zhí)行鍵盤中斷獲取到信號(hào)信息,但是該執(zhí)行并不是一個(gè)進(jìn)程,所以并不會(huì)打印調(diào)用棧
^C./test(print_stacktrace+0x32) [0x400e86]
./test(sig_op+0x1d) [0x400f00]
/lib64/libc.so.6(+0x35a00) [0x7f18223b9a00]
/lib64/libc.so.6(nanosleep+0x10) [0x7f1822441890]
/lib64/libc.so.6(sleep+0xd4) [0x7f1822441744]
./test(main+0x109) [0x401083]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7f18223a5af5]
./test() [0x400c49]
the signo is 2
sig pid is 0
sig uid is 0
sig signo is 2
NULL
now datetime: 119-6-13 19:46:47
now datetime: 119-6-13 19:48:10
now datetime: 119-6-13 19:48:11
#當(dāng)執(zhí)行了kill -9 命令時(shí),即發(fā)送SIGKILL信號(hào),會(huì)徹底終止當(dāng)前進(jìn)程。(SIGKILL信號(hào)不允許被注冊(cè),抓取)
Killed
總結(jié)
以上是生活随笔為你收集整理的使用sigaction处理内核信号的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个基督徒微信网名
- 下一篇: ceph bluestore源码分析:C