1Linux Device & Module
设备管理与模块机制
?基本概念
?传统方式的设备注册与管理
?devfs注册与管理
?块设备的请求队列
?网络设备
?模块机制
2Linux Device & Module
基本概念
? 字符设备、块设备、网络设备
字符设备以字节为单位进行数据处理,通常只允许按顺序访问
块设备将数据按可寻址的块为单位进行处理,可以随机访问,利用缓冲技术
网络设备是一类特殊的设备,每块网卡有名字但没有设备文件与之对应
查看系统中的设备,/proc/devices
? 主设备号和次设备号
major number,相同的设备使用相同的驱动程序
minor number,用来区分具体设备的实例
查看设备及其类型,ls -l /dev”
? 设备文件系统 devfs
/dev目录过于庞大,很多设备文件没有对应系统中的设备
devfs根据系统中的实际设备构建设备文件,并按目录存放,如 /dev/disk,
/dev/pts
3Linux Device & Module
基本概念
用户空间
内核空间
I/ O 请求
设备驱动程序
设 备
IS R
系统调用
re t _f ro m _s y s _c al l
返回,进程继续
4Linux Device & Module
基本概念
用户程序调用
Fd = f open (, /de v / hda,,O _ RD W R,0); re a d(buff,fd,s i z e ) w rit e ( fd,bu ff,s i z e ) c los e ( fd )
V irt ual f il e s y s te m
G e ner ic _f il e _re a d() G e ner ic _f il e _w rit e ()
块设备文件
建立设备:
#mknod /dev/dev_name type major_number minor_number
5Linux Device & Module
VFS中的文件
? include/linux/fs.h
struct file {
……
struct file_operations *f_op;
};
struct file_operations {
loff_t (*llseek)(struct file *,loff_t,int);
ssize_t (*read)(struct file *,char *,size_t,loff_t *);
ssize_t (*write)(struct file *,const char *,size_t,loff_t *);
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(*release) (struct inode *,struct file *);
int(*fsync) (struct file *,struct dentry *,int datasync);
int(*fasync) (int,struct file *,int); ……
};
6Linux Device & Module
(1) llseek(file,offset,whence),修改文件的读写指针。
(2) read(file,buf,count,offset),从设备文件的 offset 处开始读出 count个字
节,然后增加 *offset的值。
(3) write(file,buf,count,offset),从设备文件的 offset处写入 count个字节,
然后增加 *offset的值。
(4) ioctl(inode,file,cmd,arg),向一个硬件设备发命令,对设备进行控制。
(5) mmap(file,vma),将设备空间映射到进程地址空间。
(6) open(inode,file),打开并初始化设备。
(7) release(inode,file),关闭设备并释放资源。
(8) fsync(file,dentry),实现内存与设备之间的同步通信。
(9) fasync(file,on),实现内存与设备之间的异步通信。
7Linux Device & Module
? fs/devices.c
struct device_struct {
const char * name;
struct file_operations * fops;
};
static struct device_struct chrdevs[MAX_CHRDEV];
注册与注销函数:
int register_chrdev(unsigned int major,const char * name,struct
file_operations *fops)
int unregister_chrdev(unsigned int major,const char * name);
注,major即设备的主设备号,注册后就是访问数组 chrdevs
的索引(下标)。
字符设备的注册与管理
8Linux Device & Module
PCI设备(驱动实现见 word文档)
?Linux内核启动时会对所有 PCI设备进行扫描、登录和分配资源等初始化
操作,建立起系统中所有 PCI设备的拓扑结构
?此后当内核欲初始化某设备时,调用 module_init加载该设备的驱动程
序
9Linux Device & Module
块设备
? fs/block_dev.c
static struct {
const char *name;
struct block_device_operations *bdops;
} blkdevs[MAX_BLKDEV];
10Linux Device & Module
块设备注册
?fs/block_dev.c
? register_blkdev(unsigned int major,const char *name,
struct block_device_operations *bdops)
? int unregister_blkdev(unsigned int major,
const char * name)
11Linux Device & Module
块设备的操作 block_device_operations
struct block_device_operations {
int (*open) (struct inode *,struct file *);
int (*release) (struct inode *,struct file *);
int (*ioctl) (struct inode *,struct file *,unsigned,
unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t);
struct module *owner;
};
12Linux Device & Module
? block_device_operations{}并不能完全提供
file_operations结构中的所必需的主要函数(例如
read,write),所以内核实际上是采用
def_blk_fops变量对相关的 file_operations{}变量进
行了赋值:
struct file_operations def_blk_fops ;
? 除了 open,release等函数利用了设备注册时提供
的 block_device_operations{}结构中的成员变量之
外,其他函数都是采用所有块设备通用的操作函
数 (def_blk_fops{})
块设备的缺省操作 def_blk_fops
13Linux Device & Module
?fs/block_dev.c
struct file_operations def_blk_fops = {
open,blkdev_open,
release,blkdev_close,
llseek,block_llseek,
read,generic_file_read,
write,generic_file_write,
mmap,generic_file_mmap,
fsync,block_fsync,
ioctl,blkdev_ioctl,
};
块设备的缺省操作 def_blk_fops
14Linux Device & Module
?block_read与 block_write等函数是设备相关
的
?块设备注册时一个重要的任务就是提供这
个设备相关的操作函数给内核
15Linux Device & Module
devfs注册与管理
?fs/devfs/base.c
? register_chrdev()停止使用,改为 devfs_register_chrdev()
? register_blkdev()停止使用,改为 devfs_register_blkdev()
? int devfs_register_chrdev (unsigned int major,const char
*name,struct file_operations *fops)
? int devfs_register_blkdev (unsigned int major,const char
*name,struct block_device_operations *bdops)
? int devfs_unregister_chrdev (unsigned int major,const char
*name)
? int devfs_unregister_blkdev (unsigned int major,const char
*name)
16Linux Device & Module
块设备的请求队列
? 当系统对块设备进行读操作时,仅仅是通过块设备通用的
读操作函数 block_read( ),将这一个请求发送给对应的设
备,并保存在该设备的操作请求队列( request queue) 中
。然后调用这个块设备的底层处理函数,对请求队列中的
操作请求进行逐一的执行
struct blk_dev_struct { /*include/linux/blkdev.h*/
request_queue_t request_queue;
queue_proc *queue;
void *data;
};
struct blk_dev_struct blk_dev[MAX_BLKDEV];
17Linux Device & Module
block_read()流程
block_read( )
ll_rw_block( )
submit_bh ( )
generic_make_request ( )
__make_request ( )
add_request ( ),给 请求队列添加新的请求
18Linux Device & Module
Linux网络协议栈
应用系统
内核
硬件设备
应用层
B S D S oc ke t 层
I N E T S oc ke t 层
IP 层
硬件层
T C P U D P
网络设备接口
19Linux Device & Module
重要的数据结构
? 以 socket文件描述符作为参数,系统调用从用户空
间切换到内核空间,从而进入到 BSD Socket层的
操作。操作的对象是 socket{}结构,每一个这样的
结构对应的是一个网络连接
? 通过网络地址族的不同来判断是否应该进入到
INET Socket层;这一层的数据存放在 msghdr{}结
构的变量中
? 在 INET Socket层中,分成面向连接和面向无连接
两种类型,区分 TCP和 UDP协议。在这一层中的
操作对象是 sock{}类型的数据,而数据存放在
sk_buff{}结构中
20Linux Device & Module
模块机制( Module)
? Linux的单块结构( monolithic) 使得其可扩展性
较差
? 模块机制( Linux Kernel Module,LKM) 提高了
linux内核的可扩展性
? 利用 linux源码编译生成内核时,如某功能允许
,m”选项(其他为,y”,,n”),说明可以以
模块形式存在
? 多数设备驱动程序以模块的方式挂接到内核
? 系统启动时已将若干模块挂入了内核
? 用户只要有权限,就可以编写模块挂入内核
? 模块的缺点:增加了内核管理代价
21Linux Device & Module
模块的设计
? Every LKM consists of two basic functions (minimum),
int init_module(void) /*used for all initialization stuff*/
{,.,}
void cleanup_module(void) /*used for a clean
shutdown*/
{,.,}
? 安装模块命令
# insmod module.o
#modprobe module.o
? 卸载模块命令
# rmmod module.o
? 查询系统中已装入的模块
#lsmod
22Linux Device & Module
模块的设计
? 例子 hello.c
#define MODULE
#include <linux/module.h>
int init_module(void) { printk("Hello,world\n"); return 0; }
void cleanup_module(void) { printk("Goodbye cruel world\n"); }
? 编译模块
# gcc –c hello.c –DMODULE –D__KERNEL__
-DLINUX -Wall –O2 -I/usr/src/linux-2.4/include
? 安装、卸载模块
# insmod hello.o
Hello world
# rmmod hello
Goodbye cruel world
23Linux Device & Module
模块设计注意事项
? 模块设计与应用程序设计
模块是装入内核的,运行时 CPU处于核心态
应用程序运行时 CPU处于用户态
? 编译模块
设计应用程序使用的 include文件,/usr/include
设计内核模块使用的 include文件,/usr/src/linux-
2.4/include
两者如果不一致,在编译内核模块时要用 -I指明 include路
径
? 设计的模块可以调用 Linux内核及其他模块已经输出(
exported) 的函数,不能利用标准 C提供的库函数如 printf
? #more /proc/ksyms或
24Linux Device & Module
模块调试
? printk
? 利用 /proc文件系统或 ioctl;
? ksyms
? 使用调试器
?gdb
?kdebug
?远程调试
25Linux Device & Module
/proc/<pid>/下文件、目录的意义
文件 /目录名 描述
Cmdline 该进程的命令行参数
Cwd 进程运行的当前路径的符号链接
Environ 该进程运行的环境变量
Exe 该进程相关的程序的符号链接
Fd 包含该进程使用的文件描述符
Maps 可执行文件或库文件的内存映像
Mem 该进程使用的内存
Root 该进程所有者的家 ( home) 目录
Stat 进程状态
Statm 进程的内存状态
Status 用易读的方式表示的进程状态
26Linux Device & Module
SGI公司 OSS项目 的 kgdb
本地主机
Xw in d o w 环
境
G DB /XG DB
远程主机
S tu b 程序
内核
应用程序
串口或 T C P /I P 连接
GDB 远程调试环境原理图
设备管理与模块机制
?基本概念
?传统方式的设备注册与管理
?devfs注册与管理
?块设备的请求队列
?网络设备
?模块机制
2Linux Device & Module
基本概念
? 字符设备、块设备、网络设备
字符设备以字节为单位进行数据处理,通常只允许按顺序访问
块设备将数据按可寻址的块为单位进行处理,可以随机访问,利用缓冲技术
网络设备是一类特殊的设备,每块网卡有名字但没有设备文件与之对应
查看系统中的设备,/proc/devices
? 主设备号和次设备号
major number,相同的设备使用相同的驱动程序
minor number,用来区分具体设备的实例
查看设备及其类型,ls -l /dev”
? 设备文件系统 devfs
/dev目录过于庞大,很多设备文件没有对应系统中的设备
devfs根据系统中的实际设备构建设备文件,并按目录存放,如 /dev/disk,
/dev/pts
3Linux Device & Module
基本概念
用户空间
内核空间
I/ O 请求
设备驱动程序
设 备
IS R
系统调用
re t _f ro m _s y s _c al l
返回,进程继续
4Linux Device & Module
基本概念
用户程序调用
Fd = f open (, /de v / hda,,O _ RD W R,0); re a d(buff,fd,s i z e ) w rit e ( fd,bu ff,s i z e ) c los e ( fd )
V irt ual f il e s y s te m
G e ner ic _f il e _re a d() G e ner ic _f il e _w rit e ()
块设备文件
建立设备:
#mknod /dev/dev_name type major_number minor_number
5Linux Device & Module
VFS中的文件
? include/linux/fs.h
struct file {
……
struct file_operations *f_op;
};
struct file_operations {
loff_t (*llseek)(struct file *,loff_t,int);
ssize_t (*read)(struct file *,char *,size_t,loff_t *);
ssize_t (*write)(struct file *,const char *,size_t,loff_t *);
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(*release) (struct inode *,struct file *);
int(*fsync) (struct file *,struct dentry *,int datasync);
int(*fasync) (int,struct file *,int); ……
};
6Linux Device & Module
(1) llseek(file,offset,whence),修改文件的读写指针。
(2) read(file,buf,count,offset),从设备文件的 offset 处开始读出 count个字
节,然后增加 *offset的值。
(3) write(file,buf,count,offset),从设备文件的 offset处写入 count个字节,
然后增加 *offset的值。
(4) ioctl(inode,file,cmd,arg),向一个硬件设备发命令,对设备进行控制。
(5) mmap(file,vma),将设备空间映射到进程地址空间。
(6) open(inode,file),打开并初始化设备。
(7) release(inode,file),关闭设备并释放资源。
(8) fsync(file,dentry),实现内存与设备之间的同步通信。
(9) fasync(file,on),实现内存与设备之间的异步通信。
7Linux Device & Module
? fs/devices.c
struct device_struct {
const char * name;
struct file_operations * fops;
};
static struct device_struct chrdevs[MAX_CHRDEV];
注册与注销函数:
int register_chrdev(unsigned int major,const char * name,struct
file_operations *fops)
int unregister_chrdev(unsigned int major,const char * name);
注,major即设备的主设备号,注册后就是访问数组 chrdevs
的索引(下标)。
字符设备的注册与管理
8Linux Device & Module
PCI设备(驱动实现见 word文档)
?Linux内核启动时会对所有 PCI设备进行扫描、登录和分配资源等初始化
操作,建立起系统中所有 PCI设备的拓扑结构
?此后当内核欲初始化某设备时,调用 module_init加载该设备的驱动程
序
9Linux Device & Module
块设备
? fs/block_dev.c
static struct {
const char *name;
struct block_device_operations *bdops;
} blkdevs[MAX_BLKDEV];
10Linux Device & Module
块设备注册
?fs/block_dev.c
? register_blkdev(unsigned int major,const char *name,
struct block_device_operations *bdops)
? int unregister_blkdev(unsigned int major,
const char * name)
11Linux Device & Module
块设备的操作 block_device_operations
struct block_device_operations {
int (*open) (struct inode *,struct file *);
int (*release) (struct inode *,struct file *);
int (*ioctl) (struct inode *,struct file *,unsigned,
unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t);
struct module *owner;
};
12Linux Device & Module
? block_device_operations{}并不能完全提供
file_operations结构中的所必需的主要函数(例如
read,write),所以内核实际上是采用
def_blk_fops变量对相关的 file_operations{}变量进
行了赋值:
struct file_operations def_blk_fops ;
? 除了 open,release等函数利用了设备注册时提供
的 block_device_operations{}结构中的成员变量之
外,其他函数都是采用所有块设备通用的操作函
数 (def_blk_fops{})
块设备的缺省操作 def_blk_fops
13Linux Device & Module
?fs/block_dev.c
struct file_operations def_blk_fops = {
open,blkdev_open,
release,blkdev_close,
llseek,block_llseek,
read,generic_file_read,
write,generic_file_write,
mmap,generic_file_mmap,
fsync,block_fsync,
ioctl,blkdev_ioctl,
};
块设备的缺省操作 def_blk_fops
14Linux Device & Module
?block_read与 block_write等函数是设备相关
的
?块设备注册时一个重要的任务就是提供这
个设备相关的操作函数给内核
15Linux Device & Module
devfs注册与管理
?fs/devfs/base.c
? register_chrdev()停止使用,改为 devfs_register_chrdev()
? register_blkdev()停止使用,改为 devfs_register_blkdev()
? int devfs_register_chrdev (unsigned int major,const char
*name,struct file_operations *fops)
? int devfs_register_blkdev (unsigned int major,const char
*name,struct block_device_operations *bdops)
? int devfs_unregister_chrdev (unsigned int major,const char
*name)
? int devfs_unregister_blkdev (unsigned int major,const char
*name)
16Linux Device & Module
块设备的请求队列
? 当系统对块设备进行读操作时,仅仅是通过块设备通用的
读操作函数 block_read( ),将这一个请求发送给对应的设
备,并保存在该设备的操作请求队列( request queue) 中
。然后调用这个块设备的底层处理函数,对请求队列中的
操作请求进行逐一的执行
struct blk_dev_struct { /*include/linux/blkdev.h*/
request_queue_t request_queue;
queue_proc *queue;
void *data;
};
struct blk_dev_struct blk_dev[MAX_BLKDEV];
17Linux Device & Module
block_read()流程
block_read( )
ll_rw_block( )
submit_bh ( )
generic_make_request ( )
__make_request ( )
add_request ( ),给 请求队列添加新的请求
18Linux Device & Module
Linux网络协议栈
应用系统
内核
硬件设备
应用层
B S D S oc ke t 层
I N E T S oc ke t 层
IP 层
硬件层
T C P U D P
网络设备接口
19Linux Device & Module
重要的数据结构
? 以 socket文件描述符作为参数,系统调用从用户空
间切换到内核空间,从而进入到 BSD Socket层的
操作。操作的对象是 socket{}结构,每一个这样的
结构对应的是一个网络连接
? 通过网络地址族的不同来判断是否应该进入到
INET Socket层;这一层的数据存放在 msghdr{}结
构的变量中
? 在 INET Socket层中,分成面向连接和面向无连接
两种类型,区分 TCP和 UDP协议。在这一层中的
操作对象是 sock{}类型的数据,而数据存放在
sk_buff{}结构中
20Linux Device & Module
模块机制( Module)
? Linux的单块结构( monolithic) 使得其可扩展性
较差
? 模块机制( Linux Kernel Module,LKM) 提高了
linux内核的可扩展性
? 利用 linux源码编译生成内核时,如某功能允许
,m”选项(其他为,y”,,n”),说明可以以
模块形式存在
? 多数设备驱动程序以模块的方式挂接到内核
? 系统启动时已将若干模块挂入了内核
? 用户只要有权限,就可以编写模块挂入内核
? 模块的缺点:增加了内核管理代价
21Linux Device & Module
模块的设计
? Every LKM consists of two basic functions (minimum),
int init_module(void) /*used for all initialization stuff*/
{,.,}
void cleanup_module(void) /*used for a clean
shutdown*/
{,.,}
? 安装模块命令
# insmod module.o
#modprobe module.o
? 卸载模块命令
# rmmod module.o
? 查询系统中已装入的模块
#lsmod
22Linux Device & Module
模块的设计
? 例子 hello.c
#define MODULE
#include <linux/module.h>
int init_module(void) { printk("Hello,world\n"); return 0; }
void cleanup_module(void) { printk("Goodbye cruel world\n"); }
? 编译模块
# gcc –c hello.c –DMODULE –D__KERNEL__
-DLINUX -Wall –O2 -I/usr/src/linux-2.4/include
? 安装、卸载模块
# insmod hello.o
Hello world
# rmmod hello
Goodbye cruel world
23Linux Device & Module
模块设计注意事项
? 模块设计与应用程序设计
模块是装入内核的,运行时 CPU处于核心态
应用程序运行时 CPU处于用户态
? 编译模块
设计应用程序使用的 include文件,/usr/include
设计内核模块使用的 include文件,/usr/src/linux-
2.4/include
两者如果不一致,在编译内核模块时要用 -I指明 include路
径
? 设计的模块可以调用 Linux内核及其他模块已经输出(
exported) 的函数,不能利用标准 C提供的库函数如 printf
? #more /proc/ksyms或
24Linux Device & Module
模块调试
? printk
? 利用 /proc文件系统或 ioctl;
? ksyms
? 使用调试器
?gdb
?kdebug
?远程调试
25Linux Device & Module
/proc/<pid>/下文件、目录的意义
文件 /目录名 描述
Cmdline 该进程的命令行参数
Cwd 进程运行的当前路径的符号链接
Environ 该进程运行的环境变量
Exe 该进程相关的程序的符号链接
Fd 包含该进程使用的文件描述符
Maps 可执行文件或库文件的内存映像
Mem 该进程使用的内存
Root 该进程所有者的家 ( home) 目录
Stat 进程状态
Statm 进程的内存状态
Status 用易读的方式表示的进程状态
26Linux Device & Module
SGI公司 OSS项目 的 kgdb
本地主机
Xw in d o w 环
境
G DB /XG DB
远程主机
S tu b 程序
内核
应用程序
串口或 T C P /I P 连接
GDB 远程调试环境原理图