字符设备驱动数据结构

前言
数据结构是字符设备驱动的核心,其它很多代码都是围绕数据结构展开的,所以有必要对字符设备驱动数据结构做一个梳理,先看三张图,便于从宏观上了解字符设备驱动程序.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一.管理系统设备号分配
定义:

static struct char_device_struct {
	struct char_device_struct *next; // 指向下一个char_device_struct 结构体
	unsigned int major; // 主设备号
	unsigned int baseminor; // 从设备号起始值
	int minorct;// 从设备号数量
	char name[64];// 设备或驱动的名字
	struct cdev *cdev;		/* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; // CHRDEV_MAJOR_HASH_SIZE是255

说明:
1.新分配的设备号
从254开始搜索到0,如果chrdevs[i]为空,则让chrdevs[i]指向新分配的主设备号char_device_struct 结构体,i就是分配的设备号
在这里插入图片描述
2.cat /proc/devices 可以查看系统的字符设备和块设备,这些信息就取自chrdevs
内核代码如下:

void chrdev_show(struct seq_file *f, off_t offset)
{
	struct char_device_struct *cd;

	if (offset < CHRDEV_MAJOR_HASH_SIZE) {
		mutex_lock(&chrdevs_lock);
		for (cd = chrdevs[offset]; cd; cd = cd->next)
			seq_printf(f, "%3d %s\n", cd->major, cd->name);
		mutex_unlock(&chrdevs_lock);
	}
}

查询结果:
在这里插入图片描述

二.字符设备结构体
定义:

struct cdev {
	struct kobject kobj; // sysfs 内核对象
	struct module *owner;//该字符设备所在的内核模块(所有者)的对象指针,一般为THIS_MODULE主要用于模块计数 
	const struct file_operations *ops;//该结构描述了字符设备所能实现的操作集(打开、关闭、读/写、...),是极为关键的一个结构
	struct list_head list;//用来将已经向内核注册的所有字符设备形成链表
	dev_t dev; //字符设备的设备号,由主设备号和次设备号构成(如果是一次申请多个设备号,此设备号为第一
	unsigned int count;//隶属于同一主设备号的次设备号的个数
};

static struct cdev gpio_cdev;

说明:
1.cdev表示一个字符设备结构体,在字符设备注册时作为kobj_map 结构体的data数据成员

三.管理字符设备注册进系统
定义:

struct kobj_map {
	struct probe { // probe(探测)
		struct probe *next;
		dev_t dev; // 设备号
		unsigned long range;// 从设备号个数
		struct module *owner;
		kobj_probe_t *get;
		int (*lock)(dev_t, void *);
		void *data; // 存放cdev结构体指针
	} *probes[255]; // 对应每个探测到的字符设备
	struct mutex *lock;
};

static struct kobj_map *cdev_map;

说明:
1.cdev_map指针初始化:start_kernel()->vfs_caches_init()->chrdev_init()->kobj_map_init();
2.cdev_map指向每个注册到系统的字符设备
在这里插入图片描述

三.inode结构体
定义:

/*
 * Keep mostly read-only and often accessed (especially for
 * the RCU path lookup and 'stat' data) fields at the beginning
 * of the 'struct inode'
 */
struct inode {
	umode_t			i_mode;
	unsigned short		i_opflags;
	uid_t			i_uid;
	gid_t			i_gid;
	unsigned int		i_flags;

#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl	*i_acl;
	struct posix_acl	*i_default_acl;
#endif

	const struct inode_operations	*i_op;
	struct super_block	*i_sb;
	struct address_space	*i_mapping;

#ifdef CONFIG_SECURITY
	void			*i_security;
#endif

	/* Stat data, not accessed from path walking */
	unsigned long		i_ino;
	/*
	 * Filesystems may only read i_nlink directly.  They shall use the
	 * following functions for modification:
	 *
	 *    (set|clear|inc|drop)_nlink
	 *    inode_(inc|dec)_link_count
	 */
	union {
		const unsigned int i_nlink;
		unsigned int __i_nlink;
	};
	dev_t			i_rdev;
	struct timespec		i_atime;
	struct timespec		i_mtime;
	struct timespec		i_ctime;
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	unsigned short          i_bytes;
	blkcnt_t		i_blocks;
	loff_t			i_size;

#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif

	/* Misc */
	unsigned long		i_state;
	struct mutex		i_mutex;

	unsigned long		dirtied_when;	/* jiffies of first dirtying */

	struct hlist_node	i_hash;
	struct list_head	i_wb_list;	/* backing dev IO list */
	struct list_head	i_lru;		/* inode LRU list */
	struct list_head	i_sb_list;
	union {
		struct list_head	i_dentry;
		struct rcu_head		i_rcu;
	};
	atomic_t		i_count;
	unsigned int		i_blkbits;
	u64			i_version;
	atomic_t		i_dio_count;
	atomic_t		i_writecount;
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct file_lock	*i_flock;
	struct address_space	i_data;
#ifdef CONFIG_QUOTA
	struct dquot		*i_dquot[MAXQUOTAS];
#endif
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;// 字符设备驱动cdev结构体
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct hlist_head	i_fsnotify_marks;
#endif

#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	void			*i_private; /* fs or device private pointer */
};

说明:
inode结构体的详细解析参考深入浅出理解linux inode结构

VFS inode 包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是Linux 管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。
内核使用inode结构体在内核内部表示一个文件。因此,它与表示一个已经打开的文件描述符的结构体(即file 文件结构)是不同的,我们可以使用多个file 文件结构表示同一个文件的多个文件描述符,但此时,所有的这些file文件结构全部都必须只能指向一个inode结构体。
inode结构体包含了一大堆文件相关的信息,但是就针对驱动代码来说,我们只要关心其中的两个域即可:

(1) dev_t i_rdev;

  表示设备文件的结点,这个域实际上包含了设备号。

(2) struct cdev *i_cdev;

  struct cdev是内核的一个内部结构,它是用来表示字符设备的,当inode结点指向一个字符设备文件时,此域为一个指向inode结构的指针。

下面看一下上层应用open() 调用系统调用函数的过程
对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.
  由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个链表
在这里插入图片描述
首先,系统调用open打开一个字符设备的时候, 通过一系列调用,最终会执行到 chrdev_open

(最终是通过调用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 这一系列的调用过程,本文暂不讨论)

int chrdev_open(struct inode * inode, struct file * filp)

chrdev_open()所做的事情可以概括如下:

1. 根据设备号(inode->i_rdev), 在字符设备驱动模型中查找对应的驱动程序, 这通过kobj_lookup() 来实现, kobj_lookup()会返回对应驱动程序cdev的kobject.

2. 设置inode->i_cdev , 指向找到的cdev.

3. 将inode添加到cdev->list 的链表中.

4. 使用cdev的ops 设置file对象的f_op

5. 如果ops中定义了open方法,则调用该open方法

6. 返回

执行完 chrdev_open()之后,file对象的f_op指向cdev的ops,因而之后对设备进行的read, write等操作,就会执行cdev的相应操作。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值