Linux驱动程序开发基础, -->内核模块编程,内核的调试方法

来源:互联网 发布:c语言文件指针 编辑:程序博客网 时间:2024/06/06 04:59

1. 内核模块编程

加载模块: insmod ./hello.ko
卸载模块
: rmmod ./hello.ko
查看模块: lsmod | grep hello
查看模块更多信息:modinfo hello
查看模块输出: dmesg

示例: insmod  hello.ko num=999

    grep add_integer /proc/kallsyms

hello.c

/*************************************************************************    > File Name: hello.c    > Author: kevin xiang     > Mail:    > Created Time: 2014年07月29日 星期二 11时16分53秒 ************************************************************************/#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int num = 4000;static int hello_init(void){printk(KERN_ALERT "Hello world enter\n");printk(KERN_INFO "num is: %d\n", num);return 0;}static void hello_exit(void){printk(KERN_ALERT "Hello world exit\n");}int add_integer(int a, int b){return a+b;}module_init(hello_init);module_exit(hello_exit);module_param(num, int, S_IRUGO);EXPORT_SYMBOL(add_integer);MODULE_AUTHOR("kevin");MODULE_DESCRIPTION("a simple hello world module");MODULE_ALIAS("a simplest module");
Makefile

obj-m := hello.oKDIR :=  /lib/modules/2.6.38-16-generic/buildall:make -C $(KDIR) M=$(PWD) modulesclean:rm -f *.ok *.o *.mod.o *.mod.c *.symvers

2. 驱动程序访问硬件的特殊性

DMA: 存在于外设中的一个硬件控制器,它的作用是 不需要CPU协助,就可以搬移内存的数据到外设的存储设备中,或者反向搬。

通过程序配置DMA控制器,告诉DMA控制器他可以访问的内存地址,DMA读写内存完成后,通过中断告诉CPU。

I/O子系统: 在嵌入式系统中,是实现对外围附属设备进行控制的有效手段,通过对I/O端口进行0或1的操作,可以发指令或者传递信息给附属设备。

Arch/arm/mach-s5pv210/mach-smdkv210.c



3. Linux设备模型
kobject

kset

sysfs


udev



4. Linux驱动分类

字符设备: 一般以串行(字节)顺序依次访问,典型的包括触摸屏,鼠标,键盘等。

块设备:



网络设备: 以态网类的设备  net_device。

杂项设备: 无法归类的部分,或者复合设备。


5. 内核的调试方法

printk

内核通过 printk() 输出的信息具有日志级别,日志级别是通过在 printk() 输出的字符串前加一个带尖括号的整数来控制的,如 printk("<6>Hello, world!/n");。内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应。
#define KERN_EMERG    "<0>"    /* system is unusable */
#define KERN_ALERT    "<1>"    /* action must be taken immediately */
#define KERN_CRIT     "<2>"    /* critical conditions */
#define KERN_ERR      "<3>"    /* error conditions */
#define KERN_WARNING  "<4>"    /* warning conditions */
#define KERN_NOTICE   "<5>"    /* normal but significant */
#define KERN_INFO     "<6>"    /* informational */
#define KERN_DEBUG    "<7>"    /* debug-level messages */

所以 printk() 可以这样用:printk(KERN_INFO "Hello, world!/n");。
未指定日志级别的 printk() 采用的默认级别是 DEFAULT_MESSAGE_LOGLEVEL,这个宏在 kernel/printk.c 中被定义为整数 4,即对应KERN_WARNING。
/proc/sys/kernel/printk 会显示4个数值(可由 echo 8 > /proc/sys/kernel/printk修改),分别表示当前控制台日志级别、未明确指定日志级别的默认消息日志级别、最小(最高)允许设置的控制台日志级别、引导时默认的日志级别。当 printk() 中的消息日志级别小于当前控制台日志级别时,printk 的信息(要有/n符)就会在控制台上显示。但无论当前控制台日志级别是何值,通过 /proc/kmsg (或使用dmesg)总能查看。另外如果配置好并运行了 syslogd 或 klogd,没有在控制台上显示的 printk 的信息也会追加到 /var/log/messages.log 中。

char myname[] = "chinacodec/n";
printk(KERN_INFO "Hello, world %s!/n", myname);


通过宏定义来控制调试信息的打开与关闭, 下面这段code 可以通过定义DEBUG_0开控制TS_DEBUG是否打印信息

//#define DEBUG_0 1#ifdef DEBUG_0#define TS_DEBUG(fmt, args...) printk(fmt, ##args)#else#define TS_DEBUG(fmt, args...)#endifMODULE_LICENSE("Dual BSD/GPL");static int num = 4000;static int hello_init(void){TS_DEBUG("this is debug info: %d\n", num);printk(KERN_ALERT "Hello world enter\n");printk(KERN_INFO "num is: %d\n", num);return 0;}

oops: 这是内核在发生panic (错误)时,所产生的一个信息。

http://www.cnblogs.com/wwang/archive/2010/11/14/1876735.html

在Linux内核开发中的Oops是什么呢?其实,它和上面的解释也没什么本质的差别,只不过说话的主角变成了Linux。当某些比较致命的问题出现时,我们的Linux内核也会抱歉的对我们说:“哎呦(Oops),对不起,我把事情搞砸了”。Linux内核在发生kernel panic时会打印出Oops信息,把目前的寄存器状态、堆栈内容、以及完整的Call trace都show给我们看,可以使用dmesg查看,这样就可以帮助我们定位错误。

kprobe:用于调试在运行的内核中的代码,可以再关键API函数的前后插入一段code,打印信息。

下面一段code,编译为module安装后,会在do_execve调用前执行handler_pre。   do_execve在终端执行ls 内核则会调用, 然后dmesg查看打出的info.

/*************************************************************************    > File Name: kprobe.c    > Author: kevin xiang     > Mail:     > Created Time: 2014年07月29日 星期二 16时37分43秒 ************************************************************************/#include <linux/module.h>#include <linux/kprobes.h>#include <linux/kallsyms.h>#include <linux/sched.h>struct kprobe kp;int handler_pre(struct kprobe *p, struct pt_regs *regs){printk(KERN_INFO "pt_regs: %p, pid: %d, jiffies: %ld\n", regs, current->tgid, jiffies);return 0;}static __init int init_kprobe_sample(void){kp.symbol_name = "do_execve";kp.pre_handler = handler_pre;register_kprobe(&kp);return 0;}module_init(init_kprobe_sample);static __exit void cleanup_kprobe_sample(void){unregister_kprobe(&kp);}module_exit(cleanup_kprobe_sample);MODULE_LICENSE("GPL");


kcore:在运行的内核的内存印像文件,位置是/proc/kcore








0 0