設備文件三大結構:inode,file,file_operations

来源:http://www.cnblogs.com/xiaojiang1025/archive/2017/02/04/6363626.html
-Advertisement-
Play Games

驅動程式就是向下控制硬體,向上提供介面,這裡的向上提供的介面最終對應到應用層有三種方式: 設備文件,/proc,/sys ,其中最常用的就是使用設備文件,而Linux設備中用的最多的就是字元設備,本文就以字元設備為例來分析創建並打開一個字元設備的文件內部機制。 struct inode Linux中 ...


驅動程式就是向下控制硬體,向上提供介面,這裡的向上提供的介面最終對應到應用層有三種方式:設備文件,/proc,/sys,其中最常用的就是使用設備文件,而Linux設備中用的最多的就是字元設備,本文就以字元設備為例來分析創建並打開一個字元設備的文件內部機制。

struct inode

Linux中一切皆文件,當我們在Linux中創建一個文件時,就會在相應的文件系統創建一個inode與之對應,文件實體和文件的inode是一一對應的,創建好一個inode會存在存儲器中,第一次open就會將inode在記憶體中有一個備份,同一個文件被多次打開並不會產生多個inode,當所有被打開的文件都被close之後,inode在記憶體中的實例才會被釋放。既然如此,當我們使用mknod(或其他方法)創建一個設備文件時,也會在文件系統中創建一個inode,這個inode和其他的inode一樣,用來存儲關於這個文件的靜態信息(不變的信息),包括這個設備文件對應的設備號,文件的路徑以及對應的驅動對象etc。inode作為VFS四大對象之一,在驅動開發中很少需要自己進行填充,更多的是在open()方法中進行查看並根據需要填充我們的file結構。
對於不同的文件類型,inode被填充的成員內容也會有所不同,以創建字元設備為例,我們知道,add_chrdev_region其實是把一個驅動對象和一個(一組)設備號聯繫到一起。而創建設備文件,其實是把設備文件設備號聯繫到一起。至此,這三者就被綁定在一起了。這樣,內核就有能力創建一個struct inode實例了,下麵是4.8.5內核中的inode。這個inode是VFS的inode,是最具體文件系統的inode的進一步封裝,也是驅動開發中關心的inode,針對具體的文件系統,還有struct ext2_inode_info 等結構。

//include/linux/fs.h
 596 /*
 597  * Keep mostly read-only and often accessed (especially for
 598  * the RCU path lookup and 'stat' data) fields at the beginning
 599  * of the 'struct inode'
 600  */
 601 struct inode {
 602         umode_t                 i_mode;
 603         unsigned short          i_opflags;
 604         kuid_t                  i_uid;
 605         kgid_t                  i_gid;
 606         unsigned int            i_flags;
 607 
 608 #ifdef CONFIG_FS_POSIX_ACL
 609         struct posix_acl        *i_acl;
 610         struct posix_acl        *i_default_acl;
 611 #endif
 612 
 613         const struct inode_operations   *i_op;
 614         struct super_block      *i_sb;
 615         struct address_space    *i_mapping;
 616 
 617 #ifdef CONFIG_SECURITY
 618         void                    *i_security;
 619 #endif
 620 
 621         /* Stat data, not accessed from path walking */
 622         unsigned long           i_ino;
 623         /*
 624          * Filesystems may only read i_nlink directly.  They shall use the
 625          * following functions for modification:
 626          *
 627          *    (set|clear|inc|drop)_nlink
 628          *    inode_(inc|dec)_link_count
 629          */
 630         union {
 631                 const unsigned int i_nlink;
 632                 unsigned int __i_nlink;
 633         };
 634         dev_t                   i_rdev;
 635         loff_t                  i_size;
 636         struct timespec         i_atime;
 637         struct timespec         i_mtime;
 638         struct timespec         i_ctime;
 639         spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
 640         unsigned short          i_bytes;
 641         unsigned int            i_blkbits;
 642         blkcnt_t                i_blocks;
 643                                 
 644 #ifdef __NEED_I_SIZE_ORDERED
 645         seqcount_t              i_size_seqcount;
 646 #endif
 647 
 648         /* Misc */
 649         unsigned long           i_state;
 650         struct rw_semaphore     i_rwsem;
 651 
 652         unsigned long           dirtied_when;   /* jiffies of first dirtying */
 653         unsigned long           dirtied_time_when;
 654 
 655         struct hlist_node       i_hash;
 656         struct list_head        i_io_list;      /* backing dev IO list */
 657 #ifdef CONFIG_CGROUP_WRITEBACK
 658         struct bdi_writeback    *i_wb;          /* the associated cgroup wb */
 659 
 660         /* foreign inode detection, see wbc_detach_inode() */
 661         int                     i_wb_frn_winner;
 662         u16                     i_wb_frn_avg_time;
 663         u16                     i_wb_frn_history;
 664 #endif
 665         struct list_head        i_lru;          /* inode LRU list */
 666         struct list_head        i_sb_list;
 667         struct list_head        i_wb_list;      /* backing dev writeback list */
 668         union {
 669                 struct hlist_head       i_dentry;
 670                 struct rcu_head         i_rcu;
 671         };
 672         u64                     i_version;
 673         atomic_t                i_count;
 674         atomic_t                i_dio_count;
 675         atomic_t                i_writecount;
 676 #ifdef CONFIG_IMA
 677         atomic_t                i_readcount; /* struct files open RO */
 678 #endif
 679         const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
 680         struct file_lock_context        *i_flctx;
 681         struct address_space    i_data;
 682         struct list_head        i_devices;
 683         union {
 684                 struct pipe_inode_info  *i_pipe;
 685                 struct block_device     *i_bdev;
 686                 struct cdev             *i_cdev;
 687                 char                    *i_link;
 688                 unsigned                i_dir_seq;
 689         };
 690 
 691         __u32                   i_generation;
 692 
 693 #ifdef CONFIG_FSNOTIFY
 694         __u32                   i_fsnotify_mask; /* all events this inode cares about */
 695         struct hlist_head       i_fsnotify_marks;
 696 #endif
 697 
 698 #if IS_ENABLED(CONFIG_FS_ENCRYPTION)
 699         struct fscrypt_info     *i_crypt_info;
 700 #endif
 701 
 702         void                    *i_private; /* fs or device private pointer */
 703 };    

這裡面與本文相關的成員主要有:

struct inode
--602-->i_mode表示訪問許可權控制
--604-->UID
--605-->GID
--606-->i_flags文件系統標誌
--630-->硬鏈接數計數
--635-->i_size以位元組為單位的文件大小
--636-->最後access時間
--637-->最後modify時間
--638-->最後change時間
--669-->i_dentry; //目錄項鏈表
--673-->i_count引用計數,當引用計數變為0時,會釋放inode實例
--675-->i_writecount寫者計數
--679-->創建設備文件的時候i_fops填充的是def_chr_fops,blk_blk_fops,def_fifo_fops,bad_sock_fops之一,參見創建過程中調用的init_special_inode()
--683-->特殊文件類型的union,pipe,cdev,blk.link etc,i_cdev表示這個inode屬於一個字元設備文件,本文中創建設備文件的時候會把與之相關的設備號的驅動對象cdev拿來填充
--702-->inode的私有數據

上面的幾個成員只有struct def_chr_fops 值得一追,後面有大用:

//fs/char_dev.c
429 const struct file_operations def_chr_fops = { 
430         .open = chrdev_open,
431         .llseek = noop_llseek,
432 };

struct file

Linux內核會為每一個進程維護一個文件描述符表,這個表其實就是struct file[]的索引。open()的過程其實就是根據傳入的路徑填充好一個file結構並將其賦值到數組中並返回其索引。下麵是file的主要內容

//include/linux/fs.h
 877 struct file {
 878         union {
 879                 struct llist_node       fu_llist;
 880                 struct rcu_head         fu_rcuhead;
 881         } f_u;
 882         struct path             f_path;
 883         struct inode            *f_inode;       /* cached value */
 884         const struct file_operations    *f_op;
 885 
 886         /*                                            
 887          * Protects f_ep_links, f_flags.
 888          * Must not be taken from IRQ context.
 889          */
 890         spinlock_t              f_lock;
 891         atomic_long_t           f_count;
 892         unsigned int            f_flags;
 893         fmode_t                 f_mode;
 894         struct mutex            f_pos_lock;
 895         loff_t                  f_pos;
 896         struct fown_struct      f_owner;
 897         const struct cred       *f_cred;
 898         struct file_ra_state    f_ra;f
 904         /* needed for tty driver, and maybe others */
 905         void                    *private_data;
 912         struct address_space    *f_mapping;
 913 } __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */

struct file
--882-->f_path里存儲的是open傳入的路徑,VFS就是根據這個路徑逐層找到相應的inode
--883-->f_inode里存儲的是找到的inode
--884-->f_op里存儲的就是驅動提供的file_operations對象,這個對象在open的時候被填充,具體地,應用層的open通過層層搜索會調用inode.i_fops->open,即chrdev_open()
--891-->f_count的作用是記錄對文件對象的引用計數,也即當前有多少個使用CLONE_FILES標誌克隆的進程在使用該文件。典型的應用是在POSIX線程中。就像在內核中普通的引用計數模塊一樣,最後一個進程調用put_files_struct()來釋放文件描述符。
--892-->f_flags當打開文件時指定的標誌,對應系統調用open的int flags,比如驅動程式為了支持非阻塞型操作需要檢查這個標誌是否有O_NONBLOCK。
--893-->f_mode;對文件的讀寫模式,對應系統調用open的mod_t mode參數,比如O_RDWR。如果驅動程式需要這個值,可以直接讀取這個欄位。
--905-->private_data表示file結構的私有數據

我在Linux設備管理(二)_從cdev_add說起一文中已經分析過chrdev_open(),這裡僅作概述。

//fs/chr_dev.c
348 /*
349  * Called every time a character special file is opened
350  */
351 static int chrdev_open(struct inode *inode, struct file *filp)
352 {
            /* 搜索cdev */
            ...
390         replace_fops(filp, fops);
391         if (filp->f_op->open) {
392                 ret = filp->f_op->open(inode, filp);
393                 if (ret)
394                         goto out_cdev_put;
395         } 
            ...
402 }

可以看出,這個函數有三個任務(劃重點!!!):

chrdev_open()
--352-389-->利用container_of等根據inode中的成員找到相應的cdev
--390-->用cdev.fops替換filp->f_op,即填充了一個空的struct file的f_op成員。
--392-->回調替換之後的filp->f_op->open,由於替換,這個其實就是cdev.fops

至此,我們知道了我們寫的驅動中的open()在何時會被回調,這樣我們就可以實現很多有意思的功能,比如,
我們可以在open中通過inode->cdev來識別具體的設備,並將其私有數據隱藏到file結構的private_data中,進而識別同一個驅動操作一類設備;
我們也可以在回調cdev.fops->open()階段重新填充file結構的fop,進而實現同一個驅動操作不同的設備,這種思想就是內核驅動中常用的分層!
最後總結一下這些結構之間的關係:





您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第十一節 linux定時任務 標簽(空格分隔): Linux實戰教學筆記 [更多資料點我查看][1] 1.1 定時任務Crond介紹 Crond是linux系統中用來定期執行命令/腳本或指定程式任務的一種服務或軟體,一般情況下,我們安裝完Centos5/6 linux操作系統之後,預設便會啟動Cro ...
  • 當你出現ifconfig與網卡配置文件的名稱不同時,如圖所示情況 ifconfig顯示的網卡名為eth1,而配置文件名字為ifcfg-eth0,所以會導致失敗。 解決方案: 1、刪除/etc/udev/rules.d/70-persistent-net.rules文件,重啟系統。 2、ifconfi ...
  • 我在 "Linux字元設備驅動框架" 一文中簡單介紹了Linux字元設備編程模型,在那個模型中,只要應用程式 open() 了相應的設備文件,就可以使用ioctl通過驅動程式來控制我們的硬體,這種模型直觀,但是從軟體設計的角度看,卻是一種十分糟糕的方式,它有一個致命的問題,就是設備信息和驅動代碼冗餘 ...
  • NFS重要問題 1、有關NFS客戶端普通用戶寫NFS的問題。 1)為什麼要普通用戶寫NFS。 2)exports加all_squash。 Rsync介紹 什麼是Rsync? Rsync是一款開源的、快速的、多功能的、可實現全量即增量的本地或遠程數據同步備份的優秀工具。Rsync軟體適用於unix、l... ...
  • 文章作者:luxianghao 文章來源:http://www.cnblogs.com/luxianghao/p/6339470.html 轉載請註明,謝謝合作。 免責聲明:文章內容僅代表個人觀點,如有不當,歡迎指正。 為什麼會有閏秒? 世界上有幾種計量時間的方式, 世界時(UT1):是一種天文計量 ...
  • Linux教學輔助訓練(第三階段) 標簽(空格分隔): Linux輔助訓練 陳思齊 [更多資料點我查看][1] 提示 :本階段性練習題是對《實戰教學筆記》相應章節知識的歸納與擴展部分,必須要 會,是面試前必須重溫的一套基礎練習(第一階段即為實戰教學筆記第四節 Linux命令 基礎)。 本文涉及的命令 ...
  • 預設情況下系統所有的快捷方式放在/usr/share/applications,打開該目錄會看到大量的.desktop文件,每個文件便是一個快捷方式。 有時我們需要自己安裝軟體,然後添加快捷方式。文件的主要結構為: 其中Name是快捷方式名;comment:註釋;Exec(這個很關鍵):啟動腳本,要 ...
  • 公司的路由器要ubuntu伺服器進行路由網路功能的管理,在安裝的時候出現下麵的錯誤提示: 安裝ubuntu-16.04.1-server-amd64出現“無法安裝busybox-initramfs”錯誤。向目標系統中安裝busybox-initramfs軟體包時出現一個錯誤。請檢查/var/log/ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...