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.o
KDIR := /lib/modules/2.6.38-16-generic/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
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...)
#endif
MODULE_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