kdb代码分析(二)

 来看另一个函数kdb_init(),显然这个函数就是整个kdb的入口,或者说初始化函数.

 12393 +
 12394 +/*
 12395 + * kdb_init
 12396 + *
 12397 + *     Initialize the kernel debugger environment.
 12398 + *
 12399 + * Parameters:
 12400 + *     None.
 12401 + * Returns:
 12402 + *     None.
 12403 + * Locking:
 12404 + *     None.
 12405 + * Remarks:
 12406 + *     None.
 12407 + */
 12408 +
 12409 +void __init
 12410 +kdb_init(void)
 12411 +{
 12412 +       kdb_initial_cpu = smp_processor_id();
 12413 +       /*
 12414 +        * This must be called before any calls to kdb_printf.
 12415 +        */
  12416 +       kdb_io_init();
 12417 +
 12418 +       kdb_inittab();          /* Initialize Command Table */
 12419 +       kdb_initbptab();        /* Initialize Breakpoint Table */
 12420 +       kdb_id_init();          /* Initialize Disassembler */
  12421 +       kdba_init();            /* Architecture Dependent Initialization */
 12422 +
 12423 +       /*
 12424 +        * Use printk() to get message in log_buf[];
 12425 +        */
 12426 +       printk("kdb version %d.%d%s by Keith Owens, Scott Lurndal. "/
 12427 +              "Copyright SGI, All Rights Reserved/n",
 12428 +               KDB_MAJOR_VERSION, KDB_MINOR_VERSION, KDB_TEST_VERSION);
 12429 +
 12430 +       kdb_cmd_init();         /* Preset commands from kdb_cmds */
 12431 +      kdb_initial_cpu = -1;   /* Avoid recursion problems */
 12432 +       kdb(KDB_REASON_CPU_UP, 0, NULL);        /* do kdb setup on boot cpu */
 12433 +       kdb_initial_cpu = smp_processor_id();
 12434 +       atomic_notifier_chain_register(&panic_notifier_list, &kdb_block);
 12435 +       register_cpu_notifier(&kdb_cpu_nfb);
 12436 +
 12437 +#ifdef kdba_setjmp
 12438 +       kdbjmpbuf = vmalloc(NR_CPUS * sizeof(*kdbjmpbuf));
 12439 +       if (!kdbjmpbuf)
 12440 +               printk(KERN_ERR "Cannot allocate kdbjmpbuf, no kdb recovery will be possible/n");
 12441 +#endif /* kdba_setjmp */
 12442 +
 12443 +       kdb_initial_cpu = -1;
 12444 +       kdb_wait_for_cpus_secs = max(10, 2*num_online_cpus());
 12445 +}
kdb究竟有多复杂,从这个初始化函数就可见一斑.说实话Linux Kernel中光初始化就这么复杂的模块还真的不多.一共五个名字里带”init”的函数.咱们一个一个来看:
1.      kdb_io_init()
   8377 +/*
   8378 + * kdb_io_init
   8379 + *
   8380 + *     Initialize kernel debugger output environment.
   8381 + *
   8382 + * Parameters:
   8383 + *     None.
   8384 + * Returns:
   8385 + *     None.
   8386 + * Locking:
   8387 + *     None.
   8388 + * Remarks:
   8389 + *     Select a console device. Only use a VT console if the user specified
   8390 + *     or defaulted console= /^tty[0-9]*$/
  8391 + *
   8392 + *     FIXME: 2.6.22-rc1 initializes the serial console long after kdb starts,
   8393 + *     so booting with 'console=tty console=ttyS0' does not create the console
   8394 + *     entry for ttyS0 in time. For now simply assume that we have a working
   8395 + *     console, until a better solution can be found.
   8396 + */
   8397 +
   8398 +void __init
   8399 +kdb_io_init(void)
   8400 +{
   8401 +       /*
   8402 +        * Select a console.
   8403 +        */
   8404 +       struct console *c = console_drivers;
   8405 +       int vt_console = 0;
   8406 +
   8407 +       while (c) {
   8408 +#if 0 /* FIXME: we don't register serial consoles in time */
   8409 +               if ((c->flags & CON_CONSDEV) && !kdbcons)
   8410 +                       kdbcons = c;
   8411 +#else
   8412 +               if (!kdbcons)
   8413 +                       kdbcons = c;
   8414 +#endif
   8415 +               if ((c->flags & CON_ENABLED) &&
   8416 +                   strncmp(c->name, "tty", 3) == 0) {
   8417 +                       char *p = c->name + 3;
   8418 +                       while (isdigit(*p))
   8419 +                               ++p;
   8420 +                       if (*p == '/0')
   8421 +                               vt_console = 1;
   8422 +               }
   8423 +               c = c->next;
   8424 +       }
   8425 +
   8426 +       if (kdbcons == NULL) {
   8427 +               printk(KERN_ERR "kdb: Initialization failed - no console. kdb is disabled./n");
   8428 +               KDB_FLAG_SET(NO_CONSOLE);
   8429 +               kdb_on = 0;
   8430 +       }
   8431 +       if (!vt_console)
   8432 +               KDB_FLAG_SET(NO_VT_CONSOLE);
   8433 +       kdb_input_flush();
   8434 +       return;
   8435 +}
所谓的vt_console就是tty0到tty9这些个.console_drivers是一个全局变量,各种各样的console drivers通过调用register_console()来向这个变量注册.比如drivers/serial目录下面各种串口的驱动都有调用这一函数,像8250串口驱动就是注册了一个serial8250_console.
kdbcons也是全局变量.它将被赋予console_drivers的第一个console,比如tty0,比如ttyS0.
kdb_input_flush()就是延时0.5s.
2.      kdb_inittab()
 12212 +/*
 12213 + * kdb_inittab
 12214 + *
 12215 + *     This function is called by the kdb_init function to initialize
 12216 + *     the kdb command table.   It must be called prior to any other
 12217 + *     call to kdb_register_repeat.
 12218 + *
 12219 + * Inputs:
 12220 + *     None.
 12221 + * Outputs:
 12222 + *     None.
 12223 + * Returns:
 12224 + *     None.
 12225 + * Locking:
 12226 + *     None.
 12227 + * Remarks:
 12228 + *
 12229 + */
 12230 +
 12231 +static void __init
 12232 +kdb_inittab(void)
 12233 +{
 12234 +       int i;
 12235 +       kdbtab_t *kp;
 12236 +
 12237 +       for(i=0, kp=kdb_commands; i < kdb_max_commands; i++,kp++) {
 12238 +               kp->cmd_name = NULL;
 12239 +       }
 12240 +
 12241 +       kdb_register_repeat("md", kdb_md, "<vaddr>",   "Display Memory Contents, also mdWcN, e.g. md8c1", 1, KDB_REPEAT_NO_ARGS);
 12242 +       kdb_register_repeat("mdr", kdb_md, "<vaddr> <bytes>",   "Display Raw Memory", 0, KDB_REPEAT_NO_ARGS);
 12243 +       kdb_register_repeat("mdp", kdb_md, "<paddr> <bytes>",   "Display Physical Memory", 0, KDB_REPEAT_NO_ARGS);
 12244 +       kdb_register_repeat("mds", kdb_md, "<vaddr>",   "Display Memory Symbolically", 0, KDB_REPEAT_NO_ARGS);
 12245 +       kdb_register_repeat("mm", kdb_mm, "<vaddr> <contents>",   "Modify Memory Contents", 0, KDB_REPEAT_NO_ARGS);
 12246 +       kdb_register_repeat("id", kdb_id, "<vaddr>",   "Display Instructions", 1, KDB_REPEAT_NO_ARGS);
 12247 +       kdb_register_repeat("go", kdb_go, "[<vaddr>]", "Continue Execution", 1, KDB_REPEAT_NONE);
 12248 +       kdb_register_repeat("rd", kdb_rd, "",           "Display Registers", 1, KDB_REPEAT_NONE);
 12249 +       kdb_register_repeat("rm", kdb_rm, "<reg> <contents>", "Modify Registers", 0, KDB_REPEAT_NONE);
 12250 +       kdb_register_repeat("ef", kdb_ef, "<vaddr>",   "Display exception frame", 0, KDB_REPEAT_NONE);
 12251 +       kdb_register_repeat("bt", kdb_bt, "[<vaddr>]", "Stack traceback", 1, KDB_REPEAT_NONE);
 12252 +       kdb_register_repeat("btp", kdb_bt, "<pid>",     "Display stack for process <pid>", 0, KDB_REPEAT_NONE);
 12253 +       kdb_register_repeat("bta", kdb_bt, "[DRSTCZEUIMA]",     "Display stack all processes", 0, KDB_REPEAT_NONE);
 12254 +       kdb_register_repeat("btc", kdb_bt, "", "Backtrace current process on each cpu", 0, KDB_REPEAT_NONE);
 12255 +       kdb_register_repeat("btt", kdb_bt, "<vaddr>",   "Backtrace process given its struct task address", 0, KDB_REPEAT_NONE);
 12256 +       kdb_register_repeat("ll", kdb_ll, "<first-element> <linkoffset> <cmd>", "Execute cmd for each element in linked list", 0, KDB_REPEAT_NONE);
 12257 +       kdb_register_repeat("env", kdb_env, "",         "Show environment variables", 0, KDB_REPEAT_NONE);
 12258 +       kdb_register_repeat("set", kdb_set, "",         "Set environment variables", 0, KDB_REPEAT_NONE);
 12259 +       kdb_register_repeat("help", kdb_help, "",       "Display Help Message", 1, KDB_REPEAT_NONE);
 12260 +       kdb_register_repeat("?", kdb_help, "",         "Display Help Message", 0, KDB_REPEAT_NONE);
 12261 +       kdb_register_repeat("cpu", kdb_cpu, "<cpunum>","Switch to new cpu", 0, KDB_REPEAT_NONE);
 12262 +       kdb_register_repeat("ps", kdb_ps, "",           "Display active task list", 0, KDB_REPEAT_NONE);
 12263 +       kdb_register_repeat("pid", kdb_pid, "<pidnum>", "Switch to another task", 0, KDB_REPEAT_NONE);
 12264 +       kdb_register_repeat("reboot", kdb_reboot, "", "Reboot the machine immediately", 0, KDB_REPEAT_NONE);
 12265 +#if defined(CONFIG_MODULES)
 12266 +       kdb_register_repeat("lsmod", kdb_lsmod, "",     "List loaded kernel modules", 0, KDB_REPEAT_NONE);
 12267 +#endif
 12268 +#if defined(CONFIG_MAGIC_SYSRQ)
 12269 +       kdb_register_repeat("sr", kdb_sr, "<key>",      "Magic SysRq key", 0, KDB_REPEAT_NONE);
 12270 +#endif
 12271 +       kdb_register_repeat("dmesg", kdb_dmesg, "[lines]",      "Display syslog buffer", 0, KDB_REPEAT_NONE);
 12272 +       kdb_register_repeat("defcmd", kdb_defcmd, "name /"usage/" /"help/"", "Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE);
 12273 +       kdb_register_repeat("kill", kdb_kill, "<-signal> <pid>", "Send a signal to a process", 0, KDB_REPEAT_NONE);
 12274 +       kdb_register_repeat("summary", kdb_summary, "", "Summarize the system", 4, KDB_REPEAT_NONE);
 12275 +       kdb_register_repeat("per_cpu", kdb_per_cpu, "", "Display per_cpu variables", 3, KDB_REPEAT_NONE);
 12276 +}
这个函数究竟在干嘛呢?就看见它在不停的调用kdb_register_repeat()函数.如果你使用过kdb,熟悉它的那些个命令,你就会发现,每个命令都在这个函数中出现过,很显然,kdb_register_repeat()不干别的,就为了注册每个命令,kdb_register_repeat()的第二个参数就是与该命令相关的函数,比如dmesg,对应kdb_dmesg,这就意味着一旦你执行dmesg命令,背后真正会被调用的函数就是kdb_dmesg().
所有的命令最终被加入到一个全局变量kdb_commands中,这相当于一张表.它默认的大小是50,换言之,你可以定义50个命令,当然你也可以定义更多,有一个变量记录了最大的命令数,这就是kdb_max_commands.
   8540 +       /*
   8541 +        * kdb_commands describes the available commands.
   8542 +        */
   8543 +static kdbtab_t *kdb_commands;
   8544 +static int kdb_max_commands;
3.      kdb_id_init()
   7724 +/*
   7725 + * kdb_disinit
   7726 + *
   7727 + *     Initialize the disassembly information structure
   7728 + *     for the GNU disassembler.
   7729 + *
   7730 + * Parameters:
   7731 + *     None.
   7732 + * Outputs:
   7733 + *     None.
   7734 + * Returns:
   7735 + *     Zero for success, a kdb diagnostic if failure.
   7736 + * Locking:
   7737 + *     None.
   7738 + * Remarks:
   7739 + */
   7740 +
   7741 +void __init
   7742 +kdb_id_init(void)
   7743 +{
   7744 +       kdb_di.stream           = NULL;
   7745 +       kdb_di.application_data = NULL;
   7746 +       kdb_di.symbols          = NULL;
   7747 +       kdb_di.num_symbols      = 0;
   7748 +       kdb_di.flags            = 0;
   7749 +       kdb_di.private_data     = NULL;
   7750 +       kdb_di.buffer           = NULL;
   7751 +       kdb_di.buffer_vma       = 0;
   7752 +       kdb_di.buffer_length    = 0;
   7753 +       kdb_di.bytes_per_line   = 0;
   7754 +       kdb_di.bytes_per_chunk = 0;
   7755 +       kdb_di.insn_info_valid = 0;
   7756 +       kdb_di.branch_delay_insns = 0;
   7757 +       kdb_di.data_size        = 0;
   7758 +       kdb_di.insn_type        = 0;
   7759 +       kdb_di.target           = 0;
   7760 +       kdb_di.target2          = 0;
   7761 +}
最没技术含量的一个函数.这其中kdb_di是定义在kdb/kdb_id.c中的一个结构体变量:
   7544 +disassemble_info kdb_di;
一个全局变量.
4.      kdba_init()
kdb后面带个a,a表示arch,说明这个函数是体系结构相关的.换言之,那三个patch中的后两个都会包含这个函数.i386需要它自己的这个函数,x86_64需要属于它的同一个函数.我们来看i386的.
   8604 +/*
   8605 + * kdba_init
   8606 + *
   8607 + *     Architecture specific initialization.
   8608 + *
   8609 + * Parameters:
   8610 + *     None.
   8611 + * Returns:
   8612 + *     None.
   8613 + * Locking:
   8614 + *     None.
   8615 + * Remarks:
   8616 + *     None.
   8617 + */
   8618 +
   8619 +void __init
   8620 +kdba_init(void)
   8621 +{
   8622 +       kdba_arch_init();       /* Need to register KDBENTER_VECTOR early */
   8623 +       kdb_register("pt_regs", kdba_pt_regs, "address", "Format struct pt_regs", 0);
   8624 +       kdb_register("stackdepth", kdba_stackdepth, "[percentage]", "Print processes using >= stack percentage", 0);
   8625 +
   8626 +       return;
   8627 +}
这里有包含两个函数.kdba_arch_init().
   8592 +static int __init
   8593 +kdba_arch_init(void)
   8594 +{
   8595 +#ifdef CONFIG_SMP
   8596 +       set_intr_gate(KDB_VECTOR, kdb_interrupt);
   8597 +#endif
   8598 +       set_intr_gate(KDBENTER_VECTOR, kdb_call);
   8599 +       return 0;
   8600 +}
原来是设置两个中断门.这里涉及了两个中断向量.KDB_VECTOR和KDBENTER_VECTOR.与之想绑定的分别是两个函数,kdb_interrupt和kdb_call.
而kdb_register()其实就是kdb_register_repeat()的另一版本.等于说这里又注册了pt_regs和stackdepth这两个命令.
 12139 +/*
 12140 + * kdb_register
 12141 + *
 12142 + *     Compatibility register function for commands that do not need to
 12143 + *     specify a repeat state. Equivalent to kdb_register_repeat with
 12144 + *     KDB_REPEAT_NONE.
 12145 + *
 12146 + * Inputs:
 12147 + *     cmd     Command name
 12148 + *     func    Function to execute the command
 12149 + *     usage   A simple usage string showing arguments
 12150 + *     help    A simple help string describing command
 12151 + * Outputs:
 12152 + *     None.
 12153 + * Returns:
 12154 + *     zero for success, one if a duplicate command.
 12155 + * Locking:
 12156 + *     none.
 12157 + * Remarks:
 12158 + *
 12159 + */
 12160 +
 12161 +int
 12162 +kdb_register(char *cmd,
 12163 +            kdb_func_t func,
 12164 +            char *usage,
 12165 +            char *help,
 12166 +            short minlen)
 12167 +{
 12168 +       return kdb_register_repeat(cmd, func, usage, help, minlen, KDB_REPEAT_NONE);
 12169 +}
Linux内核中像这种注册或者说登记的函数到处都是,从本质上来说,无非就是有一张表,然后同一类型的变量或者数据或者函数或者指针都登记到这张表里去,便于统一管理.这种思想应用到现实中来,就好比北京市公安局打着迎奥运的名义,要求外来人口去办暂住证一样,都办了暂住证,为奥运操心的公仆们就能很好的管理我们这些刁民,贱民和草民了.
5.      kdb_cmd_init()
 12278 +/*
 12279 + * kdb_cmd_init
 12280 + *
 12281 + *     This function is called by the kdb_init function to execute any
 12282 + *     commands defined in kdb_cmds.
 12283 + *
 12284 + * Inputs:
 12285 + *     Commands in *kdb_cmds[];
 12286 + * Outputs:
 12287 + *     None.
 12288 + * Returns:
 12289 + *     None.
 12290 + * Locking:
 12291 + *     None.
 12292 + * Remarks:
 12293 + *
 12294 + */
 12295 +
 12296 +static void __init
 12297 +kdb_cmd_init(void)
 12298 +{
 12299 +       int i, diag;
 12300 +       for (i = 0; kdb_cmds[i]; ++i) {
 12301 +               if (!defcmd_in_progress)
 12302 +                       kdb_printf("kdb_cmd[%d]: %s", i, kdb_cmds[i]);
 12303 +               diag = kdb_parse(kdb_cmds[i]);
 12304 +               if (diag)
 12305 +                       kdb_printf("command failed, kdb diag %d/n", diag);
 12306 +       }
 12307 +       if (defcmd_in_progress) {
 12308 +               kdb_printf("Incomplete 'defcmd' set, forcing endefcmd/n");
 12309 +               kdb_parse("endefcmd");
 12310 +       }
 12311 +}
这又是初始化什么呢?kdb_cmds是一个数组.所有的命令都被添加到这里.
     35 extern char *kdb_cmds[]; char __initdata *kdb_cmds[]
它有许许多多个成员,每一个成员表示一条命令,当然你也可以说每一个成员表示一个字符串.
defcmd_in_progress是一个全局变量,默认当然是0.
而kdb_parse()将解析这个命令,如果这个命令能够在kdb_commands这张表里面找到,那么与这个命令相关的函数将会执行.比如说你执行dmesg命令,那么kdb_dmesg()函数就会被调用.
初始化基本上要好了,咱们调用kdb()函数.这个函数是kdb中最核心的函数之一.这个函数我们以后会有大把的机会和它打交道.这里由于传递进来的参数比较特别,实际上它不会做什么事情.
紧接着, atomic_notifier_chain_register是注册一个notifier chain.kdb_block.notifier chain是一种通知机制,我们不去深究它,事实上要深究也没问题,网上关于notifier chain的文章也不少.这里我们只想说,我们通过这行代码实际上绑定了一个kdb_panic()函数,在系统崩溃的时候,这个函数会被调用,从而进入kdb.kdb_panic以及kdb_block的定义都在下面:
 12313 +/*
 12314 + * kdb_panic
 12315 + *
 12316 + *     Invoked via the panic_notifier_list.
 12317 + *
 12318 + * Inputs:
 12319 + *     None.
 12320 + * Outputs:
 12321 + *     None.
 12322 + * Returns:
 12323 + *     Zero.
 12324 + * Locking:
 12325 + *     None.
 12326 + * Remarks:
 12327 + *     When this function is called from panic(), the other cpus have already
 12328 + *     been stopped.
 12329 + *
 12330 + */
 12331 +
 12332 +static int
 12333 +kdb_panic(struct notifier_block *self, unsigned long command, void *ptr)
 12334 +{
 12335 +       KDB_FLAG_SET(CATASTROPHIC);     /* kernel state is dubious now */
 12336 +       KDB_ENTER();
 12337 +       return 0;
 12338 +}
 12339 +
 12340 +static struct notifier_block kdb_block = { kdb_panic, NULL, 0 };
以后我们会看到KDB_ENTER()这个宏将引领我们进入kdb.只是此刻我想,kernel崩溃了,可以进入kdb,看代码的我崩溃了,找谁去?
再然后是register_cpu_notifier,这又是注册啥呢?暂时不得而知.
接下来,kdbjmpbuf,很明显,这是一个setjmp buffer,用于异常处理.setjmp和longjmp通常组合起来用,前者用于保存程序的运行时的堆栈环境,后者则用来恢复先前保存的程序堆栈环境.这二者一起使用就能提供传说中那种在程序中实现非本地跳转的机制(non-local goto).关于setjmp和longjmp,网上相关的文章那是比阿娇的艳照还要多,这里就不多说了.(当然,阿娇的照片之所以少,主要是警察叔叔有功!)与kdbjmpbuf相对应的setjmp函数我们以后会看到,叫做kdba_setjmp(),与之相对应的longjmp函数则叫做kdba_longjmp.简而言之,我们会先调用kdba_setjmp()来初始化kdbjmpbuf,或者说将CPU中大部分影响到程序执行的寄存器存入kdbjmpbuf,而日后等我们使用kdba_longjmp()函数之后,能够使程序再次跳转会kdba_setjmp()的位置.
需要多说一句,从这里的申请内存时使用的NR_CPUS我们知道,这个buffer实际上是为每个CPU都准备了一个.
最后,kdb_initial_cpu设置为-1.kdb_wait_for_cpus_secs也赋了一个值,如果你是单cpu那么这个变量就被设置为10.你可以用kdb察看一下.比如下面我的机器里看到的那样:
kdb> md kdb_wait_for_cpus_secs
0xffffffff8062db18 000000000000000a 0000000000000000   ................
0xffffffff8062db28 0000000000000000 0000000000000000   ................
0xffffffff8062db38-0xffffffff8062db87 zero suppressed
0xffffffff8062db88 0000000000000000 0000000000000000   ................
0a当然就是10.
这样,kdb的初始化就算是完成了.

你可能感兴趣的:(function,command,Parameters,代码分析,variables,locking)