现在的位置: 首页 > 综合 > 正文

linux-2.6 内核模块编程探索

2013年09月10日 ⁄ 综合 ⁄ 共 5326字 ⁄ 字号 评论关闭
转自:http://hi.baidu.com/zkheartboy/blog/item/6331d5cec964f60093457e0d.html

一个LINUX 内核模块编程的手记, 未写完不断更新中…

一 相关命令
0 查看系统装载了哪些 内核模块
lsmod modulename

1 加载内核模块
insmod modulename

2 卸载内核模块
rmmod modulename

3 创建设备文件
mknod filename dev_type major_number minor_number
说明: 参数 filename 创建的设备文件名称
dev_type 设备文件的类型,
c character device (字符设备)
b block device (块设备)
两种文件类型的主要区别, block device 有 buffer
major_number 主要号码 用于标记那个驱动可以使用此设备(每一个驱动只有唯一的 )
minor_number 镜像号码 the driver to distinguish between the various hardware it controls(不能准确翻译)

———————————————
例:
ls -l /dev/hda[1-3]
brw-rw—- 1 root disk 3, 1 Jul 5 2000 /dev/hda1
brw-rw—- 1 root disk 3, 2 Jul 5 2000 /dev/hda2
brw-rw—- 1 root disk 3, 3 Jul 5 2000 /dev/hda3

b 表示设备类型

3 表示 major number
1,2,3 表示 minor number
———————————————-

4 查看程序执行过程( 一般用于 检查程序用了哪些系统调用)
strace ./app

5 自动加载模块(调试好了再用, 要不系统容易挂)
depmod -a (检查内核 依赖关系是否正确) 生成 /lib/modules/version/modules.dep 文件
modprobe 在 insmod之前挂载 (内核模块也有依赖性~~) 可以编辑 /etc/modules.conf 让系统启动时加载 重要的,比如驱动
modprobe -a msdos (加载 于msdos有依赖关系的所有模块)

二 相关文件
1 /proc/modules.
存放加载内核的信息。 lsmod 就是读的这个文件

2/etc/modules.conf
让系统启动时加载 重要的,比如驱动

3/lib/modules/version/modules.dep
内核依赖关系 ( 猜的, 没看)

4/var/log/message
我是用turbo 10 桌面版本 作的测试 所以 printk() 函数 输出时不能显示在我的kde的终端上
这个问题曾经郁闷 我 20 分钟, 后来 情急之下 查看 message文件 才发现都printk输出信息都记录在 这里了, 我也够笨的:(
当知道怎么创建使用 "字符设备文件" 之后 就能写自己的 printx函数了。也知道为什么不能显示在 kede的终端上了 :)

5 /proc/devices
已经使用的设备文件 里面数据的结构是 major number device name

三 用户空间 VS 内核空间 | 系统调用 (有趣)
内核可以访问所有的资源,用户程序也是如此, 只不过,要借助驱动来访问。
内核要管理所有的资源,他是用户程序于硬件的一个桥梁。一个用户程序运行过程当中, 他把通过系统调用,把数据传送给内核空间, 然后内核处理这个请求, 之后又把结果返回给用户空间。内核可以捕获到所有的这些信息, 你可以替换到原来的系统调用, 你可一
让open打不开文件, 你可以让mkdir建立不了目录!!

三 该死的MakeFile!!!
一开始看的所有相关的文章,几乎都是基于2.4版本的,照着2。4版本的makefile写,但是我的内核是2.6的, 所以我百试不爽,编译完了不能加载,郁闷了好久,差一点没换掉内核,后来找到了2。6 的手册才知道问题所在。

2.6 版本内核模块的MakeFile 编译成.ko文件
————————————————————
ifneq ($(KERNELRELEASE),)
obj-m:= test.o #模块名称
else
KDIR:= /lib/modules/$(shell uname -r)/build  #内核路径
PWD:= $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
endif
————————————————————

2.4 版本内核模块的MakeFile 编译成.o文件
————————————————————
ARGET =test
WARN =-W -Wall -Wstrict-prototypes -Wmissing-prototypes
INCLUDE=-isystem /lib/modules/2.6.0-1/build/include  #内核路径
CFLAG =-O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE}
CC =gcc

${TARGET}.o: ${TARGET}.c
${CC} ${CFLAG} -c ${TARGET}.c
.PHONY: clean

clean:
rm -rf ${TARGET}.o
————————————————————

四 内核模块程序结构

————————————————-
/*
* hello-1.c - 简单的hello the world 程序摘自手册
*/
#include <linux/module.h>/* 所有的内核模块都需要的 */
#include <linux/kernel.h>/* Needed for KERN_ALERT */

//初始化程序,加载模块时执行
int init_module(void)
{
printk("<1>Hello world 1.n");  //本函数输出信息, 相当于用户空间的printf

/*
* 返回非0值,表示出错,内核不能被加载
*/
return 0;
}

//清除函数,卸载模块时执行
void cleanup_module(void)
{
printk(KERN_ALERT "Goodbye world 1.n");
}
——————————————————

2.6 版本有更多的书写方法,比如加入加载参数, 加入内核说明等等,可以参考相关文档

http://www.tldp.org/LDP/lkmpg/2.6/html/index.html

五 应用字符设备文件

5.1 数据结构
1 file_operations 在<linux/fs.h> 中定义

说明: 这个结构体中包含对设备操作的函数指针, 可以通过他的配置和实现操作函数来完成对设备的操作
把你不使用的入口 设为NULL。

—————————————————————————————-
struct file_operations {
struct module *owner;
loff_t(*llseek) (struct file *, loff_t, int);
ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t,loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int,unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t(*readv) (struct file *, const struct iovec *, unsigned long,loff_t *);
ssize_t(*writev) (struct file *, const struct iovec *, unsigned long,loff_t *);
ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t,void __user *);
ssize_t(*sendpage) (struct file *, struct page *, int, size_t,loff_t *, int);
unsigned long (*get_unmapped_area) (struct file *, unsigned long,unsigned long,
unsigned long,unsigned long);
};
—————————————————————————————-

靠,这家伙也太难记了,我曾经3次看到这,就去论坛灌水了,不想往下看了。 今儿耐着性子把他看完,才发现自己是个笨蛋!

在新的驱动程序中,你可能会看到一更简单,更友好的给file_operations 赋值的方式,看下面的这个结构,手册上说这玩意是
gcc extension gcc 扩展 (GNU c), 是这个意思吧, 反正我理解的就是,gcc在编译它的时候 先把下面这个玩意翻译正标准的c,然后再编译。 哀,可怜的 c标准!不过话又说回来,这东西除了linux别的也没什么用处。

——————————————
struct file_operations fops = {
read: device_read, //对应 相应的函数
write: device_write,
open: device_open,
release: device_release
};
——————————————

手册上说 下面的这个是 C99 特性写法!(这玩意BSD 也用不了吧? 为什么还要这么乱!) 注意:低版本的 gcc
可能会不正常编译地!因为它超出了GNU C 标准
(http://www-900.ibm.com/developerWorks/cn/linux/l-c99/index.shtml C99
标准参考)

——————————————
struct file_operations fops = {
.read = device_read, //对应 相应的函数
.write = device_write,
.open = device_open,
.release = device_release
};
——————————————

俺选择 的是第一种写法!

2 *file* 结构 在 <linux/fs.h> 中定义
说明:
* 它不同于FILE (glibc 中定义的)
* 它是内核级的结构体
* 它表示抽象的 'file'
* 它不是磁盘上的那个 file

* 驱动用结构体来给这个东西赋值,****** 完善理解 *******
* 指向它的指针类型 为 filp , 也可以直接写成 struct file file

5.2 相关函数

1 注册设备函数
<linux/fs.h>
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

还记得之前写的创建设备文件的命令吧,
参数说明: major: 它就是 major number 对应唯一的驱动,记住!
name: 它就是 filename 可以在 /proc/devices 里面找到它(叫device name 更好点)
fops: file_operations 结构体, 这个可以理解为操作设备的 内核入口,可以用上面说的两种方法去填充它

函数返回: 1 major 为 0: 动态分配的 major number 值
2 major 为 指定值:
3 major 为特殊数值:

特殊说明: 因为一个驱动只能有一 个 major值,所以有以下三种创建方法
1 先创建设备文件,你可以分配一个固定的major值
2 动态分配 major值, 读取 /proc/devices 文件 找到major值, 用脚本创建(在用户空间创建设备文件)
3 动态分配 major值,使用系统调用函数,直接创建设备文件 (在内核空间执行)

第 1 种方法不可取, 因为你不可预知 是否将来要 用到这个major number
第 2 种方法,需要在用户空间创建
第 3 种方法最好,可以动态的创建,销毁设备文件,不过麻烦

2 取消注册设备函数
关于:counter

抱歉!评论已关闭.