日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

syscall 系统调用陷入_linux 系统调用open 篇一

發布時間:2025/3/19 linux 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 syscall 系统调用陷入_linux 系统调用open 篇一 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

內核源碼:linux-4.4 目標平臺:ARM體系結構 源碼工具:source insight 4

說明: 文中由于 md 語法問題,無法在代碼高亮的同時而忽略由于 __ 或者 * 造成斜體的 問題,所以類似 __user 改成 __ user,或者 char *filename 改成 char* filename。 通過在中間添加空格進行避免。注釋統一使用了 \\。

open 對應的內核系統調用

應用層的 open 函數是 glibc 庫封裝了系統調用以比較友好的方式提供給開發者。 那么為什么要這么做? 這主要是從安全以及性能這兩大方面進行了考慮:

在用戶空間和內核空間之間,有一個叫做Syscall(系統調用, system call)的中間層,是連接用 戶態和內核態的橋梁。這樣即提高了內核的安全型,也便于移植, 只需實現同一套接口即可。Linux系統,用戶空間通過向內核空間發出Syscall,產生軟中斷, 從而讓程序陷入內核態,執行相應的操作。對于每個系統調用都會有一個對應的系統調用號 ,比很多操作系統要少很多。

安全性與穩定性:內核駐留在受保護的地址空間,用戶空間程序無法直接執行內核代碼 ,也無法訪問內核數據,通過系統調用

性能:Linux上下文切換時間很短,以及系統調用處理過程非常精簡,內核優化得好,所以性能上 往往比很多其他操作系統執行要好。

在應用層對于 open 操作主要使用的是以下兩個函數:

(1) int open(const char *pathname, int flags, mode_t mode);

(2) int openat(int dirfd, const char *pathname, int flags, mode_t mode);

復制代碼

如果打開文件成功,那么返回文件描述符,值大于或等于0;如果打開文件失敗,返 回負的錯誤號。

下面是該函數參數的說明:

參數 pathname 是文件路徑,可以是相對路徑(即不以 “/” 開頭),也可以是絕對路徑(即以 “/” 開頭)。

參數 dirfd 是打開一個目錄后得到的文件描述符,作為相對路徑的基準目錄。如果文件路徑是 相對路徑,那么在函數 openat 中解釋為相對文件描述符 dirfd 引用的目錄,open 函數中解釋為相對 調用進程的當前工作目錄。如果文件路徑是絕對路徑, openat 忽略參數 dirfd。

參數 flags 必須包含一種訪問模式: O_RDONLY (只讀)、O_ WRONLY (只寫)或 O_RDWR(讀寫)。參數 flags 可以包含多個文件創建標志和文件狀態標志。 兩組標志的區別是: 文件創建標志只影響打開操作, 文件狀態標志影響后面的讀寫操作。

文件創建標志包括如下:

O_CLOEXEC:開啟 close-on-exc標志,使用系統調用 execve() 裝載程序的時候關閉文件。

CREAT:如果文件不存在,創建文件。

ODIRECTORY:參數 pathname 必須是一個日錄。

EXCL:通常和標志位 CREAT 聯合使用,用來創建文件。如果文件已經存在,那么 open() 失敗,返回錯誤號 EEXIST。

NOFOLLOW:不允許參數 pathname 是符號鏈接,即最后一個分量不能是符號 鏈接,其他分量可以是符號鏈接。如果參數 pathname 是符號鏈接,那么打開失敗,返回錯誤號 ELOOP。

O_TMPFILE:創建沒有名字的臨時普通文件,參數 pathname 指定目錄關閉文件的時候,自動刪除文件。

O_TRUNC:如果文件已經存在,是普通文件并且訪問模式允許寫,那么把文件截斷到長度為0。

文件狀態標志包括如下:

APPEND:使用追加模式打開文件,每次調用 write 寫文件的時候寫到文件的末尾。

O_ASYNC:啟用信號驅動的輸入輸出,當輸入或輸出可用的時候,發送信號通知進程,默認的信號是 SIGIO。

O_DIRECT:直接讀寫存儲設備,不使用內核的頁緩存。雖然會降低讀寫速度, 但是在某些情況下有用處,例如應用程序使用自己的緩沖區,不需要使用內核的頁緩存文件。

DSYNC:調用 write 寫文件時,把數據和檢索數據所需要的元數據寫回到存儲設備

LARGEFILE:允許打開長度超過 4 GB 的大文件。

NOATIME:調用 read 讀文件時,不要更新文件的訪問時間。

O_NONBLOCK:使用非阻塞模式打開文件, open 和以后的操作不會導致調用進程阻塞。

PATH:獲得文件描述符有兩個用處,指示在目錄樹中的位置以及執行文件描述符層次的操作。 不會真正打開文件,不能執行讀操作和寫操作。

O_SYNC:調用 write 寫文件時,把數據和相關的元數據寫回到存儲設備。

參數 mode: 參數 mode 指定創建新文件時的文件模式。當參數 flags 指定標志位 O_CREAT 或 O_TMPFILE 的時候,必須指定參數 mode,其他情況下忽略參數 mode。 參數 mode 可以是下面這些標準的文件模式位的組合。

S_IRWXU(0700,以0開頭表示八進制):用戶(即文件擁有者)有讀、寫和執行權限。

S_IRUSR(00400):用戶有讀權限。

S_IWUSR(00200):用戶有寫權限

S_IXUSR(00100):用戶有執行權限。

S_IRWXG(00070):文件擁有者所在組的其他用戶有讀、寫和執行權限

S_IRGRP(00040):文件擁有者所在組的其他用戶有讀權限。

S_IWGRP(00020):文件擁有者所在組的其他用戶有寫權限。

S_IXGRP(0010):文件擁有者所在組的其他用戶有執行權限。

S_IRWXO(0007):其他組的用戶有讀、寫和執行權限。

S_IROTH(0004):其他組的用戶有讀權限。

S_IWOTH(00002):其他組的用戶有寫權限。

S_IXOTH(00001):其他組的用戶有執行權限。

參數 mode 可以包含下面這些 Linux 私有的文件模式位:

S_ISUID (0004000):set-user-ID 位。

S_ISGID (0002000):set-group-iD位。

S_ISVTX(0001000):粘滯(sticky)位。

那么我們該如何找到對應的 syscall? 有幾個小技巧可以用來幫助我們:

用戶空間的方法xxx,對應系統調用層方法則是 sys_xxx;

unistd.h 文件記錄著系統調用中斷號的信息。

宏定義 SYSCALL_DEFINEx(xxx,…),展開后對應的方法則是 sys_xxx;

方法參數的個數x,對應于 SYSCALL_DEFINEx。

根據第一個小技巧,我們知道我們需要找的函數為:sys_open。 具體代碼流程比較復雜,這里使用取巧的方式,找到對應的內核函數,前面提到需要找的的函數 為 sys_open。 這種函數在內核中是通過宏定義 SYSCALL_DEFINEx 展開后得到的。那么可以 利用 source insight 的搜索功能。應用層 open 函數的參數的個數為 3,可以假想先從 SYSCALL_DEFINE3 進行全局搜索。隨便選擇一個搜索結果,這里假設選擇的是 SYSCALL_DEFINE3(mknod,這步主要是為了獲取代碼格式,把 mknod 改成 open ,然后搜索 SYSCALL_DEFINE3(open。 很快我們就在 kernel\fs\open.c 文件中找到唯一的搜索結果,代碼如下:

SYSCALL_DEFINE3

SYSCALL_DEFINE3(open, const char __ user*, filename, int, flags, umode_t, mode)

{

if (force_o_largefile())

flags |= O_LARGEFILE;

return do_sys_open(AT_FDCWD, filename, flags, mode);

}

復制代碼

if (force_o_largefile())

flags |= O_LARGEFILE;

復制代碼

表示 flags 會在 64 位 Kernel 的情況下強制么設置 O_LARGEFILE 來表示支持大文件。 接著跳轉到 do_sys_open 函數。

do_sys_open 系統調用主體

long do_sys_open(int dfd, const char __ user *filename, int flags, umode_t mode){

struct open_flags op;

//檢查并包裝傳遞進來的標志位

int fd = build_open_flags(flags, mode, &op);

struct filename * tmp;

if (fd)

return fd;

//用戶空間的路徑名復制到內核空間

tmp = getname(filename);

if (IS_ERR(tmp))

return PTR_ERR(tmp);

//獲取一個未使用的 fd 文件描述符

fd = get_unused_fd_flags(flags);

if (fd >= 0) {

//調用 do_filp_open 完成對路徑的搜尋和文件的打開

struct file * f = do_filp_open(dfd, tmp, &op);

if (IS_ERR(f)) {

//如果發生了錯誤,釋放已分配的 fd 文件描述符

put_unused_fd(fd);

//釋放已分配的 struct file 數據

fd = PTR_ERR(f);

} else {

fsnotify_open(f);

//綁定 fd 與 f。

fd_install(fd, f);

}

}

//釋放已分配的 filename 結構體。

putname(tmp);

return fd;

}

復制代碼

fd 是一個整數,它其實是一個數組的下標,用來獲取指向 file 描述符的指針, 每個進程都有個 task_struct 描述符用來描述進程相關的信息,其中有個 files_struct 類型的 files 字段,里面有個保存了當前進程所有已打開文件 描述符的數組,而通過 fd 就可以找到具體的文件描述符,之間的關系可以參考下圖:

這里的參數已經在上面提到過了,唯一需要注意的是 AT_FDCWD,其定義在 include/uapi/linux/fcntl.h,是一個特殊值(** -100 **), 該值表明當 filename 為相對路徑的情況下將當前進程的工作目錄設置為起始路徑。相對而言, 你可以在另一個系統調用 openat 中為這個起始路徑指定一個目錄, 此時 AT_FDCWD 就會被該目錄的描述符所替代。

build_open_flags 初始化 flags

static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op){

int lookup_flags = 0;

//O_CREAT 或者 `__O_TMPFILE*` 設置了,acc_mode 才有效。

int acc_mode;

// Clear out all open flags we don't know about so that we don't report

// them in fcntl(F_GETFD) or similar interfaces.

// 只保留當前內核支持且已被設置的標志,防止用戶空間亂設置不支持的標志。

flags &= VALID_OPEN_FLAGS;

if (flags & (O_CREAT | __ O_TMPFILE))

op->mode = (mode & S_IALLUGO) | S_IFREG;

else

//如果 O_CREAT | __ O_TMPFILE 標志都沒有設置,那么忽略 mode。

op->mode = 0;

// Must never be set by userspace

flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;

// O_SYNC is implemented as __ O_SYNC|O_DSYNC. As many places only

// check for O_DSYNC if the need any syncing at all we enforce it's

// always set instead of having to deal with possibly weird behaviour

// for malicious applications setting only __ O_SYNC.

if (flags & __ O_SYNC)

flags |= O_DSYNC;

//如果是創建一個沒有名字的臨時文件,參數 pathname 用來表示一個目錄,

//會在該目錄的文件系統中創建一個沒有名字的 iNode。

if (flags & __ O_TMPFILE) {

if ((flags & O_TMPFILE_MASK) != O_TMPFILE)

return -EINVAL;

acc_mode = MAY_OPEN | ACC_MODE(flags);

if (!(acc_mode & MAY_WRITE))

return -EINVAL;

} else if (flags & O_PATH) {

// If we have O_PATH in the open flag. Then we

// cannot have anything other than the below set of flags

// 如果設置了 O_PATH 標志,那么 flags 只能設置以下 3 個標志。

flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;

acc_mode = 0;

} else {

acc_mode = MAY_OPEN | ACC_MODE(flags);

}

op->open_flag = flags;

// O_TRUNC implies we need access checks for write permissions

// 如果設置了,那么寫之前可能需要清空內容。

if (flags & O_TRUNC)

acc_mode |= MAY_WRITE;

// Allow the LSM permission hook to distinguish append

// access from general write access.

// 讓 LSM 有能力區分 追加訪問和普通訪問。

if (flags & O_APPEND)

acc_mode |= MAY_APPEND;

op->acc_mode = acc_mode;

//設置意圖,如果沒有設置 O_PATH,表示此次調用有打開文件的意圖。

op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;

if (flags & O_CREAT) {

//是否有創建文件的意圖

op->intent |= LOOKUP_CREATE;

if (flags & O_EXCL)

op->intent |= LOOKUP_EXCL;

}

//判斷查找的目標是否是目錄。

if (flags & O_DIRECTORY)

lookup_flags |= LOOKUP_DIRECTORY;

//判斷當發現符號鏈接時是否繼續跟下去

if (!(flags & O_NOFOLLOW))

lookup_flags |= LOOKUP_FOLLOW; //查找標志設置了 LOOKUP_FOLLOW 表示會繼續跟下去。

//設置查找標志,lookup_flags 在路徑查找時會用到

op->lookup_flags = lookup_flags;

return 0;

}

復制代碼

上面的函數主要是根據用戶傳遞進來的 flags 進一步設置具體的標志,然后把這些標志封裝到 open_flags 結構體中。以便后續使用。

接下來就是函數 getname() ,這個函數定義在 fs/namei.c,主體是 getname_flags, 我們撿重點的分析,無關緊要的代碼以 ... 略過。

getname_flags 復制路徑名

struct filename * getname(const char __ user *filename){

return getname_flags(filename, 0, NULL);

}

復制代碼

struct filename {

const char* name;// pointer to actual string ---指向真實的字符串

const __ user char* uptr;// original userland pointer -- 指向原來用戶空間的 filename

struct audit_names* aname;

intrefcnt;

const chariname[]; //用來保存 pathname

};

復制代碼

struct filename * getname_flags(const char __ user *filename, int flags, int* empty){

struct filename* result;

char* kname;

int len;

// 這里一般來說賦值為 NULL。這里主要是針對Linux 審計工具 audit,我們不管。

result = audit_reusename(filename);

// 如果不為空直接返回。

if (result)

return result;

// 通過__getname 在內核緩沖區專用隊列里申請一塊內存用來放置路徑名(filemname 結構體)

result = __getname();

if (unlikely(!result))

return ERR_PTR(-ENOMEM);

//First, try to embed the struct filename inside the names_cache

//allocation

//kname 指向 struct filename 的 iname 數組。

kname = (char*)result->iname;

// 把 filename->name 指向 iname[0],待會 iname 用來保存用戶空間傳遞過來的路徑名(filemname 結構體)。

result->name = kname;

//該函數把用戶空間的 filename 復制到 iname

len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX);

//如果復制失敗,釋放已分配的 result 并返回錯誤。

if (unlikely(len < 0)) {

__putname(result);

return ERR_PTR(len);

}

// Uh-oh. We have a name that's approaching PATH_MAX. Allocate a

// separate struct filename so we can dedicate the entire

// names_cache allocation for the pathname, and re-do the copy from

// userland.

// 這里判斷用戶空間傳遞過來的路徑名的長度接近了 PATH_MAX,所以需要分配一個獨立的空間

// 用來保存 struct filename 前面的字段,并把 name_cache 全部空間用來保存路徑名 (filename->iname)。

//

// #define PATH_MAX 4096 // 4 kb 大小。

// #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE * )0)->MEMBER)

// #define EMBEDDED_NAME_MAX(PATH_MAX - offsetof(struct filename, iname))

// EMBEDDED_NAME_MAX 指的就是:字段 iname 在 filename 結構體中的偏移。

if (unlikely(len == EMBEDDED_NAME_MAX)) {

// 注意,這里是把 iname[1] 的偏移賦值給了 size。這樣 size 的大小包含了 inaem[0]

// 可以用來保存 iname 數組的首地址。

const size_t size = offsetof(struct filename, iname[1]);

// 把舊 result 的首地址賦值給了 kanme。

kname = (char * )result;

// size is chosen that way we to guarantee that

// result->iname[0] is within the same object and that

// kname can't be equal to result->iname, no matter what.

// 分配一個獨立空間用來保存 filename,這樣就可以把 filename 分離出來。

result = kzalloc(size, GFP_KERNEL);

//分配失敗,釋放資源并返回錯誤。

if (unlikely(!result)) {

__putname(kname);

return ERR_PTR(-ENOMEM);

}

// 把原來的 filename 的首地址賦值給新分配的 result。這樣就實現了分離。

result->name = kname;

// 把用戶空間的 filename 復制到 kname(name_cache 起始地址)。

len = strncpy_from_user(kname, filename, PATH_MAX);

// 原來:

// filename struct(內核空間,用 name_cach 來保存)

// result ---> name_cache-----> name

// uptr

// aname

// .... 復制操作(strncpy_from_user())

// iname filename struct(用戶空間)

//

// 現在:

// filename struct(內核空間,注意這里是新開獨立的空間。)

// result ---> name ------> name_cache filename struct(用戶空間)

// uptr 復制操作(strncpy_from_user())

// aname

// ....

// iname

// 新分配的 filename 的首地址指向 name_cach,而 name_cach 又保存了用戶

// 空間的 filename,所以新的 filename(result) 能間接訪問到用戶空間的 filename。

// 復制失敗,釋放資源,返回。

if (unlikely(len < 0)) {

__putname(kname);

kfree(result);

return ERR_PTR(len);

}

// 路徑過長,同樣返回錯誤(從這里也可以看出,在 Linux 中路徑名的長度不能超過 4096 字節)。

if (unlikely(len == PATH_MAX)) {

__putname(kname);

kfree(result);

return ERR_PTR(-ENAMETOOLONG);

}

}

// 引用計數為 1

result->refcnt = 1;

// The empty path is special.空路徑的處理。

if (unlikely(!len)) {

if (empty)

* empty = 1;

// 如果 LOOKUP_EMPTY 沒有設置,也就是本次 open 操作的目標不是空路徑,但是傳遞了一個

// 空路徑,所以返回錯誤。

if (! (flags & LOOKUP_EMPTY)) {

//回收資源

putname(result);

return ERR_PTR(-ENOENT);

}

}

// 指向用戶空間的 filename

result->uptr = filename;

result->aname = NULL;

audit_getname(result);

return result;

}

復制代碼

struct filename {

const char* name; //pointer to actual string ---指向真實的字符串

const __ user char* uptr;//original userland pointer --- 指向原來用戶空間

struct audit_names* aname;

intrefcnt;

const chariname[]; //用來保存 pathname

};

復制代碼

首先通過 __getname 在內核緩沖區專用隊列里申請一塊內存用來放置路徑名,其實這塊內存就是 一個 4KB 的內存頁。這塊內存頁是這樣分配的,在開始的一小塊空間放置結構體 struct filename 結構體前面字段的信息,這里我們假設 iname 字段之前的結構使用 struct filename-iname 表示, 之后的空間放置字符串(保存在 iname)。初始化字符串指針 kname,使其指向這個字符串 (iname[])的首地址。然后就是拷貝字符串,返回值 len 代表了 已經 拷貝的字符串長度。如果這個字符串已經填滿了內存頁剩余空間,就說明該字符串的長度已經大于 4KB - (sizeof(struct filename-iname)了,這時就需要將結構體 struct filename-iname 從這個內存頁中分離并單獨分配空間,然后用整個內存頁保存該字符串。

get_unused_fd_flags 獲取 fd

get_unused_fd_flags() 函數用來查找一個可用的 fd(文件描述符)。

int get_unused_fd_flags(unsigned flags){

return __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags);

}

復制代碼

/*

* allocate a file descriptor, mark it busy.

*/

int __alloc_fd(struct files_struct *files,

unsigned start, unsigned end, unsigned flags)

{

unsigned int fd;

int error;

struct fdtable * fdt;

spin_lock(&files->file_lock);

repeat:

// 通過 files 字段獲取 fdt 字段。(該函數考慮了線程競爭,較復雜不展開了。)

fdt = files_fdtable(files);

//從 start 開始搜索

fd = start;

// 進程上一次獲取的 fd 的下一個號(fd + 1)保存在 next_fd 中。所以從 next_fd 開始進行查找。

if (fd < files->next_fd)

fd = files->next_fd;

if (fd < fdt->max_fds)

//獲取下一個 fd

fd = find_next_fd(fdt, fd);

// N.B. For clone tasks sharing a files structure, this test

// will limit the total number of files that can be opened.

error = -EMFILE;

if (fd >= end)

goto out;

// 獲取 fd 后,判斷是否需要擴展用來保存 file struct 描述符的數組(fdtable->fd)的容量。

// 返回 0 表示不需要,<0 表示錯誤,1 表示成功。

error = expand_files(files, fd);

if (error < 0)

goto out;

// If we needed to expand the fs array we

// might have blocked - try again.

// 1,擴容成功,并且重新嘗試獲取fd

// 因為擴容過程可能會發生阻塞,這期間就有可能其他線程也在獲取 fd,所以前面獲取的 fd

// 可能被其他線程搶先占用了,因為 Linux 的喚醒是不保證順序的。

if (error)

goto repeat;

if (start <= files->next_fd)

files->next_fd = fd + 1;

// 在 fdtable->open_fds 位圖中置位表示當前獲取的 fd 處于使用狀態。

// 也就是說當釋放該 fd 位圖中對應的位清除,從而達到重復使用的的目的。

__set_open_fd(fd, fdt);

// 如果設置了 O_CLOEXEC 標志,那么在 fdtable->close_on_exec 位圖對應的位置位。

// 前面提到過開啟 close-on-exc 標志,使用系統調用 execve() 裝載程序的時候會關閉設置過該標志的文件。

// Linux 中使用 fork() 產生子進程的時候回繼承父進程已打開的文件描述符集。execve() 一般就是在子進程

// 里用來運行新程序。

if (flags & O_CLOEXEC)

__set_close_on_exec(fd, fdt);

else

__clear_close_on_exec(fd, fdt);

// 設置返回值。

error = fd;

#if 1

// Sanity check 一些合法性檢查。

if (rcu_access_pointer(fdt->fd[fd]) != NULL) {

printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);

rcu_assign_pointer(fdt->fd[fd], NULL);

}

#endif

out:

spin_unlock(&files->file_lock);

return error;

}

復制代碼

struct fdtable {

unsigned int max_fds;

struct file __ rcu ** fd; // current fd array

unsigned long * close_on_exec;

unsigned long * open_fds;

unsigned long * full_fds_bits;

struct rcu_head rcu;

};

復制代碼

#ifdef CONFIG_64BIT

#define BITS_PER_LONG 64

#else

#define BITS_PER_LONG 32

#endif /* CONFIG_64BIT */

static inline void __set_open_fd(unsigned int fd, struct fdtable *fdt)

{

__set_bit(fd, fdt->open_fds);

fd /= BITS_PER_LONG;

if (!~fdt->open_fds[fd])

__set_bit(fd, fdt->full_fds_bits);

}

復制代碼

這里以 32 位 arm 芯片為例。其中函數 __set_bit 表示以某個地址開始在某個位置 1。 假設我們目前數組的容量為 128 ,那么如下表:共有 4 行,一行 32 列,fd = 32 * row + column。 每個格子中 0 表示當前 fd 沒有被占用,1 表示占用了。其中 ... 表示所有的列為 1。 假設我們現在獲取的 fd 為 66 也就是第 3 行第 3 列,此時我們可以看到該格子為 0。 調用 __set_bit(fd, fdt->open_fds); 把該位(66)置1,fd /= BITS_PER_LONG; 獲取行號 66 / 32 = 2(行號從 0 開始),!~fdt->open_fds[fd], open_fds為 long 類型 指針,也就是說步長為 32 位,相當于取第 3 個 long 數據的值,然后位取反,因為此時該 long 數據 的每一位都置 1 了,所以取反后的值為 0,!0 就為 true 了。此時我們可以確定第 3 行所有的 列都被使用了,所以我們可以把 full_fds_bits 的第 2 位置 1,表示該行已全部被使用。

0

1

2

...

30

31

1

1

1

...

1

1

1

1

1

...

1

1

1

1

0

...

1

1

1

1

1

...

0

0

接下來看找 fd 函數 find_next_fd() 就很簡單了。

static unsigned long find_next_fd(struct fdtable *fdt, unsigned long start){

unsigned long maxfd = fdt->max_fds;

// 當前容量最后的一行

unsigned long maxbit = maxfd / BITS_PER_LONG;

// 開始行

unsigned long bitbit = start / BITS_PER_LONG;

// 先找到一個空行(有空閑位的某一行)

bitbit = find_next_zero_bit(fdt->full_fds_bits, maxbit, bitbit) * BITS_PER_LONG;

if (bitbit > maxfd)

return maxfd;

if (bitbit > start)

start = bitbit;

// 在該行上找到一個具體的空位。

return find_next_zero_bit(fdt->open_fds, maxfd, start);

}

復制代碼

盡量以自己的能力對每行代碼進行了注釋,同時只是為了學習內核大神是如何玩轉指針以及數據結構。 可以從 __set_open_fd() 函數看出對指針熟練的使用方式,以及快速定位的思想。

總結

以上是生活随笔為你收集整理的syscall 系统调用陷入_linux 系统调用open 篇一的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。