日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

使用sigaction处理内核信号

發布時間:2023/11/27 生活经验 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用sigaction处理内核信号 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

        • 函數描述
        • 函數使用
        • 抓取發送信號的進程信息

mark一次獲取內核信號,并作相應處理的手段
linux內核中斷機制的一個重要實現就是信號。信號使得內核和用戶態的交互更加便捷,這個便捷對開發者來說可以更好的利用系統原生內核來處理信息。

《深入理解unix內核》中對信號作用的描述如下:

  • 讓進程知道已經發生了一個特定事件
  • 強迫進程執行它自己代碼中的信號處理程序

這里主要描述一下借用sigaction系統調用,對信號進行注冊并做出相應的處理

函數描述

包含頭文件:#include <signal.h>
函數原型:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
參數含義如下:

  • signum 表示特例化信號代表的數字,其中SIGKILLSIGSTOP信號不能被注冊
  • struct sigaction 該數據結構如下
    struct sigaction {void     (*sa_handler)(int); /*表示要執行操作的類型,它的值可以是指向信號處理程序的一個指針,SIG_DFL(值為0,指定缺省操作, 或者SIG_IBN(值為1,指定忽略信號)*/void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask; /*這是一個標志集,制定必須怎樣處理信號。*/int        sa_flags; /*這個是類型為sigset_t的變量,指定當運行信號處理程序時要屏蔽的信號,一般為SA_SIGINFO,來獲取處理程序的附件信息*/void     (*sa_restorer)(void);
    };
    
    這里一般是我們在程序中封裝該sigaction數據結構,將我們想要的參數封裝好傳入進去,再通過系統調用執行處理。
    其中SA_SIGINFO數據結構包含如下參數,直接通過siginfo_t 的結構體變量來獲取
     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 該參數非空的,存儲最新的一個action的信息的數據結構
  • struct sigaction *oldact 該參數也為空,同時存儲上一個action的信息的數據結構

函數使用

查看如下代碼

#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;/*未分配空間,模擬非法訪問內存*/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);/*注冊信號處理函數如下,sig_op,用來打印函數調用棧以及信號集中發出該信號的進程詳細信息*/act.sa_handler=sig_op; act.sa_flags=SA_SIGINFO;/*注冊了信號SIGSEGV用來獲取非法內存訪問*/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
這里增加了打印函數調用棧,需要將所有符號連接到二進制文件,所以需要-rdynamic參數,這里想要進一步了解打印函數調用棧,可以參考關于ceph源碼 backtrace 打印函數調用棧

執行結果如下可以看到函數調用棧如下,sig_op函數是由系統c庫發出

./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

抓取發送信號的進程信息

使用以上獲取內核命令的方式,我們可以在今后抓取終止當前進程的某個進程(/proc/p_id/status 文件)信息,舉例如下
改代碼注冊了鍵盤中斷信號(SIGINT)以及終止信號(SIGTERM),同時將發送該信號的進程信息打印出來(包括進程名稱,進程p_id,占用內存,所處內存地址等信息)

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h> 
#include <time.h>  /*打印當前時間*/
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
/*打印發送信號進程的詳細信息,即從/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");}
}  /*打印函數調用棧*/
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("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;/*注冊信號*/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);/*死循環來打印時間,當遇到注冊的內核信息,此時打印出進程詳細信息*/while(1){sleep(1);printDatetime();fflush(stdout);}return 0;
}

輸出結果如下:

now datetime: 119-6-13 19:44:29
now datetime: 119-6-13 19:44:30
#執行了killall操作,獲取到了SIGTERM信號,打印出來的信息
./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
#可以看到發送SIGTERM信號的進程名稱如下
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
#執行鍵盤中斷獲取到信號信息,但是該執行并不是一個進程,所以并不會打印調用棧
^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
#當執行了kill -9 命令時,即發送SIGKILL信號,會徹底終止當前進程。(SIGKILL信號不允許被注冊,抓取)
Killed

總結

以上是生活随笔為你收集整理的使用sigaction处理内核信号的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。