0
点赞
收藏
分享

微信扫一扫

1.1 Linux中的文件

爱喝酒的幸福人 2022-02-26 阅读 74
linux

1.1.1 文件、文件描述符和文件表

Linux内核将一切视为文件,那么Linux的文件是什么呢?

其既可以是事实上的真正的物理文件,也可以是设备、管道,甚至还可以是一块内存。

狭义的文件是指文件系统中的物理文件,而广义的文件则可以是Linux管理的所有对象

这些广义的文件利用VFS机制,以文件系统的形式挂载在Linux内中,对外提供一致的文件操作接口。

从数值上看,文件描述符是一个非负整数,其本质就是一个句柄,所以也可以认为文件描述符就是一个文件句柄

那么何为句柄呢?

一切对于用户透明的返回值,即可视为句柄

重点:用户空间利用文件描述符与内核进行交互;而内核拿到文件描述符后,可以通过它得到用于管理文件的真正的数据结构。

使用文件描述符即句柄,有两个好处:

一是增加了安全性句柄类型对用户完全透明,用户无法通过任何hacking的方式,更改句柄对应的内部结果,比如Linux内核的文件描述符,只有内核才能通过该值得到对应的文件结构;

二是增加了可扩展性,用户的代码只依赖于句柄的值,这样实际结构的类型就可以随时发生变化,与句柄的映射关系也可以随时改变,这些变化都不会影响任何现有的用户代码

Linux的每个进程都会维护一个文件表,以便维护该进程打开文件的信息,包括打开的文件个数、每个打开文件的偏移量等信息。

1.1.2 内核文件表的实现

内核中进程对应的结构是task_struct进程的文件表保存在task_struct->files中。其结构代码如下所示。

struct files_struct {

/* count为文件表

iles_struct的引用计数*/

atomic_t count;
/* 文件描述符表

*/

/*

为什么有两个fdtable呢?这是内核的一种优化策略。fdt为指针,而fdtab为普通变量。一般情况下,fdt是指向fdtab的,当需要它的时候,才会真正动态申请内存。因为默认大小的文件表足以应付大多数情况,因此这样就可以避免频繁的内存申请

这也是内核的常用技巧之一。在创建时,使用普通的变量或者数组,然后让指针指向它,作为默认情况使用。只有当进程使用量超过默认值时,才会动态申请内存。

*/
struct fdtable __rcu *fdt;

**

struct fdtable fdtab;

**

/*

written part on a separate cache line in SMP*/

/* 使用____cacheline_aligned_in_smp可以保证

file_lock是以cache line 对齐的,避免了false sharing */

spinlock_t file_lock ____cacheline_aligned_in_smp;
/* 用于查找下一个空闲的fd */
int next_fd;
/* 保存执行exec需要关闭的文件描述符的位图*/
struct embedded_fd_set close_on_exec_init;
/* 保存打开的文件描述符的位图

*/

struct embedded_fd_set open_fds_init;
/*

**fd_array为一个固定大小的file结构数组。struct file是内核用于文件管理的结构。这里使用默认大小的数组,就是为了可以涵盖大多数情况,避免动*态分配/

**

struct file __rcu * fd_array[NR_OPEN_DEFAULT];

**

};

下面看看files_struct是如何使用默认的fdtabfd_array的,initLinux的第一个进程,它的文件表是一个全局变量,代码如下:


struct files_struct init_files = {

.count = ATOMIC_INIT(1),
.fdt = &init_files.fdtab,
.fdtab = {
.max_fds = NR_OPEN_DEFAULT,
.fd = &init_files.fd_array[0],.close_on_exec = (fd_set *)&init_files.close_on_exec_init,
.open_fds = (fd_set *)&init_files.open_fds_init,
},
.file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock),

};


init_files.fdtinit_files.fdtab.fd都分别指向了自己已有的成员变量,并以此作为一个默认值。后面的进程都是从init进程fork出来的。fork的时候会调用dup_fd,而在dup_fd中其代码结构如下:

newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);

if (!newf)

goto out;

atomic_set(&newf->count, 1);

spin_lock_init(&newf->file_lock);

newf->next_fd = 0;

new_fdt = &newf->fdtab;

new_fdt->max_fds = NR_OPEN_DEFAULT;

new_fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init;

new_fdt->open_fds = (fd_set *)&newf->open_fds_init;

new_fdt->fd = &newf->fd_array[0];

new_fdt->next = NULL;

初始化new_fdt,同样是为了让new_fdt和new_fdt->fd指向其本身的成员变量fdtab和fd_array。

/proc/pid/status为对应pid的进程的当前运行状态,其中FDSize值即为当前进程max_fds的值。

因此,初始状态下,files_struct、fdtable和files的关系如图1-1所示。

 

举报

相关推荐

0 条评论