捕获内核的异常事件
2015-10-27 21:19
429 查看
捕获内核的异常事件
有些时候Linux驱动或内核的开发者想要获取Linux内核的一些异常事件,并对异常事件做出一些处理和响应。例如记录一些异常时的日志或dump出堆栈信息来帮助分析系统发生异常的原因。本博客将要介绍如何获取内核的halt、restart、power off、oops、panic以及OOM事件,另外还将介绍如何截取内核发给进程的signals和process exit事件。Kernel Halt, Kernel Restart and Kernel Power Off
内核提供了一个注册notifier的接口给开发者,开发者使用这个接口向内核中注册自定义的notifier回调函数,当内核发生如标题所述的三个事件是,这个回调函数就会被调用。这个接口就是:int register_reboot_notifier (struct notifier_block *nb);
在调用这个函数之前,必须先定义一个
notifier_block并实现他
notifier_call,具体示例代码如下:
static int your_handler (struct notifier_block *self, unsigned long val, void *data) { switch (val) { case SYS_HALT: handle_system_halt(); break; case SYS_RESTART: handle_system_restart(); break; case SYS_POWER_OFF: handle_system_power_off(); break; } } static notifier_block your_notifier = { .notifier_call = your_handler, }; register_reboot_notifier (&your_notifier);
当内核发生halt, power off和restart异常事件时。
notifier_call即
your_handler()就会被调用。
Kernel Oops
内核也提供了一个注册notifier的接口register_die_notifier(),当kernel oops发生时,内核会调用
notifier_chain上的
notifier_block。可以参考以下示例代码:
int register_die_notifier (struct notifier_block *nb); static int your_oops_handler (struct notifier_block *self, unsigned long val, void *data) { if ((enumdie_val) val == DIE_OOPS) { your_oops_handle (); } } static struct your_oops_notifier = { .notifier_call = your_oops_handler, }; register_die_notifier(&your_oops_notifier);
Kernel panic
内核同样提供了notifier的机制来提供接口获取内核的Panic事件。只是内核没有直接提供注册获取内核panic的时间的直接接口。而是暴露了一个panic_notifier_list通知链。开发者需要调用
atomic_notifier_chain_register()这个函数来注册一个
notifier_block到这个通知链上即可。在我们注册
notifier_block到
panic_notifier_list之前我们需要实现
notifer_block的回调函数。
请参考如下代码:
static int your_panic_handler (struct notifier_block *self, unsigned long val, void *data) { your_panic_handle(); } static struct notifier_block your_panic_notifier = { .notifier_call = your_panic_handler, };
然后调用
atomic_notifier_chain_register ()函数注册:
atomic_notifier_chain_register (&panic_notifier_list, &your_panic_handler);
OOM
Linux内核也同样提供了一个接口用来注册notifier,在内核发生OOM异常时,内核调用一些特殊代码来处理开发者想要处理的任务或获取一些信息。同样,在注册
notifier到内核OOM通知链之前,我们也要先实现
notifier_block的回调函数。具体实现代码如下:
static int your_oom_handler (struct notifier_block *self, unsigned long val, void *data) { your_oom_handle(); } static struct notifier_block your_oom_notifier = { .notifier_call = your_oom_handler, };
我们调用下面的函数来将这个
notifier_block注册到内核的OOM异常通知链上去:
register_oom_notifier (&your_oom_notifier);
Process Signals
进程的信号和之前的异常事件不同。这些信号通常不是致命的异常事件。我们对进程的信号的获取和处理机制也与之前的那些异常事件不同。我们用kernel提供的jprobe接口来获取进程的信号。内核提供了一个叫
get_signal_deliver()的接口来获取内核发送给所有进程的信号。我们可以利用
jprobe向
get_signal_deliver()这个函数中插入我们自己的代码。当
get_signal_deliver()这个接口被调用时,我们自己的代码也会被调用。
我们首先需要实现处理这些信号的代码,就是
jprobe对象的entry属性:
static void your_get_signal_and_handle (struct siginfo *info, struct k_sigaction, *return_ka, struct pt_regs *regs, void *cookie) { your_signal_handler(); } struct jprobe jp_sig = { .entry = your_get_signal_and_handle };
然后通过下面一段代码将我们上面实现的
jprobe结构注册到内核。
struct jprobe *jps_sig[1] = {&jp_sig}; void *jp_addr1 = (void *)kallsyms_lookup_name(“get_signal_to_deliver”); jp_sig.kp.addr = (kprobe_code_t *) jp_addr1; ret = register_jprobes(jps_sig, ARRAY_SIZE(jps_sig));
Process exit
有两种方法来获取Process exit事件。一种是通过调用profile_event_register()接口直接注册一个
notifier_block到
task_exit_notifier;另一种方法是使用
kprobe接口将我们的代码插入到系统调用
do_exit()结尾。我们分别介绍这两种方法:
使用 notifier
先实现notifier_block结构的
notifier_call:
int your_do_exit_handler (struct notifier_block *nb, unsigned long val, void *data) { your_exit_handle(); }
然后定义一个
struct notifier_block结构,然后调用
profile_event_register ()注册:
static struct notifier_block your_exit = { .notifier_call = your_do_exit_handler, }; profile_event_register(PROFILE_TASK_EXIT, &your_exit);
使用 kprobe
先实现进程退出的处理函数do_exit_handle(),然后定义一个
struct kprobe结构
kp_exit,并设置
kp_exit的
post_handler回调函数为
do_exit_handle()。然后通过调用
register_kprobe()函数,将
kp_exit结构注册到内核。当内核接口
do_exit()被调用后,
do_exit_handle()就会被调用。具体实现代码如下:
void your_do_exit_handle(struct kprobe *p, struct pt_regs *regs, unsigned long flags) { your_exit_handle(); } struct kprobe *kp_exit = (struct kprobe *)kzalloc(sizeof(struct kprobe), GFP_KERNRL); kp_exit->symbol_name = “do_exit”; kp_exit->post_handler = your_do_exit_handle; register_kprobe(kp_exit);
相关文章推荐
- Linux 自检和 SystemTap
- 开发人员、程序员与计算机科学家三者之间的区别
- OMAP3630 Linux I2C总线驱动分析
- Linux设备驱动开发环境的搭建
- 迅为4412开发板Linux驱动教程/硬件知识及原理图的使用
- Ruby中的异常处理代码编写示例
- MySQL抛出Incorrect string value异常分析
- Linux内核链表实现过程
- 详解JavaScript中的异常处理方法
- java程序中的延时加载异常及解决方案
- .NET(C#):Emit创建异常处理的方法
- windows7服务器上weblogic启动失败异常解决方法
- 有关ajax的error与后台的异常问题解决
- 深入探讨JAVA中的异常与错误处理
- GO语言异常处理机制panic和recover分析
- 浅谈JAVA 异常对于性能的影响
- C++ 异常处理 catch(...)介绍
- 开发人员一定要加入收藏夹的网站 推荐
- php中异常处理方法小结
- PHP中异常处理的一些方法整理