一、inode(索引節點)
(1) 理解inode,要從文件儲存說起。
文件儲存在硬盤上,硬盤的最小存儲單位叫做"扇區"(Sector)。每個扇區儲存512字節(相當於0.5KB)。操作系統讀取硬盤的時候,不會一個個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個"塊"(block)。這種由多個扇區組成的"塊",是文件存取的最小單位。“塊"的大小,最常見的是4KB,即連續八個 sector組成一個 block。
(2) 通常情況下,文件系統會將文件的實際內容和屬性分開存放:
- 文件的屬性保存在 inode 中(i 節點)中,每個 inode 都有自己的編號。每個文件各佔用一個 inode。不僅如此,inode 中還記錄着文件數據所在 block 塊的編號;
- 文件的實際內容保存在 block 中(數據塊),類似衣櫃的隔斷,用來真正保存衣物。每個 block 都有屬於自己的編號。當文件太大時,可能會佔用多個 block 塊。
- 另外,還有一個 super block(超級塊)用於記錄整個文件系統的整體信息,包括 inode 和 block 的總量、已經使用量和剩餘量,以及文件系統的格式和相關信息等。
如圖所示:文件系統先格式化出 inode 和 block 塊,假設某文件的權限和屬性信息存放到 inode 4 號位置,這個 inode 記錄了實際存儲文件數據的 block 號有 4 個,分別爲 2、7、13、15,由此,操作系統就能快速地找到文件數據的存儲位置。
note:
每個inode都有一個號碼,操作系統用inode號碼來識別不同的文件。Unix/Linux系統內部不使用文件名,而使用inode號碼來識別文件。對於系統來說,文件名只是inode號碼便於識別的別稱或者綽號。表面上,用戶通過文件名,打開文件。實際上,系統內部這個過程分成三步:首先,系統找到這個文件名對應的inode號碼;其次,通過inode號碼,獲取inode信息;最後,根據inode信息,找到文件數據所在的block,讀出數據。
(3) 聯繫平時實踐,大家格式化硬盤(U盤)時發現有:快速格式化和底層格式化。快速格式化非常快,格式化一個32GB的U盤只要1秒鐘,普通格式化格式化速度慢。這兩個的差異?其實快速格式化就是隻刪除了U盤中的硬盤內容管理表(其實就是inode),真正存儲的內容沒有動。這種格式化的內容是有可能被找回的。
(4) 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;
};
可以用stat命令,查看某個文件的inode信息:
二、文件描述符表
(1) 每個進程在Linux內核中都有一個task_struct結構體來維護進程相關的信息,稱爲進程描述符(Process Descriptor),而在操作系統理論中稱爲進程控制塊 (PCB,Process Control Block)。
struct task_struct {
...
/* open file information */
struct files_struct *files;
...
};
task_struct中有一個指針(struct files_struct *files)指向files_struct結構體(如下所示),稱爲文件描述符表(我認爲這個定義不確切,暫時還沒找到這個說法的來源),記錄該進程打開的所有文件。該表中有一個域(struct file * fd_array[NR_OPEN_DEFAULT]),爲數組,該數組的每個元素指向已打開的文件的指針(已打開的文件在內核中用file 結構體表示,文件描述符表中的指針指向file 結構體)。
struct files_struct
{
atomic_t count; //引用計數 累加
struct fdtable *fdt;
struct fdtable fdtab;
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd;
struct embedded_fd_set close_on_exec_init;
struct embedded_fd_set open_fds_init;
struct file * fd_array[NR_OPEN_DEFAULT]; //文件描述符數組
};
struct file
{
mode_t f_mode;//表示文件是否可讀或可寫,FMODE_READ或FMODE_WRITE
dev_ t f_rdev ;// 用於/dev/tty
off_t f_ops;//當前文件位移
unsigned short f_flags;//文件標誌,O_RDONLY,O_NONBLOCK和O_SYNC
unsigned short f_count;//打開的文件數目
unsigned short f_reada;
struct inode *f_inode;//指向inode的結構指針
struct file_operations *f_op;//文件操作索引指針
}
(2) 用戶程序不能直接訪問內核中的文件描述符表,而只能使用文件描述符表的索引 (即0、1、2、3這些數字),這些索引就稱爲文件描述符(File Descriptor),用int 型變量保存。
- 文件描述符(本質上是個數字)是open系統調用內部由操作系統自動分配的,操作系統分配這個fd時也不是隨意分配,操作系統規定,fd從0開始依次增加。linux中文件描述符表是個數組(不是鏈表),所以這個文件描述符表其實就是一個數組,fd是index,文件表指針是value
- 當我們去open時,內核會從文件描述符表中挑選一個最小的未被使用的數字給我們返回。也就是說如果之前fd已經佔滿了0-9,那麼我們下次open得到的一定是10.(但是如果上一個fd得到的是9,下一個不一定是10,這是因爲可能前面更小的一個fd已經被close釋放掉了)
- fd中0、1、2已經默認被系統佔用了,因此用戶進程得到的最小的fd就是3了。
- linux內核佔用了0、1、2這三個fd是有用的,當我們運行一個程序得到一個進程時,內部就默認已經打開了3個文件,這三個文件對應的fd就是0、1、2。這三個文件分別叫stdin(標準輸入)、stdout(標準輸出)、stderr(標準錯誤)。標準輸入一般對應的是鍵盤,標準輸出一般是LCD顯示器(可以理解爲:1對應LCD的設備文件)
- printf函數其實就是默認輸出到標準輸出stdout上了。stdio中還有一個函數叫fpirntf,這個函數就可以指定輸出到哪個文件描述符中。
三、文件表
struct file
{
mode_t f_mode;//表示文件是否可讀或可寫,FMODE_READ或FMODE_WRITE
dev_ t f_rdev ;// 用於/dev/tty
off_t f_ops;//當前文件位移
unsigned short f_flags;//文件標誌,O_RDONLY,O_NONBLOCK和O_SYNC
unsigned short f_count;//打開的文件數目
unsigned short f_reada;
struct inode *f_inode;//指向inode的結構指針
struct file_operations *f_op;//文件索引指針
}
內核爲所有打開文件維護一張文件表項,每個文件表項包含內容可以由以上結構體看出,其中比較重要的內容有:
a. 文件狀態(讀 寫 添寫 同步 非阻塞等)
b. 當前文件偏移量
c. 指向該文件i節點(i節點)的指針
d. 指向該文件操作的指針(file_operations )
file_operations 結構體在linux內核2.6.5定義如下所示:
struct file_operations
{
struct module *owner;
loff_t(*llseek) (struct file *, loff_t, int);
ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);
ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area) (struct file *, unsigned long,
unsigned long, unsigned long,
unsigned long);
};
總結:
https://blog.51cto.com/13574131/2064574
https://www.jianshu.com/p/0520d6b76318
http://www.ruanyifeng.com/blog/2011/12/inode.html
https://www.cnblogs.com/how-are-you/p/5699257.html
https://blog.csdn.net/jnu_simba/article/details/8806654
https://blog.csdn.net/luotuo44/article/details/17474099
https://www.cnblogs.com/wanghetao/archive/2012/05/28/2521675.html
https://blog.csdn.net/u010944778/article/details/45077565