read系統呼叫是glibc庫裡面的乙個函式,是對系統呼叫函式sys_read()的封裝與實現。glic庫會將read函式在使用者態下進行解析,通過暫存器將引數儲存起來,並借助於系統呼叫名稱獲得系統呼叫號,該系統呼叫號又可以作為系統呼叫函式在sys_call_table中的索引獲取函式入口位址,該錶位於linux-4.13.16\arch\x86\entry\syscalls\syscall_64.tbl中,接下來就是即將要分析的sys_read呼叫了
1.核心原始碼特殊用法
[1]__user屬性
該屬性的主要目的是便於通過 sparse 工具檢查 linux核心**。[include/linux/compile.h]
中定義了該屬性:
# define __user __attribute__((noderef, address_space(1)))
其中__attribute__是gcc的編譯屬性,主要用於改變所宣告或定義的函式或資料的特性,它有很多子項,用於改變作用物件的特性。比如對函式,noline將禁止進行內聯擴充套件、noreturn表示沒有返回值、pure表明函式除返回值外,不會通過其它(如全域性變數、指標)對函式外部產生任何影響。此處的__user告訴編譯器它是乙個使用者資料,這樣在進行核心編譯時就可以進行語法檢查。這類特性均是核心開發人員需要關注的事情,目前無需關注。如果使用了 __user 巨集的指標不在使用者位址空間初始化, 或者指向核心位址空間, 裝置位址空間等等, sparse會給出警告.
[2]asmlinkage關鍵字
asmlinkage在核心原始碼**現的頻率非常高,它是告訴編譯器在本地堆疊中傳遞引數,與之對應的是fastcall;fastcall是告訴編譯器在通用暫存器中傳遞引數。執行時,直接從通用暫存器中取函式引數,要比在本地堆疊(記憶體)中取,速度快很多。
2.read code
#define ebadf 9
/* bad file number */
(定義在errno.h中的錯誤**)
syscall_define3
(read,
unsigned
int, fd,
char __user *
, buf, size_t, count)
return ret;
}
可以看到函式的實現開始於fdget_pos()函式(定義於linux-4.13.16\include\linux\file.h),該函式得到乙個struct fd結構:
struct fd
;static
inline
struct fd fdget_pos
(int fd)
static
inline
struct fd __to_fd
(unsigned
long v)
;}
可知fdget_pos(int fd)首先呼叫linux-4.13.16\fs\file.c中的__fdget_pos(int fd)函式,然後呼叫__to_fd()函式,最後總之其主要目的就是將給定的只有數字的檔案描述符轉化為其對應的 fd 結構。接下來先檢查是否成功找到檔案,然後呼叫file_pos_read()函式:
static
inline loff_t file_pos_read
(struct file *file)
該函式返回fd檔案在當前程序中的檔案偏移量,接下來呼叫vfs_read(f.file, buf, count, &pos)函式,此函式功能為從指定檔案的指定位置讀入資料到指定緩衝中。此處不深入vfs_read函式的細節,在下面再回過頭來解析。vfs_read結束相關工作後, 檢查結果若成功執行,使用file_pos_write 函式改變在檔案中的位置:
static
inline
void
file_pos_write
(struct file *file, loff_t pos)
static
inline
void
fdput_pos
(struct fd f)
該函式是在解鎖共享檔案描述符的執行緒併發讀檔案時保護檔案位置的互斥量f_pos_lock。最後返回此次系統呼叫成功讀取的位元組數,即vfs_read()函式的返回值ret。
注:f_pos_lock是struct file 的成員變數struct mutex f_pos_lock
3.vfs_read函式解析
ssize_t vfs_read
(struct file *file,
char __user *buf, size_t count, loff_t *pos)
inc_syscr
(current);}
return ret;
}
故忽略一些技術性的檢查措施之後其**如上所示,接下來主要實現由__vfs_read()函式實現:
#define einval 22
/* invalid argument */
typedef
long
long __kernel_loff_t;
//在linux-4.13.16\include\uapi\asm-generic\posix_types.h中
typedef __kernel_loff_t loff_t;
中ssize_t __vfs_read
(struct file *file,
char __user *buf, size_t count,
loff_t *pos)
其實該檔案啟用的f_op->read操作是該檔案對應的檔案系統的read的操作,此處所指的檔案系統即我們常說的虛擬檔案系統是乙個統一介面,而具體的檔案系統有ext3/4檔案系統。下面以ext4為例剖析ext4檔案系統讀寫操作:
const
struct file_operations ext4_file_operations =
;
顯然ext4沒有定義read與write操作,故此時會呼叫f_op->read_iter也即 ext4_file_operations->ext4_file_read_iter:
static ssize_t ext4_file_read_iter
(struct kiocb *iocb,
struct iov_iter *to)
ext4_file_read_iter最終會呼叫generic_file_read_iter函式:
ssize_t
generic_file_read_iter
(struct kiocb *iocb,
struct iov_iter *iter)
retval =
do_generic_file_read
(file,
&iocb->ki_pos, iter, retval)
;//buffer io
out:
return retval;
}
該函式的主要目的是區分是否使用快取讀取資料。顯然當發現定義有iocb_direct時會直接從磁碟上讀取資料,否則則呼叫do_generic_file_read()函式從快取讀取,大多數檔案系統預設使用快取。 read系統呼叫,mmap系統呼叫
read系統呼叫,mmap系統呼叫 2012 07 23 09 54 28 分類 linux 標籤 linux 檔案系統 虛擬記憶體 儲存系統 字型大小 訂閱 一般情況下,操作檔案既可以使用標準i o,也可直接使用系統呼叫。兩者有何區別呢?在輸入輸出中,直接使用底層的系統呼叫效率是非常低的,為什麼?...
read 系統呼叫剖析
注 所有清單中 均來自 linux2.6.11 核心原 讀資料之前,必須先開啟檔案。處理 open 系統呼叫的核心函式為 sys open 所以我們先來看一下該函式都作了哪些事。清單1顯示了 sys open 的 省略了部分內容,以後的程式清單同樣方式處理 清單1 sys open 函式 asmli...
檔案系統 read系統呼叫剖析(一)
一 read系統呼叫剖析 1,kernel層的read系統呼叫的入口函式是在kernel fs read write.c檔案中,如下所示 372 syscall define3 read,unsigned int,fd,char user buf,size t,count 373 386 retur...