1. 程式人生 > >字元裝置驅動資料結構

字元裝置驅動資料結構

前言
資料結構是字元裝置驅動的核心,其它很多程式碼都是圍繞資料結構展開的,所以有必要對字元裝置驅動資料結構做一個梳理,先看三張圖,便於從巨集觀上了解字元裝置驅動程式.
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

一.管理系統裝置號分配
定義:

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的相應操作。