与进程相关的文件系统
进程是通过文件描述符(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];
};
Linux根文件系统介绍
根文件系统首先是一种文件系统,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所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。