在Linux使用者空間中,我們常常需要呼叫系統呼叫。下面我們以Linux2.6.37版本為例,追蹤read系統呼叫的實作。不同版本的Linux系統呼叫實作可能會有所不同。
#在一些應用程式中,我們可以看到如下定義:
scssCopy code #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;
程式碼解析:
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
到此,虛擬檔案系統層所做的處理就完成了,控制權交給了 ext2 檔案系統層。
以上是Syscall系統呼叫Linux核心追蹤的詳細內容。更多資訊請關注PHP中文網其他相關文章!