打通用戶態程序和內核系列之一:用戶態程序如何執行系統調用

前言

之前弄個一段時間內核,而最近在應用程序特別是C++ 方面開發多一些。當前日常工作中碰到一些性能分析、不同鎖API選擇的問題,發現由於對用戶態程序API背後的工作原理,特別是它和內核的調用關係、在內核中具體實現過程不甚清楚,導致前期的預期無法進行分析。爲此,打算結合工作中碰到的問題,比如:

  1. 用戶態程序如何執行系統調用;
  2. 用戶態鎖的底層實現及其和內核態鎖的關係如何;
  3. 用戶態不同鎖(自旋鎖,讀寫鎖,條件鎖)和無鎖機制的對比該如何做理論分析;
  4.  Libaio 異步IO在內核中的具體使怎樣實現的;
  5.  系統調用、線程調度、中斷分別對系統性能、CPU消耗的影響如何;

希望通過理清上面的這些基礎問題,打通腦子中相互孤立的知識點,幫助他們建立起比較緊密的聯繫。

系統調用ABC

系統調用的功能

從操作系統的角度看,系統調用是內核提供用戶態程序操作最底層軟件或硬件的接口。Linux 系統中每種硬件系統架構都有自己的系統調用表,比如下面mips的系統調用表:

 EXPORT(sys_call_table)
    PTR sys_read            /* 5000 */
    PTR sys_write
    PTR sys_open
    PTR sys_close
        ....
        PTR sys_statx
    PTR sys_rseq
    PTR sys_io_pgetevents
    .size   sys_call_table,.-sys_call_table

        ...
        NESTED(handle_sys64, PT_SIZE, sp)
#if !defined(CONFIG_MIPS32_O32) && !defined(CONFIG_MIPS32_N32)
    /*
     * When 32-bit compatibility is configured scall_o32.S
     * already did this.
     */
    .set    noat
    SAVE_SOME
    TRACE_IRQS_ON_RELOAD
    STI
    .set    at
        .....

        syscall_common:
    dsubu   t2, v0, __NR_64_Linux
    sltiu   t0, t2, __NR_64_Linux_syscalls + 1
    beqz    t0, illegal_syscall

    dsll    t0, t2, 3       # offset into table
    dla t2, sys_call_table
    daddu   t0, t2, t0
    ld  t2, (t0)        # syscall routine
    beqz    t2, illegal_syscall

    jalr    t2          # Do The Real Thing (TM)

系統調用的工作過程

從調用者的角度看:

(1)參考系統調用的操作規範,準備參數和數據;
(2)通過系統調用的標準接口模式,調用系統調用的接口;

從被調用者的角度看:

(1)將處理機狀態由用戶態轉爲系統態之後,由硬件和內核程序進行系統調用的一般性處理,即首先保護被中斷進程的CPU環境;

(2)分析系統調用類型,轉入相應的系統調用處理子程序;

(3)在系統調用處理子程序執行完後,恢復被中斷的或設置新進程的CPU現場,然後返冋被中斷進程或新進程,繼續往下執行。

用戶態程序快速調用系統調用

當前爲了快速開發,也可以通過syscall 快速調用,比如下面Libaio的兩個快速調用的實現:

/* Actual syscalls */
int io_setup(int maxevents, io_context_t *ctxp) {
    return syscall(_io_setup, maxevents, ctxp);
}

int io_destroy(io_context_t ctx) {
    return syscall(__io_destroy, ctx);
}

讀者可以根據上面的示例,調用自己的系統調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章