由來:公司內部外網記錄日誌的方式如今都是經過Nginx模塊收到數據發送到系統消息隊列,而後由另一個進程來從消息隊列讀取而後寫回磁盤這樣的操做,儘可能的減小Nginx的阻塞。node
可是因爲System/V消息隊列在使用前須要規定消息長度,且結構不同須要從新定義消息格式等等...因此在增長需求的時候須要修改代碼並從新編譯,這樣帶來的壞處可想而知,外網服務器也會所以重啓。shell
因此組內有同事準備在Nginx中加入異步寫日誌的功能,大體方式就是將數據寫入到一塊內存而後由另一個進程讀取而後flush到磁盤,或者直接使用同步寫的方式。而後測試對比後發現其實同步寫和異步寫差異很小。數據庫
並且最大的疑惑就是,Nginx的多進程寫在沒有應用層加鎖的狀況下是寫到同一個日誌文件的,究竟是應該把日誌寫到同一個文件下仍是按照進程PID來分割日誌呢。若是應用層不加鎖會致使文件寫混亂嗎?緩存
好了,說了那麼多屁話,其實今天討論的主題和這個功能需求沒多大關係。安全
不過也是由於這個勾起了我研究內核中文件緩存的慾望。服務器
下面經過這幾天的資料收集,簡單的介紹一下在系統調用read/write調用的時候底層到底發生了什麼等等...併發
因爲主題是文件和文件系統,那麼首先第一個要了解的是什麼是文件。app
一 、文件的描述異步
文件實際上是一種對磁盤中存儲的一堆零散的數據的一種描述,在Linux上,一個文件由一個inode 表示。inode在系統管理員看來是每個文件的惟一標識,在系統裏面,inode是一個結構,存儲了關於這個文件的大部分信息。測試
命令 stat [file]能夠看到某個文件的信息
inode號就是這個文件的惟一標識,能夠看作是數據庫中的主鍵。一個inode 通常佔了128KB或者是256KB,是的,有可能比文件自己還大。
inode中存儲了一個文件的如下信息:
1.文件大小
2.文件的存儲位置
3.用戶的GID, UID
4.文件的訪問權限
5.時間戳
6.硬連接數()
將inode直觀的展示出來,而後根據inode來說解整個文件系統就顯得很容易理解了。
不一樣於數據庫的自增主鍵,inode號在系統中是會用完的,查看系統的inode總體信息能夠用命令
df -i
是的,若是你的系統中零散的小文件不少,是會浪費掉不少的inode的,有可能致使的狀況就是磁盤任然有空間可是建立文件缺失敗了。
若是爲一個文件建立了一個硬連接,就是將不一樣的文件名指向了相同的inode,跟文件路徑無關,由於inode沒有存儲文件路徑。
系統在發現一個文件的Links == 0 的時候就會刪除對應的文件。
以上是屬於系統管理員的,若是你不止想了解這些,請往下看。
inode就是一個文件的一部分描述,不是所有,在內核中,inode對應了這樣一個實際存在的結構。
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; /* 塊設備驅動 */ 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; };
縱觀整個inode的C語言描述,沒有發現關於文件名的東西,也就是說文件名不禁inode保存,實際上系統是不關心文件名的,對於系統中任何的操做,大部分狀況下你都是經過文件名來作的,但系統最終都要經過找到文件對應的inode來操做文件,由inode結構中 *i_op指向的接口來操做。
系統是怎樣經過文件名找到inode的?
要想明白這一點,就須要知道在內核中,目錄也是一個文件,也有對應的inode,只不過inode中存儲文件實際內容的不是文件內容而是一個 dentry(dir entry)結構。
好比說在目錄 /data/shells/text.txt中,test/既是一個文件也是一個目錄
找到根目錄/data/的'data' dentry,根據'data' dentry中的inode找到'shells' dentry和inode,而後遞歸的查找下去,最終找到test.txt的inode.
文件名就存在於dentry中,路徑中的每一級的路徑名也算作是其文件名。
在dentry結構中有一個指向父節點的指針,也就是 '../',值得一提的是 '..'是指向上層目錄的一個硬連接
inode的基本介紹就算完了,下面會介紹一下每一個進程是怎樣關聯到每一個文件的,也就是文件描述符那一塊。而後介紹一下多個進程對同一個文件操做的時候併發問題以及寫操做過程當中緩存結構的管理。