在 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() 等。

更新於:2021-07-31

569 次瀏覽

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.