linux 驱动私有数据
最近在学习字符设备驱动,遇到了一个私有数据的结构
很多教程,例如原子,只是把需要用到的结构体在open时候,把地址传递给filp->private_data
然后再read或write等时候,通过filp->private_data再赋值结构体
原子以及网络上大部分的说法是,避免使用全局变量,实际上全局变量依旧存在,只不过在fops成员函数里面访问时候,不再对全局变量进行直接访问,而通过指针访问。
真正避免全局变量使用应该是在open时候通过kzalloc申请内存,用于存储私有数据,在使用完后进行kfree释放。
这里试图阐述说明私有数据的真实用途。
什么是私有数据?
struct file *filp
在 struct filed有个成员void *private_data
这个字段允许驱动程序在不同的文件操作(如read、write、ioctl等)中访问与特定设备相关的私有信息。
为何要使用私有数据?(私有数据应用目的)
真实目的,为了在同一个驱动支持多个应用程序同时访问时,为各个设备准备的数据结构互相不冲突,避免并发访问问题。
在Linux驱动程序中,通常需要支持多个设备实例。
每个设备都有自己的状态和配置。如果使用全局变量,所有设备将共享这些变量,这在多设备环境下会造成严重问题,因为无法区分不同设备的状态。
私有数据如何实现?
应用程序调用驱动过程
加载一个驱动模块,产生一个设备文件,有唯一对应的inode结构体
应用层调用open函数打开设备文件,对于上层open调用到内核时会发生一次软中断,从用户空间进入到内核空间。
open会调用到sys_open(内核函数),sys_open根据文件的地址,找到设备文件对应的struct inode结构体描述的信息,可以知道接下来要操作的设备类型(字符设备还是块设备),还会分配一个struct file结构体。
根据struct inode结构体里面记录的主设备号和次设备号,在驱动链表(管理所有设备的驱动)里面,根据找到字符设备驱动
每个字符设备都有一个struct cdev结构体。此结构体描述了字符设备所有信息,其中最重要的一项就是字符设备的操作函数接口
找到struct cdev结构体后,linux内核就会将struct cdev结构体所在的内存空间首地址记录在struct inode结构体i_cdev成员中,将struct cdev结构体中的记录的函数操作接口地址记录在struct file结构体的f_ops成员中。
执行xxx_open驱动函数。
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{struct open_flags op;int fd = build_open_flags(flags, mode, &op);struct filename *tmp;if (fd)return fd;tmp = getname(filename);if (IS_ERR(tmp))return PTR_ERR(tmp);fd = get_unused_fd_flags(flags);if (fd >= 0) {struct file *f = do_filp_open(dfd, tmp, &op);if (IS_ERR(f)) {put_unused_fd(fd);fd = PTR_ERR(f);} else {fsnotify_open(f);fd_install(fd, f);}}putname(tmp);return fd;
}
do_filp_open这里可以看到,内核为打开的文件创建一个新的’struct file’实例
struct file {union {struct llist_node fu_llist;struct rcu_head fu_rcuhead;} f_u;struct path f_path;struct inode *f_inode; /* cached value */const struct file_operations *f_op;/** Protects f_ep_links, f_flags.* Must not be taken from IRQ context.*/spinlock_t f_lock;atomic_long_t f_count;unsigned int f_flags;fmode_t f_mode;struct mutex f_pos_lock;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_SECURITYvoid *f_security;
#endif/* needed for tty driver, and maybe others */void *private_data;#ifdef CONFIG_EPOLL/* Used by fs/eventpoll.c to link all the hooks to this file */struct list_head f_ep_links;struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */struct address_space *f_mapping;
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */struct file_handle {__u32 handle_bytes;int handle_type;/* file identifier */unsigned char f_handle[0];
};
在linux操作系统中,每打开一次文件,Linux操作系统会在VFS层分配一个struct file结构体来描述打开的文件
这里可以看到,每次open都会新建一个实例,这样每个实例都有自己对应的private_data私有函数。这里private_data使用void型,可以指向任何类型的数据结构。
虽然linux使用的c语言,实际上应用了大量面向对象的编程思维。
参考网站:https://blog.csdn.net/ZHUhapi/article/details/138683130