Linux内核中的文件描述符

Kernel version:2.6.14

CPU architecture:ARM920T

作为文件的使用者,进程理所当然的要将所使用的文件记录于自己的控制块中,也就是task_struct。另外,由于进程所对应的程序也是一个文件,因此进程控制块还必须记录这个文件的相关信息。由于OS要对所有进程提供服务,因此OS还要维护一个记录所有进程打开的文件的总表。

1、文件对象

当进程通过open系统调用打开一个文件时,该系统调用找到这个文件后,会把文件封装到一个file结构的实例中提供给进程,这个实例称为file对象。file结构的定义如下:

struct file {
struct list_head f_list; //所有打开文件的链表
struct dentry *f_dentry; //文件的dentry
struct vfsmount *f_vfsmnt; //文件目录的VFS安装点指针
struct file_operations *f_op; //指向文件操作函数集的指针
atomic_t f_count; //记录访问本文件的进程数目的计数器
unsigned int f_flags; //访问类型
mode_t f_mode; //访问模式
loff_t f_pos; //文件当前的读写位置
struct fown_struct f_owner;
unsigned int f_uid, f_gid; //文件所有者ID和用户组ID
struct file_ra_state f_ra;

unsigned long f_version;
void *f_security;

/* needed for tty driver, and maybe others */
void *private_data;

#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
struct rcu_head f_rcuhead;
};

结构中的域f_uid为文件所有者的ID,f_gid为文件所有者所在组的ID。这样就使得一个文件可能面临三种用户的访问:

● 文件所有者;

● 同组用户;

● 其他用户。

内核在处理一个进程或用户访问一个文件的请求时,要根据进程的f_uid和f_gid以及访问模式来确定该进程是否具有访问这个文件的权限。对于一个用户来说,可以有读、写和执行三种文件权限,这三种权限和三种用户就共有9中组合,即文件的访问权限可以用9个bit来表示,并将其保存在文件的dentry中。

结构中的域f_pos记录了进程对文件读写位置的当前值,可以通过调用函数llseek进程移动。

结构中的f_op执向结构file_operations,该结构封装了对文件进行操作的函数,定义如下:

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);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (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 *);
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);
int (*check_flags)(int);
int (*dir_notify)(struct file *filp, unsigned long arg);
int (*flock) (struct file *, int, struct file_lock *);
};

从上面的代码可以看到,结构中是一系列函数的指针,这里有我们比较熟悉的read、open、write和close等函数的指针。进程就是通过这些函数访问一个文件的,file_operations是linux虚拟文件系统VFS和进程之间的接口。

2、文件描述符

下面进一步介绍进程对自己所访问的file对象的管理方法。linux中使用一个数组来管理进程打开的文件的file对象,数组中的每个元素都存放一个纸箱进程所打开的文件的file对象。既然用一个数组来存放file对象,那么用数组的下标来访问文件就是一件顺理成章的方法,于是,linux就把数组元素的下标叫做该数组元素所对应的文件的文件描述符,该描述符就是系统对文件的标识,这个数组也叫文件描述符数组,如下图所示:



内核通过系统调用dup、dup2和fctl可以使数组中的多个元素指向同一个文件的file对象,也就是说,在linux中,同一个文件可以有多个文件描述符。

3、进程打开文件表

进程描述符数组中存放了一个进程所访问的所有文件,把这个文件描述符数组和这个数组在系统中的一些动态信息组合到一起,就形成了一个新的数据结构——进程打开文件表,即file_struct,其定义如下:

/*

* Open file table structure

*/

struct files_struct {

atomic_t count; //引用计数

spinlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */

struct fdtable *fdt; //管理文件描述符

struct fdtable fdtab; //管理文件描述符

fd_set close_on_exec_init; //位图

fd_set open_fds_init; //位图

struct file * fd_array[NR_OPEN_DEFAULT]; //文件描述符数组

};

显然,这个结构应该属于进程的私有数据,所以进程控制块task_struct用指针files指向它。

struct task_struct {

...

/* open file information */

struct files_struct *files;

...

};

进程与其打开文件之间的关系如下图所示。



4、文件描述符的管理

file_struct中的fdt和fdtab用于管理文件文件描述符,一个是fdtable类型,另一个是其指针类型。fdtable的定义如下:

struct fdtable {

unsigned int max_fds; //可以代开的最大文件数

int max_fdset; //位图的最大长度

int next_fd; //下一个可用的fd

struct file ** fd; /* current fd array 指向files_struct的fd_array */

fd_set *close_on_exec;

fd_set *open_fds; //打开的文件标记,比如第2位为0,则打开了2号文件

struct rcu_head rcu;

struct files_struct *free_files;

struct fdtable *next;

};

下图可以很直观的说明文件描述符fd的管理。

(0)

相关推荐

  • Linux下修改文件描述符的大小的方法

    在Linux下默认的文件描述符大小为1024,可以通过下面的命令查看: [root@localhost -]# ulimit -n 某些特殊情况,可能需要修改文件描述符的大小,比如说运行squid服务 ...

  • Linux中通过Socket文件描述符寻找连接状态介绍

    Proc虚拟文件系统下面有许多数字命名的子目录,这些数字表示系统当前运行的进程号; 其中/proc/N/fd目录下面保存了打开的文件描述符,指向实际文件的一个链接.如下: 代码如下: [root@XX ...

  • 玩转Linux文件描述符和重定向

    linux下的文件描述符是与文件输入、输出相关联的整数。它们用来跟踪已打开的文件。 最常见的文件描述符是stdin、stdout和stderr.我们可以将某个文件描述符的内容重定向到另一个文件描述符中 ...

  • Linux下如何增大可打开文件描述符的最大数

    在安装Oracle Grid Infrastructure检查约束时遇到一个错误,大体是说当前可打开的文件描述符的最大数为1024,而要求是65536. 于是在gird用户下执行ulimit -a,显 ...

  • Linux下增大可打开文件描述符的最大数的方法

    在安装Oracle Grid Infrastructure检查约束时遇到一个错误,大体是说当前可打开的文件描述符的最大数为1024,而要求是65536. 于是在gird用户下执行ulimit -a,显 ...

  • Linux系统中的文件类型及文件扩展名详解

    Linux文件类型和Linux文件的文件名所代表的意义是两个不同的概念。我们通过一般应用程序而创建的比如file.txt、file.tar.gz ,这些文件虽然要用不同的程序来打开,但放在Linux文 ...

  • Linux系统中fstab文件丢失的恢复方法

    /etc/fstab配置文件决定了Linux系统在启动后如何加载各分区,若分区无法挂载,系统也将无法成功启动,启动时将会出现如下错误信息 /etc/fstab文件丢失的解决办法 1. 使用进入急救模式 ...

  • 虚拟机中linux系统中的文件如何创建和移动

    对于新手来说,经常会碰到如何移动linux系统中的文件夹之类的问题,这些命令都很简单,关键在于熟能生巧,接下来让我们一起学习一下如何在装有linux的虚拟机中进行文件夹的移动. 操作方法 01 第一步 ...

  • Linux系统中查找文件的方法

    -name 必须用到的选项。表明要求系统按照文件名查找. 一般格式:find /(dirname) -name filename 具体文件名查找法: 如果知道了某个文件的文件名,而不知道这个文件放到哪 ...