添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

捕获内核的异常事件

有些时候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_callyour_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_blockpanic_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);

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()的接口来获取内核发送给所有进程的信号。我们可以利用jprobeget_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_blocktask_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_exitpost_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);
代码如下:[removed] try{ …some code… }catch(e){ …some code… //处理错误 throw(e.name); //抛出异常 }finally{<BR>    // 完成后执行语句块,非必须<BR>} [removed] javascript Error 对象: name: 错误名称number: 错误号description: 描述信息 message: 错误信息 fileName: 错误发生文件 stack: 错误发生时调用堆栈
程序运行了一段时间,有2个进程挂掉了,正常情况下进程不会主动挂掉,简单分析后认为可能是运行时某段时间内存占用过大,系统内存不足导致触发了Linux操作系统OOM killer机制,将运行中进程杀掉了。 Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快进程,然后防止内存耗尽而自动把该进程杀掉。...
与中断不同,各种异常都具有固定中断向量以及固定异常服务程序(在 trap_init 函数中指定)。因 此,当异常发生时,将直接跳转到相应服务程序并执行。 异常服务程序都定义在 arch/i386/kernel/entry.S 文件,比如堆栈段异常(中断向量 12)服务程序即是: 955 ENTRY(stack_segment) 956 RING0_EC_FRAME 957 pushl
本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ ------------------------------------------ 一: 前言 本文主要是对trace框架做详尽分析, 在后续分析中,再来分析接入到框架中几个重要trac
为了性能分析,参考x86perf_event.c写了个例程,结果在加载时,提示: test: Unknown symbol register_die_notifier test: Unknown symbol unregister_die_notifier 但是查找了下符号表明明有这两个符号: [root@bogon boot]# grep "register_die_notifie
通知链用于向请求通知代码区发送状态变化消息。 有几个内核中预定义通知器: l Die Notification: 当一个内核函数触发了一个由“opps”引起陷阱或错误时。 l Net device notification:当一个网...
linux内核系统中,各个模块、子系统之间是相互独立Linux内核可以通过通  知链机制来获取由其它模块或子系统产生它感兴趣某些事 件。 使用notifier由通知者可以传递给被通知者整形参数与指针,在linux中有许多地  方用到,比如reboot通知,cpu调频通知,电池低 电警报等等。熟 悉使用notifier有助于驱动开发。 notifier_block结构: 在前端项目中,由于JavaScript本身是一个弱类型语言,加上浏览器环境复杂性,网络问题等等,很容易发生错误。做好网页错误监控,不断优化代码,提高代码健壮性是一项很重要工作。本文将从Error开始,讲到如何捕获页面中异常。文章较长,细节较多,请耐心观看。 前端开发中Error...
int register_module_notifier(struct notifier_block *nb) 函数用于注册需要知道当前模块状态,然后触发一个回调函数 使用例子如下: static __init int jump_label_init_module(void) return register_module_notifier(&jump_label_module_n