学习计划:
1.vfs:虚拟文件系统
VFS的作用就是采用标准的Unix系统调用读写位于不同物理介质上的不同文件系统。VFS是一个可
以让open()、read()、write()等系统调用不用关心底层的存储介质和文件系统类型就可以工作的
粘合层。在古老的DOS操作系统中,要访问本地文件系统之外的文件系统需要使用特殊的工具才能
进行。而在Linux下,通过VFS,一个抽象的通用访问接口屏蔽了底层文件系统和物理介质的差异性。
这是Linux文件系统对外的接口。任何要使用文件系统的程序都必须经由这层接口来使用它。
2.字符设备驱动
作业:
memmove与memcopy的区别
void *memcpy(void *dst, const void *src, size_t count);
void *memmove(void *dst, const void *src, size_t count);
memcpy与memmove的目的都是将N个字节的源内存地址的内容拷贝到目标内存地址中。
但当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。
memmove的处理措施:
(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝
(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝
(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝
内容:
0.设备驱动分类:
块设备驱动、字符设备驱动、网络设备驱动。
块设备、字符设备都有设备文件,而网络设备则是以网络套接字来实现的,如:eth0 eth1等
1.inode号:设备编号,用户态都是通过设备编号来访问设备文件,一个文件至少占用一个逻辑块,
一般都是一个文件对应多个逻辑块
1).通过 ln -s xxx xxx 可在两个文件之间实现软链接
2.字符设备驱动
定义:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先
后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。:
查看已有设备:cat /proc/devices
字符设备编写步骤:
1).实例化一个cdev设备对象结构体
2).分配设备号,注册字符设备(静态分配和动态分配)
3).初始化字符设备
4).将字符设备加入到系统文件当中
3.重要的数据结构:
file_operations, file,和 inode.
(1).inode结构体:
dev_t:字符设备,i_rdev:设备号;设备号一共占32位;
一个字符设备必然会对应一个设备,*i_cdev,void * i_private
inode包含文件访问权限,属主,属组,大小,生成时间,访问时间,最后修改时间等信息,结构体如下:
struct inode {
struct hlist_node i_hash;
(有删减)
uid_t i_uid;//inode拥有者id
gid_t i_gid;//inode所属群组id
dev_t i_rdev;//若是设备文件,表示记录设备的设备号
u64 i_version;
loff_t i_size;//inode所代表大少
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime;//inode最近一次的存取时间
struct timespec i_mtime;//inode最近一次修改时间
struct timespec i_ctime;//inode的生成时间
(有删减)
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;//若是字符设备,对应的为cdev结构体
};
1.1字符设备结构体描述:cdev
struct cdev{
struct kobject kobj;/*内嵌的kobject对象*/
strcut module *owner;/*所属模块*/
struct file_operations *ops;/*文件操作结构体*/
struct list_head list;
dev_t dev;/*设备号,dev_t实质是一个32位整,12位为主设备号,20位为次设备号,
提取主次设备号的方法:MAJOR(dev_t dev),MINOR(dev_t dev),
生成dev_t的方法:MKDEV(int major,int minor)*/
unsigned int count;
};
1.2.linux2.6内核提供了一组函数来操作cdev结构体
void cdev_init(struct cdev *,struct file_operations *);/*初始化cdev的成员,
*并且建立cdev与file_operation的连接*/
struct cdev *cdev_alloc(void);/*动态申请一个cdev的内存空间*/
void cdev_put(struct cdev *p);
int cdev_add(struct cdev*,dev_t,unsigned);/*添加一个cdev,完成字符的注册*/
void cdev_del(struct cdev*);/*删除一个cdev,完成字符的注销*/
在调用cdev_add函数向系统注册字符设备之前,应该先调用register_chrdev_region()
函数或是alloc_chrdev_region()函数向系统申请设备号;模型为:
int register_chrdev_region(dev_t from,unsigned count,constchar *name);
int alloc_chrdev_region(dev_t *dev,unsignedbaseminor,unsigned count,const char *name)
在系统调用cdev_del函数从系统注销字符设备后,unregister_chrdev_region()应该释放之前申请的设备号
该函数原型为:
unregister_chrdev_region(dev_t from,unsigned count)
(2).file结构体:
系统中每个打开的文件在内核空间都会有一个关联的struct file结构体,它由内核打开文件
创建,在文件所有实例都关闭后,内核释放这个数据结构。在内核源代码中struct file结
构体的指针通常被命名为file或filp。这个结构体在内核中如下:
struct file {
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;//和文件关联的操作,file_operations结构体是字符设备核心
//函数操作集
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags;//文件标志,也就是文件打开方式O_RDONLY,O_WRONLY,ORDWR等
fmode_t f_mode;//文件读写模式,也就是权限
loff_t f_pos;//当前读写位置
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
void *private_data;//文件私有数据
#ifdef CONFIG_EPOLL
struct list_head f_ep_links;
#endif
struct address_space *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};
文件读写模式f_mode,标志f_flags都是设备驱动关心的内容,而私有数据指针*private_data在设备驱动中广泛
应用,大多数指向设备驱动自定义用于描述设备的结构体。
if(file->f_mode & FMODE_WRITE)//用户要求可写
if(file->f_flags & O_RDWR)//用于检查设备是否使用可读写方式打开
4.测试驱动程序
1).加载驱动程序:insomd xxxx.ko
2).查看是否加载成功:cat /proc/devices
3).创建设备文件:mknod demo3 c 11 xx;
demo0:文件名,c:字符设备文件,11:主设备号,xx:次设备号(0-255)之间
4).运行设备文件:cat demo3
5).使用测试文件测试驱动程序
6).写入命令:echo 12568585 >> demo3