Linux引入了虚拟文件系统,从而使设备的访问能够像访问普通文件系统同样。所以在内核中描述打开文件的数据inode中的rdev成员用来记录设备文件对应到的设备号。设备文件也由一个对应的file_operations 数据对象,用来描述设备的操做接口。设备文件系统最先是采用devfs实现的,可是后来由于种种缘由在2.6之后的内核中已经将其废弃而转而使用udev,他来本质上是没有区别都是在设备添加到系统中时在/dev目录下产生设备文件(机制相同),可是不一样的是策略devfs的策略是将设备文件的建立过程放在了内核空间,而udev的策略是由内核提供机制而用户空间提供策略从而完成设备的建立,因此如今设备文件管理由两个软件可用分别是PC平台上的udev和嵌入式平台上的mdev。node
接口均已经废弃,不在详细探究。linux
devfs_handle_t devfs_mk_dir(devfs_handle_t dir,const char* name,void* info); devfs_handle_t devfs_register(devfs_handle_t de,const char* name,unsigned int flag,uinsigned int major,unsigned int minor,umode_t mode,void* ops,void* info); devfs_handle_t devfs_unregister(devfs_handle_t de);
与devfs不一样udev彻底工做在用户空间,而内核经过netlink机制将,设备添加过程的相关信息经过netlink发送到用户空间的udev程序,netlink机制能够不理解是一种特殊的socket进程通信方式,用户空间的udev接收到设备添加的信息后将完成设备文件的建立和设备文件操做接口等相关的配置和初始化操做。进而用户空间程序就能像访问普通文件同样访问设备了。编程
比较好奇设备文件在被多个用户进程打开后后续fops操做接口的file和inode是同一个仍是个进程单独一个,由于file中还由一个private_data成员在驱动编程中是比较重要的因此接下来进行专门的验证。
编写一个虚拟的设备驱动以下数据结构
#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/device.h> #include <asm/uaccess.h> #include <asm/io.h> //file open operation function static int char_drv_open(struct inode *inode , struct file *filp) { printk(KERN_EMERG "inode:%08x file:%08x\n",inode,filp); printk(KERN_EMERG "private_data:%08x\n",filp->private_data); printk(KERN_EMERG "inode:%08x\n",inode); filp->private_data = 0x5A5A5A5A; return 0; } //file read operation function static int char_drv_read(struct file *filp , char __user *buf , size_t cnt , loff_t *offt) { return 0; } //file write operation function static int char_drv_write(struct file *filp,const char __user *buf , size_t cnt , loff_t *offt) { return 0; } //file close operation function static int char_drv_release(struct inode *inode , struct file *filp) { return 0; } //file operation function struct static struct file_operations my_test_fop= { .owner = THIS_MODULE, .open = char_drv_open, .read = char_drv_read, .write = char_drv_write, .release = char_drv_release }; /* 设备结构体 */ struct test_dev { dev_t devid; /* 设备号 */ struct cdev cdev; /* cdev */ struct class *class; /* 类 */ struct device *device; /* 设备 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ }; #define NEWCHRLED_CNT 1 /* 设备号个数 */ #define NEWCHRLED_NAME "newchrdev" /* 名字 */ struct test_dev test_char_dev; //module init function static int __init char_drv_test_init(void) { //same hardware init //apply device num alloc_chrdev_region(&test_char_dev.devid, 0, NEWCHRLED_CNT,NEWCHRLED_NAME); test_char_dev.major = MAJOR(test_char_dev.devid); /* 获取主设备号 */ test_char_dev.minor = MINOR(test_char_dev.devid); /* 获取次设备号 */ printk(KERN_EMERG "major:%d minor:%d\n",test_char_dev.major ,test_char_dev.minor); //init dev struct cdev_init(&test_char_dev.cdev,&my_test_fop); //add dev to system cdev_add(&test_char_dev.cdev ,test_char_dev.devid ,NEWCHRLED_CNT ); //build class test_char_dev.class = class_create(THIS_MODULE,"test2"); if (IS_ERR(test_char_dev.class)) { return PTR_ERR(test_char_dev.class); } //build device test_char_dev.device = device_create(test_char_dev.class,NULL ,test_char_dev.devid,NULL,"test2"); if (IS_ERR(test_char_dev.device)) { return PTR_ERR(test_char_dev.device); } return 0; } //module uninstall function static void __exit char_drv_test_exit(void) { /* 注销字符设备 */ cdev_del(&test_char_dev.cdev); /* 删除 cdev */ unregister_chrdev_region(test_char_dev.devid, NEWCHRLED_CNT); device_destroy(test_char_dev.class, test_char_dev.devid); class_destroy(test_char_dev.class); } //module function band module_init(char_drv_test_init); module_exit(char_drv_test_exit); //license and author MODULE_LICENSE("GPL"); MODULE_AUTHOR("Smile");
编写应用程序以下:app
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { int fd = open("/dev/fileinode",O_RDWR); if(fd<0) { perror("open"); } getc(stdin); return 0; }
结论
通过实验验证,设备文件每打开一次,内核都会在内核空间建立file对象而inode是指向同一数据块。具体输出以下:socket
//安装模块 [ 466.459870] major:250 minor:0 //第一次执行 [ 534.073202] inode:f437da00 file:f02cea80 [ 534.073205] private_data:00000000 [ 534.073207] inode:f437da00 //第二次执行 [ 548.632708] inode:f437da00 file:f02bd000 [ 548.632712] private_data:00000000 [ 548.632713] inode:f437da00