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 篇一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html5a链接_关于html a、ht
- 下一篇: iview template模式_使用I