Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48017
  • 博文数量: 37
  • 博客积分: 1800
  • 博客等级: 上尉
  • 技术积分: 451
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-29 19:56
文章存档

2011年(9)

2010年(28)

我的朋友

分类: LINUX

2010-12-26 16:03:21

(1) 热键: Alt + SysRq : 两个键同时按住不放,再按下面的键.
    b boot. 立刻重启系统. 确认先同步和重新加载磁盘.
    k 调用"安全注意键"( SAK ) 功能. SAK 杀掉在当前控制台的所有运行的进程, 给你一个干净的终端.
    r 关闭键盘原始模式; 用在一个崩溃的应用程序( 例如 X 服务器 )可能将你的键盘搞成一个奇怪的状态.
    s 进行一个全部磁盘的紧急同步.
    u umount. 试图重新加载所有磁盘在只读模式. 这个操作, 常常在 s 之后马上调用, 可以节省大量的文件系统检查时间, 在系统处于严重麻烦时.
    p 打印处理器消息.
    t 打印当前任务列表.
    m 打印内存信息.
(2) printk的输出:
   dmesg 命令: 查看历史---曾经打印的,输出的.这是我发现的最好的方式了.
   /var/log/messages : 很多printk信息,当然,最好的还是  dmesg 命令.
(3) /proc 接口
   <1> 默认创建文件到 /proc/ 目录下,例如下面的伪代码 创建  /proc/my_proc :
 创建: create_proc_read_entry(
  "my_proc",
  0744,   //文件属性 777 之类的东西.
  NULL,   //parent dir,如果为NULL则默认到 /proc/
  scull_read_procmem, //抄模板吧...没难度.
  (void *)0x12345678 // client data: 自定义,传递给scull_read_procmem的最后一个参数.
  );
  
 销毁: remove_proc_entry(
  "my_proc",
  NULL   //这个参数是 parent,NULL代表 parent= /proc/
  );
  
   <2> 可以自己创建子目录,子目录只能位于/proc/ 目录之下,当然还可以创建更深的子目录.
       下面的代码创建:  /proc/myporc/my_proc
      
 static struct proc_dir_entry *parent; //要作为全局变量保存起来,销毁时候需要使用.
 {
  //会在 /proc/ 下创建 myproc 子目录,注意代码中调用一次这个语句,就创建一个...
  parent = proc_mkdir("myproc", NULL);
  create_proc_read_entry(
   "my_proc",
   0744,
   parent,
   scull_read_procmem,
   (void *)0x12345678
  );
 }
 
 销毁:
 {
  //1) 带有自定义子目录的,先删除文件
  remove_proc_entry("my_proc", parent);
  //2) 然后需要删除 字目录
  remove_proc_entry("myproc",NULL);
 }
   参考代码: /home/gzw/LDD3/04/proc_test/
  

(4) seq_file 接口:
    也是创建到/proc/目录下, 内核提供了另外一套处理函数和框架而已,
    就是为了解决 proc 接口对大文件的支持不好的问题.小文件用 proc 接口很好了.
    <1> 创建
     struct proc_dir_entry *entry;
     entry = create_proc_entry(SEQ_FILE_NAME, 0, NULL);
     if (entry)
     {
        entry->proc_fops = &scull_proc_ops;
     }
    <2> 销毁
        remove_proc_entry(SEQ_FILE_NAME, NULL);
       
    <3> 原理和 scull_proc_ops
       
 static struct file_operations scull_proc_ops = {
  .owner   = THIS_MODULE,
  .open    = scull_proc_open, //需要自己定义(其实系统也有  seq_open 函数,试了一下,读取后得到"已杀死"三个字)
  .read    = seq_read,  //使用系统内建的: 在EXPORT TABLE中可以找到. /proc/kallsyms 中有.
  .llseek  = seq_lseek,  //同上
  .release = seq_release  //同上
 };
 内核其实内建了seq_read,seq_lseek,seq_release,seq_open 函数.多数情况下,需要自己的open函数,其他使用内核提供的.
 
 open函数标准模板如下:
  static int scull_proc_open(struct inode *inode, struct file *file)
  {
   return seq_open(file, &scull_seq_ops);
  }
 这里又来了scull_seq_ops操作,这才是主角:
 static struct seq_operations scull_seq_ops = {
  .start = scull_seq_start,
  .next  = scull_seq_next,
  .stop  = scull_seq_stop,
  .show  = scull_seq_show
  };
 我们必须实现上述几个函数:
  scull_seq_start
  scull_seq_next
  scull_seq_show
 当我们 cat 一个 创建的文件,会走这样的流程:
         open -->start --->show -->next-->show -->next --->show 直到 next函数返回空.
        
   <4> 原型和接口:
 static int scull_seq_show(struct seq_file *s, void *v)
 show()函数最重要的是 参数 v,实际上就是  start 和 next 函数的返回值.
 这里用函数: seq_printf(s,"%2.2X ", *p0); 就可以让 cat 显示出来!
 show()函数 返回0表示成功,其他是错误码.
 
 static void *scull_seq_start(struct seq_file *s, loff_t *pos)
 返回值,在show函数中,作为v参数传入. 相当于第一次的 next函数 .
 
 static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
 返回值,在show函数中,作为v参数传入.
 
 static void scull_seq_stop(struct seq_file *s, void *v)
 直接返回即可.
 
 所有的逻辑完全由我们自己决定,包括那个 *pos,*v 这样的东西
 
 我可以这样安排:一个大的二维数组,例如: static unsigned char my_seq_data_array[8][16];
 用 *pos 作为 [*pos][16]
 每次把对应的地址作为next的返回值.然后,show中就显示 v 指定的地址中的数据.....
 
 参考代码: /home/gzw/LDD3/04/proc_test/
 

(5) insmod 之后,会产生几个虚拟文件系统的文件,下面以usb_boot_jz.ko为例:
    # ls /sys/module/usb_boot_jz/
 drivers/usb:skeleton -> ../../../bus/usb/drivers/skeleton
  
 sections/  这里存放了本驱动的全部sections,每个文件就一行,表示该section的起始地址.
 sections/__mcount_loc 内容就一行内容,0xf812ded0
 initstate 表示初始化状态
 refcnt  引用计数,数字串
 srcversion 源码版本,字符串串内容,例如: B0654CD51F7F598E4F6833F
    调试器 gdb 需要 sections/ 中存放的段地址信息---因为模块不是静态的,不会出现在 静态内核中.
    gdb的调试方法是把内核当做一个APP来调试:
           # gdb /usr/src/linux/vmlinux   /proc/kcore
    vmlinux 是是非压缩的 ELF 内核可执行文件的名字,编译出来的,不是 zImage 或者 bzImage.
    kcore:  用来代表内核"可执行文件", 以一个核心文件的形式, 是一个巨大的文件,
            因为他代表整个的内核地址空间, 对应于所有的物理内存.当你任何时候读取这个文件时,
            系统都会给你最新的内核可执行文件.
(6) gdb 调试APP是如此之简单: /home/gzw/LDD3/04/gdb_hello
    <1> 编译APP必须加入 -g 选项
       例如: # cc -g tst.c -o tst
    <2> 使用GDB调试:
       # gdb tst <---------- 启动GDB
      
       启动后,出现gdb提示符,然后可以使用各种gdb命令
       (gdb) l <-------------------- l 命令相当于list,从第一行开始例出原码。
          下面就会列出源码...
          help 命令可以列出帮助.
          q命令退出.
    可以调试内核吗?好像可以,有时间在细研究吧.
 
(7)其他调试内核的工具:
  strace 命令:  要执行的程序用  strace 启动,可以看到调用关系,输出格式: 
   函数(参数1,参数2。..) =返回值
   例如: close(3) = 0
// todo:下面几个有时间在看吧.
  kgdb
  Linux 追踪工具:
  好像比较有用: 
  Dynamic Probes ( DProbes ):
// todo: 问题: 关于 console_loglevel
   <1> 一直想寻找 uBuntu下的 console  终端,还是没找到.
   <2> 单独开一个窗口: # cat /proc/kmsg   可以随时看到 内核 printk的情况,似乎不太准,有时没有.
   <3> /proc/sys/kernel/printk: 记录了 printk 的级别,没有别的.好像没啥用啊.
   <4> 源码: setconsole 是个APP,编译过了.但是运行时候,总是报错:
       ./setconsole 1
       输出结果: ./setconsole: ioctl(stdin, TIOCLINUX): Invalid argument
   <5> 源码: setlevel是个APP,编译成功,运行也能成功改变 /proc/sys/kernel/printk 中的值,
       但是不起作用---一直试图寻找一个终端,可以随时看到printk的输出,没找到.
          echo 5 > /proc/sys/kernel/printk
          dmesg -n 5
          ./setlevel 5  #这是LDD3中提供的小程序
          程序中可以使用系统调用:  sys_syslog() 设置.
       下面4种方法等效,并且全局作用---cat /proc/sys/kernel/printk 可以看到第一个参数变了.
       重新启动后如何? 恢复默认值 4 应该是 DEFAULT_CONSOLE_LOGLEVEL, 这个值就是: 叫做console_loglevel
  
  <6>总之: 还是没找到方法把printk直接输出到窗口,只有用 dmesg 看.
     理论上是这样说的: printk中的级别与 console_loglevel 进行比较,只有当信息的日志级别小于console_loglevel这
     个整数变量的值时,信息才会在当前控制台上显示出来。
     验证发现: printk的级别和 console_loglevel 没啥关系! 在uBuntu下都printk都不能直接在终端显示,
               只能通过 dmesg 显示.
 
本章调试源码:
文件: 04.rar
大小: 473KB
下载: 下载
阅读(390) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~