與進程相關的文件系統
進程是通過文件描述符(file descriptor,fd)來訪問文件的,每個進程最多能同時使用NR_OPEN個文件描述符,這個值在include/linux/limits.h中定義為1024。每一個進程用一個打開文件表files_struct來描述進程的文件描述符使用情況。每一個文件都有一個文件指針。
進程的task_struct中有文件系統相關的數據成員:
struct task_struct {
……
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
……
};
結構fs_struct給出了與進程相關的文件系統的信息,比如進程自己的當前工作目錄,它的根目錄等,這個結構的定義為:
---------------------------------------------------------------------
include/linux/fs_struct.h
struct fs_struct {
int users;
rwlock_t lock; /* 用於表中字段的讀/寫自旋鎖 */
int umask; /* 當打開文件設置文件權限時所使用的位掩碼 */
int in_exec;
struct path root, pwd;
};
---------------------------------------------------------------------
其中path結構的root和pwd兩個成員分別描述了進程最常用到的兩個目錄的信息,即根目錄和當前目錄,path結構定義如下:
---------------------------------------------------------------------
include/linux/path.h
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
---------------------------------------------------------------------
mnt:描述目錄所安裝的文件系統對象
dentry:描述目錄的目錄項
還有一個表表示進程打開的文件,即task_struct結構的files_struct類型的files字段。它給出了所有的進程描述符的使用情況,其file結構指針數組成員給出了文件描述符的信息,其定義如下:
---------------------------------------------------------------------
include/linux/fdtable.h
struct files_struct {
/*
* read mostly part
*/
atomic_t count; /* 共享該表的進程數目 */
/* 文件描述符表 */
struct fdtable *fdt;
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
/* 用於表中字段的讀/寫自旋鎖 */
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd; /* 所分配的最大文件描述符加1 */
/* 執行exec() 時需要關閉的文件描述符的集合 */
struct embedded_fd_set close_on_exec_init;
/* 文件描述符的初始集合 */
struct embedded_fd_set open_fds_init;
/* 文件對象指針的初始化數組 */
struct file * fd_array[NR_OPEN_DEFAULT];
};
---------------------------------------------------------------------
在新的管理文件描述符的無鎖模型中,鎖機制是基於RCU的。文件描述表包含多個成員——fd sets(open_fds 和 close_on_exec, 文件指針數組, 文件描述符集和文件指針數組的大小)。為了使更新在一個無鎖的讀者看來是原子的,則文件描述符表的所有元素被放在一個單獨的結構——struct fdtable中。
即,fdtable結構是進程的文件描述符表,其定義如下:
---------------------------------------------------------------------
include/linux/fdtable.h
struct fdtable {
unsigned int max_fds;
struct file ** fd; /* current fd array */
fd_set *close_on_exec;
fd_set *open_fds;
struct rcu_head rcu;
struct fdtable *next;
};
---------------------------------------------------------------------
fd字段指向文件對象指針數組。該數組的長度存放在max_fds中。通常,fd字段指向files_struct的fd_array字段,該字段包含32個文件對象指針。如果進程打開的文件數目多於32個,內核就分配一個新的、更大的文件指針數組,並將其地址放在fd中,內核也同時更新max_fds字段的值。
對於在fd數組中有元素的每個文件來說,數組的索引就是文件描述符。Unix進程將文件描述符作為主文件標識符。兩個文件描述符可以指向同一個打開的文件。
進程不能使用多於NR_OPEN個文件描述符。open_fds字段最初包含open_fds_init字段的地址,open_fds_init表示當前已打開文件描述符的位圖。max_fds字段存放位圖中的位數。
fd_set結構是文件描述符集,它將同一種情況下的多個文件描述符放在一起。在include/linux/types.h有中定義:
typedef __kernel_fd_set fd_set;
__kernel_fd_set結構在include/linux/posix_types.h中定義:
typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
其中與__FDSET_LONGS有關的一些宏:
#define __NFDBITS (8 * sizeof(unsigned long))
#undef __FD_SETSIZE
#define __FD_SETSIZE 1024
#undef __FDSET_LONGS
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
embedded_fd_set結構是小的文件描述符集,它將同一情況下的文件描述符放在一起,只能存放unsigned long類型位數個文件描述符,不過,這對於許多進程已經足夠了。
/*
* The embedded_fd_set is a small fd_set,
* suitable for most tasks (which open <= BITS_PER_LONG files)
*/
struct embedded_fd_set {
unsigned long fds_bits[1];
};
根文件系統首先是一種文件系統,但是相對於普通的文件系統,它的特殊之處在於,它是內核啟動時所mount的第一個文件系統,內核代碼映像文件保存在根文件系統中,而系統引導啟動程序會在根文件系統掛載之後從中把一些基本的初始化腳本和服務等加載到內存中去運行。
我們首先從主機上所安裝的Linux操作系統中了解一些根文件系統的信息。比如在筆者工作的Linux桌面系統中可以得到下面的結果:
# mount
/dev/hda2 on / type ext3 (rw)
none on /proc type proc (rw)
/dev/hda1 on /boot type ext3 (rw)
none on /dev/pts type devpts (rw,gid=5,mode=620)
none on /dev/shm type tmpfs (rw)
# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/hda2 16216016 5667* 9724600 37% /
/dev/hda1 101089 9321 8*9 10% /boot
none 63028 0 63028 0% /dev/shm
從上面的mount命令我們可以看到,在桌面Linux中,根文件系統”/”被mount到/dev/hda2設備上,文件系統類型為ext3,屬性為rw即可讀寫。從df命令則可以得到更多根文件系統使用空間的相關信息。
根文件系統一直以來都是所有類Unix操作系統的一個重要組成部分,也可以認為是嵌入式Linux系統區別於其他一些傳統嵌入式操作系統的重要特征,它給Linux帶來了許多強大和靈活的功能,同時也帶來了一些復雜性。我們需要清楚的了解根文件系統的基本結構,以及細心的選擇所需要的系統庫、內核模塊和應用程序等,並配置好各種初始化腳本文件,以及選擇合適的文件系統類型並把它放到實際的存儲設備的合適位置。
根文件系統的基本目錄結構
Linux的根文件系統以樹型結構組織,包含內核和系統管理所需要的各種文件和程序,一般說來根目錄”/”下的頂層目錄都有一些比較固定命名和用途。
下面列出了一個Linux根文件系統中的比較常見的目錄結構:
/bin 存放二進制可執行命令的目錄
/dev 存放設備文件的目錄
/etc 存放系統管理和配置文件的目錄
/home 用戶主目錄,比如用戶user的主目錄就是/home/user,可以用~user表示
/lib 存放動態鏈接共享庫的目錄
/sbin存放系統管理員使用的管理程序的目錄
/tmp 公用的臨時文件存儲點
/root 系統管理員的主目錄
/mnt 系統提供這個目錄是讓用戶臨時掛載其他的文件系統。
/proc 虛擬文件系統,可直接訪問這個目錄來獲取系統信息。
/var 某些大文件的溢出區
/usr 最龐大的目錄,要用到的應用程序和文件幾乎都在這個目錄。
對於經常使用Linux系統的讀者來說,這些目錄大部分應該很熟悉了。不過有幾個目錄對初學者來說容易混淆,如/bin,/sbin,/usr/bin和/usr/sbin。這裏簡單介紹一下它們的區別:/bin目錄一般存放對於用戶和系統來說都是必須的二進制文件,而/sbin目錄要存放的是只針對系統管理的二進制文件,該目錄的文件將不會被普通用戶使用。相反,那些不是必要的用戶二進制文件存放在/usr/bin下面,那些不是非常必要的系統管理工具放在/usr/sbin下。此外,對於一些本地的庫也非常類似,對於那些要求啟動系統和運行的必須命令要存放在/lib目錄下,而對於其他不是必須的庫存放在/usr/lib目錄就可以。
對於嵌入式Linux系統的根文件系統來說,一般可能沒有上面所列出的那麽復雜,比如嵌入式系統通常都不是針對多用戶的,所以/home這個目錄在一般嵌入式Linux中可能就很少用到,而/boot這個目錄則取決於你所使用的BootLoader是否能夠重新獲得內核映象從你的根文件系統在內核啟動之前。一般說來,只有/bin,/dev,/etc,/lib,/proc,/var,/usr這些需要的,而其他都是可選的。
簡單的來說,根文件系統包括虛擬根文件系統和真實根文件系統。在Kernel啟動的初始階段,首先去創建虛擬的根文件系統,接下來再去調用do_mount來加載真正的文件系統,並將根文件系統切換到真正的文件系統,也即真實的文件系統。
一.什麽是根文件系統
在傳統的Windows機器上目錄結構中,可能會包括C:或者D:盤,而他們一般就稱之為特定邏輯磁盤的根目錄。從文件系統的層面來說,每一個分區都包含了一個根目錄區,也即系統中存在多個根目錄。
但是,在Linux系統中,目錄結構與Windows上有較大的不同。系統中只有一個根目錄,路徑是“/”,而其它的分區只是掛載在根目錄中的一個文件夾,如“/proc”和“system”等,這裏的“/”就是Linux中的根目錄。
對應根目錄也就存在一個根目錄文件系統的概念,我們可以將某一個分區掛載為根目錄文件系統,如6410公版中就將mtdblk2掛載為根目錄文件系統。程序中可以通過U-Boot給Kernel指定參數或者編譯選項來指定,如目前的開發板中就通過如下的編譯選項來制定根目錄文件系統:
CONFIG_CMDLINE="console=ttyS0,115200 mem=108M rdinit=/linuxrc root=/dev/mtdblock2" |
簡單的來說,根目錄文件系統就是一種目錄結構,包括了Linux啟動的時候所必須的一些目錄結構和重要文件。
根文件系統有兩種,一種是虛擬根文件系統,另外一種是真實的根文件系統。一般情況下,會首先在虛擬的根文件系統中做一部分工作,然後切換到真實的根文件系統下面。
籠統的來說,虛擬的根文件系統包括三種類型,即Initramfs、cpio-initrd和image-initrd。
與進程相關的文件系統