设备管理与模块机制 基本概念 传统方式的设备注册与管理 devfs注册与管理 块设备的请求队列 网络设备 模块机制 Linux Device & Module
基本概念 字符设备、块设备、网络设备 主设备号和次设备号 设备文件系统devfs 字符设备以字节为单位进行数据处理,通常只允许按顺序访问 块设备将数据按可寻址的块为单位进行处理,可以随机访问,利用缓冲技术 网络设备是一类特殊的设备,每块网卡有名字但没有设备文件与之对应 查看系统中的设备:/proc/devices 主设备号和次设备号 major number:相同的设备使用相同的驱动程序 minor number:用来区分具体设备的实例 查看设备及其类型“ls -l /dev” 设备文件系统devfs /dev目录过于庞大,很多设备文件没有对应系统中的设备 devfs根据系统中的实际设备构建设备文件,并按目录存放,如/dev/disk,/dev/pts Linux Device & Module
基本概念 Linux Device & Module
基本概念 建立设备: #mknod /dev/dev_name type major_number minor_number Linux 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); …… Linux 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):实现内存与设备之间的异步通信。 Linux 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的索引(下标)。 Linux Device & Module
PCI设备(驱动实现见word文档) Linux内核启动时会对所有PCI设备进行扫描、登录和分配资源等初始化操作,建立起系统中所有PCI设备的拓扑结构 此后当内核欲初始化某设备时,调用module_init加载该设备的驱动程序 Linux Device & Module
块设备 fs/block_dev.c static struct { const char *name; struct block_device_operations *bdops; } blkdevs[MAX_BLKDEV]; Linux 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) Linux 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; }; Linux Device & Module
块设备的缺省操作def_blk_fops 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{}) Linux Device & Module
块设备的缺省操作def_blk_fops 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, }; Linux Device & Module
block_read与block_write等函数是设备相关的 块设备注册时一个重要的任务就是提供这个设备相关的操作函数给内核 Linux 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) Linux 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]; Linux Device & Module
generic_make_request ( ) block_read()流程 block_read( ) ll_rw_block( ) submit_bh ( ) generic_make_request ( ) __make_request ( ) add_request ( ),给请求队列添加新的请求 Linux Device & Module
Linux网络协议栈 Linux Device & Module
重要的数据结构 以socket文件描述符作为参数,系统调用从用户空间切换到内核空间,从而进入到BSD Socket层的操作。操作的对象是socket{}结构,每一个这样的结构对应的是一个网络连接 通过网络地址族的不同来判断是否应该进入到INET Socket层;这一层的数据存放在msghdr{}结构的变量中 在INET Socket层中,分成面向连接和面向无连接两种类型,区分TCP和UDP协议。在这一层中的操作对象是sock{}类型的数据,而数据存放在sk_buff{}结构中 Linux Device & Module
模块机制(Module) Linux的单块结构(monolithic)使得其可扩展性较差 模块机制(Linux Kernel Module,LKM)提高了linux内核的可扩展性 利用linux源码编译生成内核时,如某功能允许“m”选项(其他为“y”, “ n”),说明可以以模块形式存在 多数设备驱动程序以模块的方式挂接到内核 系统启动时已将若干模块挂入了内核 用户只要有权限,就可以编写模块挂入内核 模块的缺点:增加了内核管理代价 Linux 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 Linux Device & Module
模块的设计 例子 hello.c 编译模块 # gcc –c hello.c –DMODULE –D__KERNEL__ #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 Linux Device & Module
模块设计注意事项 模块设计与应用程序设计 模块是装入内核的,运行时CPU处于核心态 应用程序运行时CPU处于用户态 编译模块 设计应用程序使用的include文件:/usr/include 设计内核模块使用的include文件:/usr/src/linux-2.4/include 两者如果不一致,在编译内核模块时要用-I指明include路径 设计的模块可以调用Linux内核及其他模块已经输出(exported)的函数,不能利用标准C提供的库函数如printf #more /proc/ksyms或 Linux Device & Module
模块调试 printk 利用/proc文件系统或ioctl; ksyms 使用调试器 gdb kdebug 远程调试 Linux Device & Module
/proc/<pid>/下文件、目录的意义 文件/目录名 描述 Cmdline 该进程的命令行参数 Cwd 进程运行的当前路径的符号链接 Environ 该进程运行的环境变量 Exe 该进程相关的程序的符号链接 Fd 包含该进程使用的文件描述符 Maps 可执行文件或库文件的内存映像 Mem 该进程使用的内存 Root 该进程所有者的家(home)目录 Stat 进程状态 Statm 进程的内存状态 Status 用易读的方式表示的进程状态 Linux Device & Module
SGI公司OSS项目的kgdb Linux Device & Module