• Sysrq 诊断系统故障 与 gdb 调试core dump


    1. 典型应用场景如:
        1)系统进入了挂死状态(如调度出现异常、或系统负荷过重),但仍能响应中断,此时可以通过Sysrq魔术键(c)手工触发panic,结合kdump,就能收集到vmcore信息,用于问题的后续分析定位,非常有用。
        2)当系统中某进程出现挂死(可能是D状态,或是死锁),此时需要确认该进程具体挂在什么地方,可以使用Sysrq魔术键(t)打印出系统中所有进程的堆栈信息。
        3)当系统出现反应迟钝、交互困难时,难以通过shell或终端交互获取到有用信息,此时可以使用Sysrq魔术键(m,p)打印出系统中内存使用的详细信息和CPU运行上下文信息等。

    Linux进程状态:R (TASK_RUNNING),可执行状态。
    只有在该状态的进程才可能在CPU上运行, 而同一时刻可能有多个进程处于可执行状, 这些进程的task_struct结构(进程控制块)被放入对 应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中).
    
    Linux进程状态:S (TASK_INTERRUPTIBLE),可中断的睡眠状态。
    处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中.
    
    Linux进程状态:D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态。
    与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。
    
    Linux进程状态:T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态。
    向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态(除非该进程本身处于 TASK_UNINTERRUPTIBLE状态而不响应信号)。
    
    Linux进程状态:Z (TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程。
    进程在退出的过程中,处于TASK_DEAD状态。
    
    Linux进程状态:X (TASK_DEAD - EXIT_DEAD),退出状态,进程即将被销毁。 
    而进程在退出过程中也可能不会保留它的task_struct。

     

    2、使用
    1)编译
        在Kernel hacking中,选中Magic SysRq key (CONFIG_MAGIC_SYSRQ) 。

    启用 SysRq

    内核的支持

    要启用 SysRq 功能,首先必须确保内核已经加入 CONFIG_MAGIC_SYSRQ 支持。在现今 Linux 发行版中,无一例外的均已加入该功能的支持,验证如下:

    # grep "CONFIG_MAGIC_SYSRQ" /boot/config-`uname -r` 
     CONFIG_MAGIC_SYSRQ=y

    2)使用方法
        通常有两种方式:a、通过/proc接口; b、通过键盘输入组合键。  
        通过/proc接口的使用方法为:echo ‘command’ > /proc/sysrq-trigger
        其中command是一个字符,可以是’1 – 9’或者’a-z’或者’A-Z’,字母不区分大小写。
        常用的命令有:
        0-9 设置printk的打印级别。和/proc/sys/kernel/printk的第一个参数意思相同
        b 立即重启单板,不进行磁盘同步等操作;调用内核的函数为emergency_restart
        c 进行kexec reboot,需要KEXC支持。调用内核的函数为crash_kexec 
        d 显示此有的锁,需要配置CONFIG_LOCKDEP,调用函数为debug_show_all_locks。
        e 向所有进程(init除外)发送SIGTERM信号,发送信号调用force_sig函数
        f 进入out-of-memory流程,杀死一个进程,调用out_of_memory函数
        i 向所有进程(init除外)发送SIGKILL信号
        m 显示当前内存信息,调用内核show_mem函数
        P 显示当前寄存器的值
        q 显示当前的timer,调用timer_list_show
        s 同步当前所有的文件系统,调用emergency_sync函数
        t 显示当前系统上所有的进程信息,调用函数为show_state
        u 重新mount所有文件系统,调用函数为emergency_remount
        w 显示系统中所有处于uninterruptable状态的进程,调用函数为show_state
        通过键盘组合键输入的规则是:
             串口:按住break键,然后5秒内输入command字符
             键盘:alt + sysrq +command键
        详细使用规则可以参考linux内核文档:Documentation/sysrq.txt。
    

      

    3、基本原理
        Sysrq实现的基本原理为:在键盘或串口驱动中(如果是/proc接口方式,则直接定义/proc的相关写入接口即可),对按键进行判断过滤,然后根据不同的按键进行相应的处理。普通键盘和串口的流程不尽相同,主要差别在键盘和串口驱动的具体实现上,总体流程一致。
        对于普通键盘来说 ,其底层的处理(从硬件中断到键盘驱动)过程依赖于内核中的输入(input)子系统。键盘处理的大致流程如下:
        1)键盘中断调用中断服务程序
        2)键盘中断服务程序调用输入子系统
        3)输入子系统调用键盘设备对应的键盘事件处理器
        4)键盘事件处理器完成键码的转换分类工作,根据按键类型的不同,执行不同的操作。对于输入类按键,先将按键值存放到临时缓冲区,激活临时缓冲区的工作队列,然后结束。对于控制类按键,激活对应此次控制操作的工作队列,然后结束。
        5)系统在适当的时机调度工作队列执行,完成剩下的操作
        而Sysrq魔术键的处理比较特殊,在内核主分支的代码中,在上述步骤4中的键盘事件处理器中进行相应的处理,不依赖于工作队列,相当于直接在硬件中断中处理。而在3.10内核版本的分支代码中,处理流程不太一样,其合入了相应的补丁,使sysrq的处理剥离出来,放在input子系统进行处理,而脱离了键盘事件的处理流程,其还是在中断上下文中处理的,不依赖于工作队列等。主要是通过注册input_handler实现,具体见后面的代码分析。
        另一方面,对于串口设备来说,其sysrq的处理流程根据各串口驱动的实现而稍有不同,但基本都是直接在硬件中断中直接处理的。
        所以,总的来说,sysrq魔术键基本都在中断上下文中处理,优先级很高,能在关键时刻发挥重要作用。
    
    4、代码分析
        Sysrq功能使用结构体sysrq_key_op定义了一个键盘键码所对应的行为,
    struct sysrq_key_op {
        void (*handler)(int);
        char *help_msg;
        char *action_msg;
        int enable_mask;
    };
        其中: 
        handler表示相应键码所对应的处理函数;
        action_msg是执行处理函数前打印的信息; 
        help_msg指相应键码的帮助信息; 
        enable_mask指该功能是否打开,仅限于键盘输入方式。
    
       另外,sysrq还定义了一个静态全局数组sysrq_key_table,共有36个元素,其中0~9用于命令字0~9,10到36用于命令字a~z。当从/proc/得到输入的命令字后,可以根据这个规则计算出他在sysrq_key_table中的index,然后判断对应handler是否为空,如果不为空的话,则调用handler函数处理。
    static struct sysrq_key_op *sysrq_key_table[36] = {
        &sysrq_loglevel_op,        /* 0 */
        &sysrq_loglevel_op,        /* 1 */
        &sysrq_loglevel_op,        /* 2 */
        &sysrq_loglevel_op,        /* 3 */
        &sysrq_loglevel_op,        /* 4 */
        &sysrq_loglevel_op,        /* 5 */
        &sysrq_loglevel_op,        /* 6 */
        &sysrq_loglevel_op,        /* 7 */
        &sysrq_loglevel_op,        /* 8 */
        &sysrq_loglevel_op,        /* 9 */
        /*
         * a: Don‘t use for system provided sysrqs, it is handled specially on
         * sparc and will never arrive.
         */
        NULL,                /* a */
        &sysrq_reboot_op,        /* b */
        &sysrq_crash_op,        /* c & ibm_emac driver debug */
        &sysrq_showlocks_op,        /* d */
        &sysrq_term_op,            /* e */
        &sysrq_moom_op,            /* f */
        /* g: May be registered for the kernel debugger */
        NULL,                /* g */
        NULL,                /* h - reserved for help */
        &sysrq_kill_op,            /* i */
    #ifdef CONFIG_BLOCK
        &sysrq_thaw_op,            /* j */
    #else
        NULL,                /* j */
    #endif
        &sysrq_SAK_op,            /* k */
    #ifdef CONFIG_SMP
        &sysrq_showallcpus_op,        /* l */
    #else
        NULL,                /* l */
    #endif
        &sysrq_showmem_op,        /* m */
        &sysrq_unrt_op,            /* n */
        /* o: This will often be registered as ‘Off‘ at init time */
        NULL,                /* o */
        &sysrq_showregs_op,        /* p */
        &sysrq_show_timers_op,        /* q */
        &sysrq_unraw_op,        /* r */
        &sysrq_sync_op,            /* s */
        &sysrq_showstate_op,        /* t */
        &sysrq_mountro_op,        /* u */
        /* v: May be registered for frame buffer console restore */
        NULL,                /* v */
        &sysrq_showstate_blocked_op,    /* w */
        /* x: May be registered on ppc/powerpc for xmon */
        /* x: May be registered on sparc64 for global PMU dump */
        NULL,                /* x */
        /* y: May be registered on sparc64 for global register dump */
        NULL,                /* y */
        &sysrq_ftrace_dump_op,        /* z */
    };
    如之前所说,3.10版本内核代码中使用了input_handler来实现Sysrq魔术键的单独处理。
    相应的input_handler定义:
    static struct input_handler sysrq_handler = {
        .filter        = sysrq_filter,/*相应的filter处理函数,在input子系统中调用,其中调用了Sysrq的处理函数*/
        .connect    = sysrq_connect,
        .disconnect    = sysrq_disconnect,
        .name        = “sysrq”,
        .id_table    = sysrq_ids,
    };
    
    注册input_handler,函数调用流程:sysrq_init()–>sysrq_register_handler()–>input_register_handler()
    static int __init sysrq_init(void)
    {
     sysrq_init_procfs();
     if (sysrq_on())
      sysrq_register_handler();
     return 0;
    }
    static inline void sysrq_register_handler(void)
    {
     unsigned short key;
     int error;
     int i;
     for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
      key = platform_sysrq_reset_seq[i];
      if (key == KEY_RESERVED || key > KEY_MAX)
       break;
      sysrq_reset_seq[sysrq_reset_seq_len++] = key;
     }
     error = input_register_handler(&sysrq_handler);
     if (error)
      pr_err(“Failed to register input handler, error %d”, error);
     else
      sysrq_handler_registered = true;
    }
    Sysrq魔术键处理代码流程:
    atkbd_interrupt()  //键盘中断ISR
        input_event() //输入子系统相关处理
            input_handle_event()
                input_pass_values()
                    input_to_handler()
                        handler->filter() //sysrq预先注册好的handler(sysrq_handler)的filter接口(sysrq_filter)
                            sysrq_filter()
                                sysrq_handle_keypress()
                                    __handle_sysrq()  //sysrq魔术键具体处理
    最终在__handle_sysrq()函数中完成Sysrq魔术键的具体处理。
    
    键盘其它按键的处理
    键盘其它按键的处理也是通过注册相应的input_handler(kbd_handler)来实现的,相应的event接口kbd_event()也在input子系统中调用:
    /*键盘按键相应的input_handler*/
    static struct input_handler kbd_handler = {
        .event        = kbd_event,
        .match        = kbd_match,
        .connect    = kbd_connect,
        .disconnect    = kbd_disconnect,
        .start        = kbd_start,
        .name        = “kbd”,
        .id_table    = kbd_ids,
    };
    
    kbd_handler注册:kbd_init()–>input_register_handler()
    int __init kbd_init(void) { int i; int error; for (i = 0; i < MAX_NR_CONSOLES; i++) { kbd_table[i].ledflagstate = kbd_defleds(); kbd_table[i].default_ledflagstate = kbd_defleds(); kbd_table[i].ledmode = LED_SHOW_FLAGS; kbd_table[i].lockstate = KBD_DEFLOCK; kbd_table[i].slockstate = 0; kbd_table[i].modeflags = KBD_DEFMODE; kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; } error = input_register_handler(&kbd_handler); if (error) return error; tasklet_enable(&keyboard_tasklet); tasklet_schedule(&keyboard_tasklet); return 0; } 键盘其它按键的处理代码流程: atkbd_interrupt() //键盘中断ISR input_event() //输入子系统相关处理 input_handle_event() input_pass_values() input_to_handler() handler->events() //键盘初始化是预先注册好的handler(kbd_handler)的event接口(kbd_event) kbd_event() kbd_keycode() put_queue() tty_insert_flip_char() //将键盘键值对应的编码数据写入缓冲区 tty_schedule_flip() //激活工作队列处理,处理函数为flush_to_ldisc 最终在kbd_keycode()函数进行相应的键码处理,主要完成键码的转换分类工作,根据按键类型的不同,执行不同的操作。对于输入类按键,先将按键值存放到临时缓冲区,激活临时缓冲区的工作队列,然后结束。对于控制类按键,激活对应此次控制操作 的工作队列,然后结束。 通过/proc接口触发Sysrq魔术键的主要函数流程(write_sysrq_trigger()为/proc/sysrq-trigger接口的write接口): write_sysrq_trigger() __handle_sysrq() 串口驱动(以8250串口为例)中对Sysrq魔术键的支持: 按照sysrq的设计,通过标准串口链接,按下break键后5秒内,再按住command字符,会触发command对应的sysrq流程。 相关的处理流程如下(从串口驱动的接收函数serial8250_rx_chars()开始,此函数在中断上下文中执行): serial8250_rx_chars() /* * 判断是否按下了break键,用于判断Sysrq。当按住break键时,在uart_handle_break中判断port->sysrq是否为0,如果为0, * 则将port->sysrq置为5秒后的jiffies数。 */ uart_handle_break() /* * 对于每一个接收的字符,都会调用uart_handle_sysrq_char。如果当前jiffies数值比port->sysrq小, * 则说明当前字符是在按住了break后5秒内输入的,因此调用handle_sysrq处理该命令。 */ uart_handle_sysrq_char()

     

    **********************************************************************************************

    gdb结合coredump定位崩溃进程

    打开core dump文件生成?

    ulimit -c unlimited  

    生成的core file在哪里?
    core file生成的地方是在/proc/sys/kernel/core_pattern文件定义的。
    %%: 相当于%
    %p: 相当于<pid>
    %u: 相当于<uid>
    %g: 相当于<gid>
    %s: 相当于导致dump的信号的数字
    %t: 相当于dump的时间
    %h: 相当于hostname
    %e: 相当于执行文件的名称
    这时用如下命令设置生成的core file到系统/tmp目录下,并记录pid以及执行文件名
        echo "/tmp/core-%e-%p" > /proc/sys/kernel/core_pattern
    示例代码:
    #include <stdio.h>
     
    int func(int *p)
    {
            *p = 0;
    }
     
    int main()
    {
            func(NULL);
            return 0;
    }

    在编译的时候开启-g调试开关就可以了.

    bt

    l

    info thread

    x/d 0xbf96d4d4

    (3)调试无-g编译的release程序,与core dump文件无关

    1. 相同的代码编译debug版本test_debug和release版本test_release;

    2. objcopy --only-keep-debug test_debug projectsymbol.dbg   #从DEBUG版本生成符号表;

    3. gdb -q --symbol=projectsymbol.dbg -exec=test_release    #加载符号表;

    4. 像调试debug一样可以调试release了

  • 相关阅读:
    PHP中使用CURL实现GET和POST请求
    ecstore关于smarty语法调用
    Linux 定时任务详解
    fzu 1753 Another Easy Problem
    zoj 2562 More Divisors
    poj 2992 Divisors
    UVA10078多边形判断凹凸性
    UVA10002求凸包的质心
    UVA10088多边形内整点个数计算(计算几何)
    HDU 1824 简单2-sat
  • 原文地址:https://www.cnblogs.com/eksay/p/sysrq_linux.html
Copyright © 2020-2023  润新知