歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

走馬觀花: Linux 系統調用 open 七日游(七)

【場景三】open(pathname, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)

在這個場景中我們希望創建一個新文件(O_CREAT),並賦予該文件用戶可讀(S_IRUSR)和用戶可寫(S_IWUSR)的權限,然後以只寫(O_WRONLY)的方式打開這個文件。O_EXCL 在這裡保證該文件必須被創建,如果該文件已經存在則失敗返回。

這些標志位的作用已經解釋得很清楚了,現在來看看 build_open_flags:

【fs/open.c】sys_open

> do_sys_open > build_open_flags

點擊(此處)折疊或打開

static inline int build_open_flags(int flags, umode_t mode,

struct open_flags *op)

{

...

if (flags

& (O_CREAT

| __O_TMPFILE))

op->mode

= (mode

& S_IALLUGO)

| S_IFREG;

...

acc_mode = MAY_OPEN

| ACC_MODE(flags);

...

if (flags

& O_CREAT)

{

op->intent

|= LOOKUP_CREATE;

if (flags

& O_EXCL)

op->intent

|= LOOKUP_EXCL;

}

...

}

由於是創建一個新文件,所以 mode 就不能丟棄了(845),在這裡 mode 就是“S_IRUSR | S_IWUSR”。然後在我們的情境中,875 行 acc_mode 將被設置成“MAY_OPEN | MAY_WRITE”。最後 intent 也被設置成了相應的查詢模式(894、896)。接著還是 do_last:

【fs/namei.c】sys_open

> do_sys_open >

do_filp_open >

path_openat > do_last

點擊(此處)折疊或打開

static int do_last(struct nameidata

*nd, struct path

*path,

struct file *file,

const struct open_flags

*op,

int *opened, struct filename

*name)

{

...

if (!(open_flag

& O_CREAT))

{

...

} else

{

...

error

= complete_walk(nd);

if (error)

return error;

audit_inode(name, dir, LOOKUP_PARENT);

error

= -EISDIR;

/* trailing slashes?

*/

if (nd->last.name[nd->last.len])

goto out;

}

retry_lookup:

if (op->open_flag

& (O_CREAT

| O_TRUNC | O_WRONLY

| O_RDWR))

{

error

= mnt_want_write(nd->path.mnt);

if (!error)

got_write =

true;

/*

* do _not_ fail yet

- we might not need that

or fail with

* a different

error; let lookup_open() decide; we'll be

* dropping this one anyway.

*/

}

mutex_lock(&dir->d_inode->i_mutex);

error = lookup_open(nd, path, file, op,

got_write, opened);

mutex_unlock(&dir->d_inode->i_mutex);

...

首先會調用 complete_walk 告別 rcu-walk 模式,然後會判斷這個最終目標是不是以“/”結尾,如果是的話就表示最終目標是一個目錄那就返回並報錯“Is a directory”(2926)。隨後,如果本次操作是有可能“寫入”的(2931),那就需要取得當前文件系統的寫權限,但是注釋上寫的很明白,就算現在獲取寫權限失敗也不要急著返回,因為首先現在只是“有可能”會“寫入”,其次我們可能會因為別的原因失敗,所以現在先不理會這次的 fail,讓 lookup_open 來決定這一切吧。

接著就是 lookup_open,我們進去看看:

【fs/namei.c】sys_open

> do_sys_open >

do_filp_open >

path_openat > do_last > lookup_open

點擊(此處)折疊或打開

static int lookup_open(struct nameidata

*nd, struct path

*path,

struct file *file,

const struct open_flags

*op,

bool got_write,

int *opened)

{

...

*opened &=

~FILE_CREATED;

dentry = lookup_dcache(&nd->last, dir,

nd->flags,

&need_lookup);

...

dentry = lookup_real(dir_inode, dentry, nd->flags);

...

if (!dentry->d_inode

&&

(op->open_flag

& O_CREAT))

{

...

if (!got_write)

{

error

= -EROFS;

goto out_dput;

}

*opened

|= FILE_CREATED;

...

error

= vfs_create(dir->d_inode, dentry, mode,

nd->flags

& LOOKUP_EXCL);

if (error)

goto out_dput;

}

...

}

這裡我們需要關注一個局部變量:opened,首先會清除改變量的 FILE_CREATED 位(2813),如果該文件的確不存在的話會被重新賦上 FILE_CREATED,表示這個文件是這次創建的(2851)。最後,vfs_create 會調用具體文件系統的 inode_operations.create 函數真正創建這個文件。

好了,回到 do_last:

【fs/namei.c】sys_open

> do_sys_open >

do_filp_open >

path_openat > do_last

點擊(此處)折疊或打開

...

if (*opened

& FILE_CREATED)

{

/* Don't check

for write permission, don't truncate

*/

open_flag &=

~O_TRUNC;

will_truncate =

false;

acc_mode = MAY_OPEN;

path_to_nameidata(path, nd);

goto finish_open_created;

}

...

error =

-EEXIST;

if ((open_flag

& (O_EXCL

| O_CREAT))

==

(O_EXCL | O_CREAT))

goto exit_dput;

...

finish_open_created:

error = may_open(&nd->path,

acc_mode, open_flag);

if (error)

goto out;

file->f_path.mnt

= nd->path.mnt;

error = finish_open(file, nd->path.dentry,

NULL, opened);

...

}

這裡有兩個地方用到了局部變量 opened,首先如果這個文件就是本次新建的,那麼就要清除 O_TRUNC 位(2959)。O_TRUNC 用來將打開的文件長度“截斷”為零,其實就是將文件清空,因為我們是新建的文件所以這個標志位顯然是沒用的了。既然已經成功的創建了新文件,那麼寫權限也沒必要檢查了(2961),然後直接跳轉到標號 finish_open_created 處完成打開。如果這個文件本來就存在呢?這時會檢查 O_EXCL 和 O_CREAT 兩個標志位,因為 O_EXCL 標志位要求必須創建成功,所以這裡就會返回“File

exists”錯誤(2983)。

最後 may_open 檢查相應的權限,然後 finish_open 完成打開操作,這兩個函數我們已經看過了,這裡就不深入了。

現在,我們終於到了說再見的時候了,希望這七天的旅行能讓你有所收獲。

本來只是在看完代碼後想給自己留下點什麼,要不然以後忘了就白看了,但是等寫的時候才發現要想寫得明白光馬馬虎虎看完遠遠不夠,於是就越寫越多。大家可以發現在這幾篇博客中有好多自問自答,這都是我在看代碼時的疑問,有些回答可能並不是那麼准確,還請海涵。最後推薦兩本參考書籍:

《Linux 內核源代碼情景分析》,可以說這本書寫得相當精彩,雖然它內核版本是 2.4.0 相對來說有點老了,但是並不影響我們探索內核腳步,萬變不離其宗,有很多東西並沒有本質上的變化。

《深度探索 Linux 操作系統》,這本書將從另一個角度帶我們領略 Linux 的美麗和神秘,在一步一步建立並編譯自己的內核的同時,也深刻的揭示了編譯、鏈接、加載的原理,同樣是一本精彩的 Linux 讀物。

Copyright © Linux教程網 All Rights Reserved