在 Linux 中打开文件究竟做了什么?
当我们谈论打开文件时,我们会遇到不同的情况,例如我们实际在使用什么语言和什么 API 来打开文件。虽然在大多数情况下这很简单,但高级语言最终会调用 C API 或直接调用 Linux 的 **open()** 函数,该函数也是用 C 编写的。
如果我们尝试讨论不同的语言,这是一个非常广泛的问题,无法在一篇文章中涵盖,这是因为当添加新的语言和不同的 API 时,复杂性会大幅增加。
现在,假设我们讨论的是 C 和 Linux,让我们首先看一下在 Linux 中打开文件时调用的 C 代码。
请考虑以下代码 -
int sys_open(const char *filename, int flags, int mode) { char *tmp = getname(filename); int fd = get_unused_fd(); struct file *f = filp_open(tmp, flags, mode); fd_install(fd, f); putname(tmp); return fd; }
以上代码也可以在 Linux 机器上的 fs/open.c 文件中找到。
现在,我们可以看到,从这个函数中调用了许多函数,例如第一个函数名为 getname(),我们向其中传递文件名作为参数,getname() 函数的代码如下所示 -
#define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL) #define putname(name) kmem_cache_free(names_cachep, (void *)(name)) char *getname(const char *filename) { char *tmp = __getname(); /* allocate some memory */ strncpy_from_user(tmp, filename, PATH_MAX + 1); return tmp; }
以上代码可以在 fs/namei.c 文件中找到,其主要用途是从用户空间复制文件名并将其传递到内核空间。然后,在 **getname()** 函数之后,我们有 **get_unused_fd()** 函数,它为我们返回一个未使用的文件描述符,它只不过是当前打开的文件的可增长列表中的一个整数索引。**get_unused_fd()** 函数的代码如下所示 -
int get_unused_fd(void) { struct files_struct *files = current->files; int fd = find_next_zero_bit(files->open_fds, files->max_fdset, files->next_fd); FD_SET(fd, files->open_fds); /* in use now */ files->next_fd = fd + 1; return fd; }
现在我们有 filp_open() 函数,其实现如下 -
struct file *filp_open(const char *filename, int flags, int mode) { struct nameidata nd; open_namei(filename, flags, mode, &nd); return dentry_open(nd.dentry, nd.mnt, flags); }
以上函数扮演两个关键角色,首先,它使用文件系统查找与传递的路径的文件名对应的 inode。接下来,它创建一个 struct file,其中包含有关 inode 的所有必要信息,然后返回该文件。
现在,调用栈中的下一个函数是 fd_install() 函数,它可以在 include/linux/file.h 文件中找到。它用于存储 filp_open() 函数返回的信息。fd_install() 函数的代码如下所示 -
void fd_install(unsigned int fd, struct file *file) { struct files_struct *files = current->files; files->fd[fd] = file; }
然后我们有 store() 函数,它存储从 filp_open() 函数返回的结构体,然后将该结构体安装到进程的打开文件列表中。
下一步是释放分配的内核控制内存块。最后,它返回文件描述符,然后可以将其传递给其他 C 函数,例如 close()、write() 等。