首頁 >系統教程 >Linux >Syscall系統呼叫Linux核心追蹤

Syscall系統呼叫Linux核心追蹤

WBOY
WBOY轉載
2024-02-12 21:21:14458瀏覽

在Linux使用者空間中,我們常常需要呼叫系統呼叫。下面我們以Linux2.6.37版本為例,追蹤read系統呼叫的實作。不同版本的Linux系統呼叫實作可能會有所不同。

Syscall系統呼叫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;

程式碼解析:

  • 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 檔案系統層。

以上是Syscall系統呼叫Linux核心追蹤的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:lxlinux.net。如有侵權,請聯絡admin@php.cn刪除