1. 程式人生 > >開啟檔案的核心原始碼分析

開啟檔案的核心原始碼分析

一、概要
   使用者程序在能夠讀/寫一個檔案之前必須要先開啟這個檔案,從概念上說是一個程序與檔案系統之間的一種有連結的通訊,對於開啟檔案實質就是在程序和檔案之間建立起連線,而“開啟檔案號”就是唯一的標識這個連線。在檔案系統的處理當中,每當一個程序重複開啟同一個檔案時就建立起一個由file資料結構代表的獨立的上下文。通常,一個file資料結構,即一個讀/寫檔案的上下文,都是由一個“開啟檔案號”加以標識的。從上面可以看出,一個檔案的開啟與程序是有著緊密聯絡的。接下來就首先介紹程序中與檔案有關的相關結構。
二、與程序有關的檔案
(1)程序中與檔案相關的結構
  1、首先是task_struct結構,每個程序都有一個task_struct結構的物件,該物件包含了程序處理的上下文。在這裡只貼出與檔案有關的部分如下:

點選(此處)摺疊或開啟

  1. struct task_struct{
  2.  ...........
  3.     struct fs_struct *fs;
  4.     struct file_struct *files;
  5.  ...........
  6. }
從上面程序中與檔案有關的程式碼可以看出,在一個task_struct結構中與檔案操作有關的結構之後兩個即struct fs_struct結構物件和file_struct結構物件。下面分別講解這兩個物件的相關資訊。
  2、fs_struct結構。

點選(此處)摺疊或開啟

  1. struct fs_struct {

  2.         int users;  
  3.         rwlock_t lock;
  4.         int umask;
  5.         int in_exec;
  6.         struct path root, pwd;  //其中的root物件是用來存放根目錄的相關資訊,比如根目錄的目錄項等而pwd存放的是當前目錄的相關資訊。主要是當前目錄的目錄項物件。這兩個物件主要是在檔案路徑名解析的時候用於查詢檔案開始的目錄項。如果路徑名查詢是絕對路徑查詢,那麼就會根據root->dentry來開始查詢。而如果是相對路徑查詢就會根據pwd->dentry來進行查詢。

  7. };
其中涉及到的path結構如下:

點選(此處)摺疊或開啟

  1. struct path {
  2.         struct vfsmount *mnt;//只有當相應的目錄為掛載目錄的時候這個變數才存放一些有關掛載的資訊
  3.         struct dentry *dentry;//表示目錄的目錄項物件結構。
  4. }
    3、file_struct物件

點選(此處)摺疊或開啟

  1. struct files_struct { 
  2.     atomic_t count; //自動增量 
  3.     struct fdtable *fdt; 
  4.     struct fdtable fdtab; 
  5.     fd_set close_on_exec_init; //執行exec時
  6. 需要關閉的檔案描述符初值集合 
  7.     fd_set open_fds_init; //當前開啟檔案
  8. 的檔案描述符遮蔽字 
  9.     struct file * fd_array[NR_OPEN_DEFAULT]; 
  10.     spinlock_t file_lock; 
  11. };
這個資料結構中最主要的就是一個file結構指標陣列fd_array[],該陣列的大小事固定的,即32,其下標為開啟檔案號即為檔案描述符。
這個結構中兩個重要的結構即為檔案描述符管理結構fdtable與file物件。其中fdtable物件的程式碼如下:

點選(此處)摺疊或開啟

  1. struct fdtable {
  2.  unsigned int max_fds; //可以代開的最大檔案數
  3.  int max_fdset; //點陣圖的最大長度
  4.  int next_fd; //下一個可用的fd
  5.  struct file ** fd; /* current fd array 指向files_struct的fd_array */
  6.  fd_set *close_on_exec;
  7.  fd_set *open_fds; //開啟的檔案標記,比如第2位為1,則打開了2號檔案.這個欄位和close_on_exec欄位都是一個位圖的形式來進行表示的
  8.  struct rcu_head rcu;
  9.  struct files_struct *free_files;
  10.  struct fdtable *next;
  11. }
files_struct結構:該結構儲存了該程序中開啟的所有的檔案得資訊。比如檔案描述符管理表、開啟的檔案陣列等等都是表示的是該程序開啟的檔案的詳細資訊。
files_struct結構與fdtable結構的結構體如下

對於檔案結構體,其代表的是一個開啟的檔案,系統中的每個開啟的檔案在核心空間都有一個關聯的struct file。它有核心在開啟檔案時建立。同時在檔案關閉之後,核心釋放這個資料結構。
file物件的程式碼如下:

點選(此處)摺疊或開啟

  1. struct file {
  2.         /*
  3.          * fu_list becomes invalid after file_free is called and queued via
  4.          * fu_rcuhead for RCU freeing
  5.          */
  6.         union {
  7.                 struct list_head fu_list;
  8.                 struct rcu_head fu_rcuhead;
  9.         } f_u;
  10.         struct path f_path;//該檔案所對應的path結構,主要是存放的是該檔案的目錄項物件結構。
  11. #define f_dentry f_path.dentry
  12. #define f_vfsmnt f_path.mnt
  13.         const struct file_operations *f_op;//檔案操作的指標,如果開啟的檔案存在那麼這個指標是通過dentry裡的物件來設定。而如果開啟的檔案不存在需要新建立,那麼這個指標是通過父目錄的dentry中的物件來設定。
  14. //以下的資訊主要是通過檔案的inode節點中的資訊進行設定的     
  15.    atomic_t f_count;
  16.         unsigned int f_flags;
  17.         mode_t f_mode;
  18.         loff_t f_pos; //這個欄位是很重要的,表示的是檔案中的當前讀寫位置,當使用系統呼叫lseek的時候就是改變的這個欄位的值
  19.         struct fown_struct f_owner;
  20.         unsigned int f_uid, f_gid;
  21.         struct file_ra_state f_ra;
  22.         unsigned long f_version;
  23. #ifdef CONFIG_SECURITY
  24.         void *f_security;
  25. #endif
  26.         /* needed for tty driver, and maybe others */
  27.         void *private_data;
  28. #ifdef CONFIG_EPOLL
  29.         /* Used by fs/eventpoll.to link all the hooks to this file */
  30.         struct list_head f_ep_links;
  31.         spinlock_t f_ep_lock;
  32. #endif /* #ifdef CONFIG_EPOLL */
  33.         struct address_space *f_mapping;//這個指標是想的是地址空間的物件的指標,主要是用於在page cache中進行查詢操作。
  34. };
(2)程序中與檔案相關的各個結構的聯絡
以上就是程序環境中與檔案操作有關的所有的結構了,為了更好的理解結構之間的聯絡,下面給出各個結構之間的結構圖。


三、open函式的核心追蹤過程
   開啟檔案的系統呼叫是open(),這個系統呼叫在物理檔案系統層是通過do_sys_open()函式來實現的,其程式碼在/fs/open.c這個檔案中。其具體的程式碼如下:

點選(此處)摺疊或開啟

  1. long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
  2.  {
  3.          struct open_flags op;
  4.          int lookup = build_open_flags(flags, mode, &op);
  5.          struct filename *tmp = getname(filename);//將從使用者空間傳入的路徑名複製到核心空間
  6.          int fd = PTR_ERR(tmp);
  7.          if (!IS_ERR(tmp)) {
  8.                  fd = get_unused_fd_flags(flags);//獲取一個沒有使用的檔案描述符fd
  9.                  if (fd >= 0) {
  10.                          struct file *= do_filp_open(dfd, tmp, &op, lookup);//建立file物件
  11.                         if (IS_ERR(f)) {
  12.                                  put_unused_fd(fd);
  13.                                  fd = PTR_ERR(f);
  14.                          } else {
  15.                                  fsnotify_open(f);
  16.                                  fd_install(fd, f);//將current->files_struct和檔案物件進行關聯
  17.                          }
  18.                  }
  19.                  putname(tmp);
  20.          }
  21.          return fd;
  22.  }
從上面的程式碼可以看出open()系統呼叫的主要是由以下的函式來實現的:
(1)getname()函式:將從使用者空間傳入的路徑名複製到核心空間。
(2)
get_unused_fd_flags()函式:獲取一個沒有使用的檔案描述符fd,這個函式主要是操作fdtable結構中的相關欄位
(3)do_filp_open()函式:建立file物件,並把file物件插入到檔案系統超級塊的s_files欄位所指向的開啟檔案的連結串列。
(4)fd_install()函式:
將current->files_struct和檔案物件進行關聯。
下面就分別來講解這些函式的具體操作
(1)getname()函式:這個函式在這裡就不追蹤了。比較麻煩。
(2)get_unused_fd_flags()函式:這個函式主要是通過核心函式__alloc_fd()函式來實現的該函式的程式碼如下:

點選(此處)摺疊或開啟

  1. /*
  2.   * allocate a file descriptor, mark it busy.
  3.   *功能:分配一個檔案描述符,並把它標記為忙的狀態
  4.   *其中的files引數為:當前程序中的current->files變數
  5.   */
  6. int __alloc_fd(struct files_struct *files,
  7.                unsigned start, unsigned end, unsigned flags)
  8.  {
  9.          unsigned int fd;
  10.          int error;
  11.          struct fdtable *fdt;//檔案描述符管理結構
  12.          spin_lock(&files->file_lock);
  13.  repeat:
  14.          fdt = files_fdtable(files);//指向files_struct結構中的fdtable指標
  15.         fd = start;
  16.          if (fd < files->next_fd)//首先判斷fd是不是小於程序中下一個可用的fd,如果是則直接賦值下一個可用的fd給fd
  17.                 fd = files->next_fd;
  18.          if (fd < fdt->max_fds

    相關推薦

    開啟檔案核心原始碼分析

    一、概要    使用者程序在能夠讀/寫一個檔案之前必須要先開啟這個檔案,從概念上說是一個程序與檔案系統之間的一種有連結的通訊,對於開啟檔案實質就是在程序和檔案之間建立起連線,而“開啟檔案號”就是唯一的標識這個連線。在檔案系統的處理當中,每當一個程序重複開啟同一個檔案時就

    Linux核心原始碼分析--檔案系統(五、Inode.c)

    _bmap()         1、_bmap()函式用於把一個檔案資料塊對映到盤塊的處理操作                  因為一個i節點對應一個檔案,所以上面的i節點對映的邏輯塊號就是檔案資料存放的邏輯塊號;i_zone[0]到i_zone[6]是直接邏輯塊號,i

    am335x 核心原始碼分析2 LCD移植

    1、/arch/arm/mach-omap2/board-am335xevm.c/lcdc_init(){得到LCD硬體引數struct da8xx_lcdc_platform_data} -> am33xx_register_lcdc() -> omap_device_

    Android7.1 [Camera] cam_board.xml 檔案解析原始碼分析(一)

            原始碼平臺:rk3399           RK支援了很多個攝像頭,在驅動目錄hardware/rockchip/camer

    spring mvc核心原始碼分析

    前言 自研究了spring security核心原始碼以來,在實踐使用的基礎上更為深入的學習了它的思想.我的java世界彷彿被打開了一扇大門,開始對研究原始碼有種渴望.打算先看一輪核心原始碼,滿足目前工作需要,待對boot有個整體的瞭解之後再逐個細細研究 spring m

    linux核心原始碼分析-夥伴系統

    之前的文章已經介紹了夥伴系統,這篇我們主要看看原始碼中是如何初始化夥伴系統、從夥伴系統中分配頁框,返回頁框於夥伴系統中的。   我們知道,每個管理區都有自己的夥伴系統管理屬於這個管理區的頁框,這也說明了,在夥伴系統初始化時,管理區必須要已經存在(初始化完成)

    《Spark核心原始碼分析與開發實戰》讀書筆記之一

    第1章 Spark系統概述 1.1 Spark是什麼 1. Spark比Hadoop快在哪裡 (1)Spark使用記憶體計算,而Hadoop使用IO (2)Hadoop的計算是按部就班一步一步進行的,而Spark則是提前生成了DAG,優化了運算路徑   1.2 Sp

    Linux核心原始碼分析--zImage出生實錄(Linux-3.0 ARMv7)

    此文為兩年前為好友劉慶敏的書《嵌入式Linux開發詳解--基於AT91RM9200和Linux 2.6》中幫忙寫的章節的重新整理。如有雷同,純屬必然。經作者同意,將我寫的部分重新整理後放入blog中。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    mmap核心原始碼分析,基於核心版本3.10(三)

    之前寫了(一)(二)其實就梳理到了get_unmapped_area的內容,而且有一點混亂,這裡進行第三篇的講解,講解在do_mmap_pgoff中除了get_unmapped_area的內容,來了解mmap的具體實現。通過(一)(二)(三)來將mmap核心原始碼進行一次梳理

    hadoop 1.0.3 fsimage 檔案原始碼分析

    2013-01-08 周海漢 2013.1.8 上一篇文章《hadoop 1.0.4 fsimage 檔案格式分析》描述了hadoop1.04的fsimage的格式。 本篇看看hadoop 1.0.3原始碼是如何實現的。fsim

    CFNet視訊目標跟蹤核心原始碼分析——網路結構設計及實現

    1. 論文資訊 2. 網路結構設計及實現 根據官方實際程式碼,更加詳細一點的網路結構如下圖所示,可以看出,與SiamFC的網路結構類似,CFNet也包含兩個分支——z和x,其中z分支對應目標物體模板,可以理解為目標在第  幀之內所有幀的模板資料加權融合(利用學習率

    【Python】實現網站備份檔案掃描+原始碼分析

    一開始我用的requests庫的get方法 但是這種方法會自動下載檔案,所以不可取 後來發現urllib2的庫相對來說不錯 原始碼如下# coding = utf-8 import urllib2 i

    WebKit 核心原始碼分析 ( 四 )

    WebKit 核心原始碼分析 ( 四 ) 摘要:本文介紹 WebCore 中 Loader 模組是如何載入資源的,分主資源和派生資源分析 loader 模組的類關係。 關鍵詞: WebKit,Loader,Network,ResouceLoader,Subresou

    用Source Insight開啟linux核心原始碼

    用Source Insight開啟linux核心原始碼 2008-01-09 19:06 Linux的核心原始碼可以從很多途徑得到。一般來講,在安裝的linux系統下,/usr/src/linux目錄下的東西就是核心原始碼。另外還可以從互連網上下載,解壓縮後文件一般也都位於linux目錄下。核心原始碼有很多

    linux核心原始碼分析

    本文基於3.18.3核心的分析,nvme裝置為pcie介面的ssd,其驅動名稱為nvme.ko,驅動程式碼在drivers/block/nvme-core.c. 驅動的載入   驅動載入實際就是module的載入,而module載入時會對整個module進行初始化,nvme驅動的module初始化函式

    yii1.1核心原始碼分析(1)目錄結構說明

    framework框架核心庫 1.base底層類庫資料夾包括CApplication:(應用類,負責全域性的使用者請求處理,它管理的應用元件集,將提供特定功能給整個應用程式);CComponent(元

    s3c2410 RTC驅動框架linux核心原始碼分析

    實在無聊中就將原來的一些東西整理了一下,自己是個記性不好的人,隔斷時間整理自己,同時也希望可以方便他人。 -------------------------------------------------------------------------------------

    ernel 3.10核心原始碼分析--KVM相關--虛擬機器執行

    1、基本原理 KVM虛擬機器通過字元裝置/dev/kvm的ioctl介面建立和執行,相關原理見之前的文章說明。 虛擬機器的執行通過/dev/kvm裝置ioctl VCPU介面的KVM_RUN指令實現,在VM和VCPU建立好並完成初始化後,就可以排程該虛擬機器運行了,通

    Linux核心原始碼分析——Linux核心的入口

    Jack:hi,淫龍,在Linux核心的原始碼裡,有幾段彙編程式碼,那幾段程式碼是負責Linux核心引導的。 我:是的。早期的Linux核心引導程式碼只有bootsect.s、setup.s、head.s這3個檔案,這三個檔案都是Linus在1991年左右親手寫的。後來的程

    Linux核心原始碼分析--記憶體管理(一、分頁機制)

            Linux系統中分為幾大模組:程序排程、記憶體管理、程序通訊、檔案系統、網路模組;各個模組之間都有一定的聯絡,就像蜘蛛網一樣,所以這也是為什麼Linux核心那麼難理解,因為不知道從哪裡開始著手去學習。很多人會跟著系統上電啟動 BIOS-->bootse