/************************************************************************************html
*本文爲我的學習記錄,若有錯誤,歡迎指正。node
* http://www.169it.com/tech-qa-linux/article-5682294992603241339.html安全
* http://www.javashuo.com/article/p-rywmihdl-bm.html
數據結構
* https://www.cnblogs.com/wanghetao/archive/2012/05/28/2521675.htmlapp
* http://www.cnblogs.com/xiaojiang1025/p/6196198.html框架
* http://www.javashuo.com/article/p-dlfrucxn-bw.htmlide
* http://www.cnblogs.com/xiaojiang1025/p/6363626.html函數
************************************************************************************/oop
VFS inode包含文件訪問權限、屬主、組、大小、生成時間、訪問時間、最後修改時間等信息。它是linux管理文件系統的最基本單位,也是文件系統鏈接任何子目錄、文件的橋樑。
在Linux內核中,當建立一個文件時,就會在相應的文件系統建立一個inode與之對應,文件實體和文件的inode是一 一對應的,建立好一個inode會存在存儲器中,第一次open就會將inode在內存中有一個備份,同一個文件被屢次打開並不會產生多個inode,當全部被打開的文件都被close以後,inode在內存中的實例纔會被釋放。
當建立一個設備文件(mknod或udev)時,也會在文件系統中建立一個inode,該inode用來存儲關於這個文件的靜態信息,其中包括該設備文件對應的設備號、文件路徑以及對應的驅動對象等。
inode結構體中包含了設備文件的大量信息,着重關心如下結構體成員便可:
(1)dev_t i_rdev:表示設備文件對應的字符設備的設備號。
(2)struct cdev *i_cdev:指向字符設備對應的cdev結構體。
(3)const struct file_operations *i_fop:文件的操做方法集,建立設備文件的時候i_fops填充的是def_chr_fops,blk_blk_fops,def_fifo_fops,bad_sock_fops之一。
//linux/fs.h struct inode { struct hlist_node i_hash; /* 哈希表 */ struct list_head i_list; /* 索引節點鏈表 */ struct list_head i_dentry; /* 目錄項鍊表 */ unsigned long i_ino; /* 節點號 */ atomic_t i_count; /* 引用記數 */ umode_t i_mode; /* 訪問權限控制 */ unsigned int i_nlink; /* 硬連接數 */ uid_t i_uid; /* 使用者id */ gid_t i_gid; /* 使用者id組 */ kdev_t i_rdev; /* 實設備標識符 */ loff_t i_size; /* 以字節爲單位的文件大小 */ struct timespec i_atime; /* 最後訪問時間 */ struct timespec i_mtime; /* 最後修改(modify)時間 */ struct timespec i_ctime; /* 最後改變(change)時間 */ unsigned int i_blkbits; /* 以位爲單位的塊大小 */ unsigned long i_blksize; /* 以字節爲單位的塊大小 */ unsigned long i_version; /* 版本號 */ unsigned long i_blocks; /* 文件的塊數 */ unsigned short i_bytes; /* 使用的字節數 */ spinlock_t i_lock; /* 自旋鎖 */ struct rw_semaphore i_alloc_sem; /* 索引節點信號量 */ struct inode_operations *i_op; /* 索引節點操做表 */ struct file_operations *i_fop; /* 默認的索引節點操做 */ struct super_block *i_sb; /* 相關的超級塊 */ struct file_lock *i_flock; /* 文件鎖鏈表 */ struct address_space *i_mapping; /* 相關的地址映射 */ struct address_space i_data; /* 設備地址映射 */ struct dquot *i_dquot[MAXQUOTAS]; /* 節點的磁盤限額 */ struct list_head i_devices; /* 塊設備鏈表 */ struct pipe_inode_info *i_pipe; /* 管道信息 */ struct block_device *i_bdev; /* 塊設備 */ struct cdev *i_cdev; /* 字符設備 */ unsigned long i_dnotify_mask; /* 目錄通知掩碼 */ struct dnotify_struct *i_dnotify; /* 目錄通知 */ unsigned long i_state; /* 狀態標誌 */ unsigned long dirtied_when; /* 首次修改時間 */ unsigned int i_flags; /* 文件系統標誌 */ unsigned char i_sock; /* 多是個套接字吧 */ atomic_t i_writecount; /* 寫者記數 */ void *i_security; /* 安全模塊 */ __u32 i_generation; /* 索引節點版本號 */ union { void *generic_ip; /* 文件特殊信息 */ } u; };
Linux內核中,使用 file結構體描述一個已經打開的文件(設備對應於設備文件),系統中的每一個打開的文件在內核空間都有一個相應的struct file結構體,它由內核在打開文件時建立,並傳遞給在文件上進行操做的任何函數,直至文件被關閉。若是文件被關閉,內核就會釋放相應的數據結構。
Linux中同一個文件可被多個進程打開,該文件每被打開一次,內核就會在該進程中建立一個struct file來描述該文件。由此可知,一個文件在內核中可能對應多個struct file,可是該文件只有惟一一個struct inode與之對應。
struct file { union { struct list_head fu_list; //文件對象鏈表指針linux/include/linux/list.h struct rcu_head fu_rcuhead; //RCU(Read-Copy Update)是Linux 2.6內核中新的鎖機制 } f_u; struct path f_path; //包含dentry和mnt兩個成員,用於肯定文件路徑 #define f_dentry f_path.dentry //f_path的成員之一,當前文件的dentry結構 #define f_vfsmnt f_path.mnt //表示當前文件所在文件系統的掛載根目錄 const struct file_operations *f_op; //與該文件相關聯的操做函數 atomic_t f_count; //文件的引用計數(有多少進程打開該文件) unsigned int f_flags; //對應於open時指定的flag mode_t f_mode; //讀寫模式:open的mod_t mode參數 off_t f_pos; //該文件在當前進程中的文件偏移量 struct fown_struct f_owner; //該結構的做用是經過信號進行I/O時間通知的數據。 unsigned int f_uid, f_gid; //文件全部者id,全部者組id struct file_ra_state f_ra; //在linux/include/linux/fs.h中定義,文件預讀相關 unsigned long f_version; #ifdef CONFIG_SECURITY void *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; spinlock_t f_ep_lock; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; };
着重關注如下結構體成員:
(1)struct inode *f_inode:指向該文件對應的inode。
(2)const struct file_operations *f_op:驅動提供的file_operations對象,這個對象應該在第一次open()的時候被填充(調用char_open實現),直至該文件被close。
(1)當一個字符設備文件被建立的時候,內核會構造相應的inode,並對其進行初始化操做。
//fs/char_dev.c const struct file_operations def_chr_fops = { .open = chrdev_open, .llseek = noop_llseek, }; //fs/inode.c void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev) { inode->i_mode = mode; if (S_ISCHR(mode)) { inode->i_fop = &def_chr_fops; inode->i_rdev = rdev; } else if (S_ISBLK(mode)) { inode->i_fop = &def_blk_fops; inode->i_rdev = rdev; } else if (S_ISFIFO(mode)) inode->i_fop = &def_fifo_fops; else if (S_ISSOCK(mode)) inode->i_fop = &bad_sock_fops; else printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for" " inode %s:%lu\n", mode, inode->i_sb->s_id, inode->i_ino); }
(2)chrdev_open()
--359-->嘗試將inode->i_cdev(一個cdev結構指針)保存在局部變量p中;
--360-->若是p爲空,即inode->i_cdev爲空;
--364-->咱們就根據inode->i_rdev(設備號)經過kobj_lookup()搜索cdev_map,並返回與之對應kobj;
--367-->因爲kobject是cdev的父類,咱們根據container_of很容易找到相應的cdev結構並將其保存在inode->i_cdev中;
--374-->找到了cdev,咱們就能夠將inode->devices掛接到inode->i_cdev的管理鏈表中,這樣下次就不用從新搜索;
--378-->直接cdev_get()便可;
--386-->找到了咱們的cdev結構,咱們就能夠將其中的操做方法集inode->i_cdev->ops傳遞給filp->f_ops(386-390);
--392-->這樣,咱們就能夠回調咱們的設備打開字符設備fops中的char_opena函數。若是咱們沒有實現本身的open接口,就什麼都不作,也不是錯。
351 static int chrdev_open(struct inode *inode, struct file *filp) 352 { 353 const struct file_operations *fops; 354 struct cdev *p; 355 struct cdev *new = NULL; 356 int ret = 0; ... 359 p = inode->i_cdev; 360 if (!p) { 361 struct kobject *kobj; 362 int idx; ... 364 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); ... 367 new = container_of(kobj, struct cdev, kobj); 369 /* Check i_cdev again in case somebody beat us to it while 370 we dropped the lock. */ 371 p = inode->i_cdev; 372 if (!p) { 373 inode->i_cdev = p = new; 374 list_add(&inode->i_devices, &p->list); 375 new = NULL; 376 } else if (!cdev_get(p)) 377 ret = -ENXIO; 378 } else if (!cdev_get(p)) 379 ret = -ENXIO; ... 386 fops = fops_get(p->ops); ... 390 replace_fops(filp, fops); 391 if (filp->f_op->open) { 392 ret = filp->f_op->open(inode, filp); ... 395 } 396 397 return 0; 398 399 out_cdev_put: 400 cdev_put(p); 401 return ret; 402 }