前述章节已经说明了open是如何打开文件的,本章节将说明open打开文件的过程中初始化了哪些结构体及这些结构体之间建立的联系
1.1 各结构体之间的关系图
如下图:
接下来一步一步来说明。
1.2 与进程相关的结构体
首先,文件必须由进程打开,进程的结构体task_struct,在进程创建时,会初始化该结进程描述符,包括两个成员结构(struct fs_struct *)fs和(struct files_struct *)files:
struct task_struct {
...
pid_t pid;
pid_ttgid;
...
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
...
}
每个进程都有它自己当前的工作目录和它自己的根目录:这两个信息都是记录在结构体struct fs_struct *fs中:
struct fs_struct {
int users;
spinlock_t lock;
seqcount_t seq;
int umask;
int in_exec;
struct path root, pwd;
};
其中:
root为根目录的目录项;
pwd:当前工作目录的目录项
这两个结构体在open操作定位文件的过程中会使用到。
那么进程如何描述和记录自己打开的文件信息哪?
使用结构体struct files_struct *files来描述进程当前打开的文件,结构体地址存放于进程描述符task_struct的files字段。该表的类型为files_struct结构:
struct files_struct {
/*
* readmostly part
*/
atomic_tcount; // files_struct的引用计数
boolresize_in_progress;
wait_queue_head_tresize_wait;
/*
* 为什么有两个fdtable呢?这是内核的一种优化策略。
* fdt为指针, 而fdtab为普通变量。一般情况下,fdt是指向fdtab的,* 当需要它的时候, 才会真正动态申请内存。因为默认大小的文件表足以应付大多数情况,* 因此这样就可以避免频繁的内存申请。这也是内核的常用技巧之一。* 在创建时, 使用普通的变量或者数组, 然后让指针指向它, 作为默认情况使用。* 只有当进程使用量超过默认值时, 才会动态申请内存。*/ struct fdtable __rcu *fdt; struct fdtable fdtab; /* * writtenpart on a separate cache line in SMP */ spinlock_tfile_lock ____cacheline_aligned_in_smp; intnext_fd; unsignedlong close_on_exec_init[1]; unsignedlong open_fds_init[1]; unsignedlong full_fds_bits_init[1];/** fd_array为一个固定大小的file结构数组。* struct file是内核用于文件管理的结构。这里使用默认大小的数组,* 就是为了可以涵盖大多数情况, 避免动态分配*/ struct file __rcu *fd_array[NR_OPEN_DEFAULT];}; fdtable结构嵌入在files_struct中,并且由它的fdt指向。
struct fdtable {
unsignedint max_fds;
structfile __rcu **fd; /* current fd array*/
unsignedlong *close_on_exec;
unsignedlong *open_fds;
unsignedlong *full_fds_bits;
structrcu_head rcu;
};
fdtable结构的fd字段指向文件对象的指针数组。该数组的长度存放在max_fds字段中。通常,fd字段指向files_struct结构的fd_array字段,该字段包括32个文件对象指针。如果进程打开的文件数目多于32,内核就分配一个新的、更大的文件指针数组,并将其地址存放在fd字段中,内核同时也更新max_fds字段的值。
对于在fd数组中所有元素的每个文件来说,数组的索引就是文件描述符(file descriptor)。通常,数组的第一个元素(索引为0)是进程的标准输入文件,数组的第二个元素(索引为1)是进程的标准输出文件,数组的第三个元素(索引为2)是进程的标准错误文件。请注意,借助于dup()、dup2()和fcntl()系统调用,两个文件描述符可以指向同一个打开的文件,也就是说,数组的两个元素可能指向同一个文件对象。当用户使用shell结构(如2>&1)将标准错误文件重定向到标准输出文件上时,用户也能看到这一点。
进程不能使用多于NR_OPEN(通常为1 048 576)个文件描述符。内核也在进程描述符的signal->rlim[RLIMIT_NOFILE]结构上强制动态限制文件描述符的最大数;这个值通常为1024,但是如果进程具有超级用户特权,就可以增大这个值。
各结构体间关系如下:
1.3 与文件系统相关的结构体
在进程进行打开文件操作中,首先分配空的file结构体,然后查找或初始化与文件相对应的dentry,在此过程中会初始化文件索引节点(inode),在路径节点查找过程中会使用到task_struct成员变量fs的两个变量root与pwd。具体请参考资料《[IO系统]05 open流程分析》。
索引节点对象(inode):存储了文件和目录的相关信息(和文件本身是两个不同的概念。它包含的是诸如文件的大小、拥有者、创建时间、磁盘位置、文件操作方法、脏标示等和文件相关的信息),代表一个实质的文件,在磁盘保存有该对象。当一个文件首次被访问时,内核会在内存中组装相应的索引节点对象,以便向内核提供对一个文件进行操作时所必需的全部信息。
struct inode {
…
conststruct inode_operations *i_op;
structsuper_block *i_sb;
structaddress_space *i_mapping;
…
/* Statdata, not accessed from path walking */
unsignedlong i_ino;
…
union {
constunsigned int i_nlink;
unsignedint __i_nlink;
};
structtimespec i_atime;
structtimespec i_mtime;
struct timespec i_ctime;
…
structlist_head i_lru; /* inode LRU list */
structlist_head i_sb_list;
union {
structhlist_head i_dentry;
structrcu_head i_rcu;
};
…
conststruct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct address_space i_data;
…
void *i_private; /* fs or device privatepointer */
};
目录项对象(dentry):它代表一个目录项(包括该目录对象对应的索引节点,子目录链表,父目录目录项对象,与它同级的目录的目录项对象链表,使用计数,缓存标志),是路径的一个组成部分(注:路径中的每个组成部分都由一个索引节点对象表示)。该对象只存放在内存中。
struct dentry {
/* RCUlookup touched fields */
unsignedint d_flags; /* protected by d_lock*/
seqcount_td_seq; /* per dentry seqlock */
structhlist_bl_node d_hash; /* lookup hashlist */
structdentry *d_parent; /* parent directory 父目录的dentry */
structqstr d_name; /* 文件名 */
structinode *d_inode; /* Where the namebelongs to - NULL is
* negative */
unsignedchar d_iname[DNAME_INLINE_LEN]; /*small names */
/* Reflookup also touches following */
structlockref d_lockref; /* per-dentry lockand refcount */
conststruct dentry_operations *d_op;
structsuper_block *d_sb; /* The root of thedentry tree文件的超级块对象*/
unsignedlong d_time; /* used by d_revalidate*/
void*d_fsdata; /* fs-specific data */
structlist_head d_lru; /* LRU list 未使用目录项链表的头 */
structlist_head d_child; /* child of parentlist 子目录项链表的头 */
structlist_head d_subdirs; /* our children */
/*
* d_alias and d_rcu can share memory
*/
union {
structhlist_node d_alias; /* inode alias list*/
structrcu_head d_rcu;
} d_u;
};
文件对象(file):是已打开的文件在内存中的表示(包括相应的目录项对象、使用计数、访问模式、当前偏移量、操作方法等),主要用于建立进程和磁盘上的文件的对应关系。它由进程调用sys_open()创建与分配。
File对象并不是首先填充的,是初始化dentry后填充
struct file {
union {
structllist_node fu_llist;
structrcu_head fu_rcuhead;
} f_u;
structpath f_path;
structinode *f_inode; /* cached value *//*指向文件索引节点的指针 */
conststruct file_operations *f_op; /* 指向文件操作表的指针 */
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
atomic_long_t f_count;
unsignedint f_flags;
fmode_t f_mode;
structmutex f_pos_lock;
loff_t f_pos;
structfown_struct f_owner;/* 通过信号进行IO事件通知的数据 */
conststruct cred *f_cred;
structfile_ra_state f_ra;
u64 f_version;/* 版本号,每次使用后,自动递增 */
#ifdef CONFIG_SECURITY
void *f_security;/* 指向文件对象安全结构的指针 */
#endif
/* neededfor tty driver, and maybe others */
void *private_data; /* 指向特定文件系统或设备驱动程序所需数据的指针 */
#ifdef CONFIG_EPOLL
/* Used byfs/eventpoll.c to link all the hooks to this file */
structlist_head f_ep_links; /* 文件的事件轮训等待者链表的头 */
structlist_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
structaddress_space *f_mapping; /* 指向文件地址空间对象的指针 */
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK*/
1.4 参考文献
博文:http://www.cnblogs.com/zengyiwen/p/5755186.html