在Linux的使用者空間,我們經常會調用系統調用,下面我們跟蹤一下read系統調用,使用的Linux核心版本為Linux2.6.37。不同的Linux版本其中的實現略有不同。在一些應用中我們可以看到下面的一些定義:
#define real_read(fd, buf, count ) (syscall(SYS_read, (fd), (buf), (count)))
|
其實真正調用的還是系統函數syscall(SYS_read),也就是sys_read()函數中,在Linux2.6.37中的利用幾個宏定義實現。
Linux 系統調用(SCI,system call interface)的實現機制實際上是一個多路匯聚以及分解的過程,該匯聚點就是 0x80 中斷這個進入點(X86 系統結構)。也就是說,所有系統調用都從使用者空間中匯聚到 0x80 中斷點,同時儲存具體的系統調用號。當 0x80 中斷處理常式運行時,將根據系統調用號對不同的系統調用分別處理(調用不同的核心功能處理)。
引起系統調用的兩種途徑
(1)int $0×80 , 老式linux核心版本中引起系統調用的唯一方式
(2)sysenter彙編指令
在Linux核心中使用下面的宏進行系統調用
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) { struct file *file; ssize_t ret = -EBADF; int fput_needed;
file = fget_light(fd, &fput_needed); if (file) { loff_t pos = file_pos_read(file); ret = vfs_read(file, buf, count, &pos); file_pos_write(file, pos); fput_light(file, fput_needed); }
return ret; }
|
其中SYSCALL_DEFINE3的宏定義如下:
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
|
##的意思就是宏中的字元直接替換,
如果name = read,那麼在宏中__NR_##name就替換成了__NR_read了。 __NR_##name是系統調用號,##指的是兩次宏展開.即用實際的系統調用名字代替"name",然後再把__NR_...展開.如name == ioctl,則為__NR_ioctl。
#ifdef CONFIG_FTRACE_SYSCALLS #define SYSCALL_DEFINEx(x, sname, ...) \ static const char *types_##sname[] = { \ __SC_STR_TDECL##x(__VA_ARGS__) \ }; \ static const char *args_##sname[] = { \ __SC_STR_ADECL##x(__VA_ARGS__) \ }; \ SYSCALL_METADATA(sname, x); \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #else #define SYSCALL_DEFINEx(x, sname, ...) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) #endif
|
不管是否定義CONFIG_FTRACE_SYSCALLS宏,最終都會執行 下面的這個宏定義:
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
#define SYSCALL_DEFINE(name) static inline long SYSC_##name
#define __SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \ static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \ { \ __SC_TEST##x(__VA_ARGS__); \ return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \ } \ SYSCALL_ALIAS(sys##name, SyS##name); \ static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))
#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */
#define SYSCALL_DEFINE(name) asmlinkage long sys_##name #define __SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
|
最終會調用下面類型的宏定義:
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
也就是我們前面提到的sys_read()系統函數。
asmlinkage通知編譯器僅從棧中提取該函數的參數。所有的系統調用都需要這個限定詞!這和我們上一篇文章quagga中提到的宏定義,有異曲同工之妙。
也就是宏定義中的下面代碼:
struct file *file; ssize_t ret = -EBADF; int fput_needed;
file = fget_light(fd, &fput_needed); if (file) { loff_t pos = file_pos_read(file); ret = vfs_read(file, buf, count, &pos); file_pos_write(file, pos); fput_light(file, fput_needed); }
return ret;
|
代碼解析:
- fget_light() :根據 fd 指定的索引,從當前進程描述符中取出相應的 file 對象(見圖3)。
- 如果沒找到指定的 file 對象,則返回錯誤
- 如果找到了指定的 file 對象:
- 調用 file_pos_read() 函數取出此次讀寫檔案的當前位置。
- 調用 vfs_read() 執行檔案讀取操作,而這個函數最終調用 file->f_op.read() 指向的函數,代碼如下:
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
- 調用 file_pos_write() 更新檔案的當前讀寫位置。
- 調用 fput_light() 更新檔案的引用計數。
- 最後返回讀取資料的位元組數。
到此,虛擬檔案系統層所做的處理就完成了,控制權交給了 ext2 檔案系統層。
http://blogold.chinaunix.net/u3/104447/showart_2527011.html