Linux 系統(tǒng)調(diào)用

與內(nèi)核通信

為了和用戶空間上運(yùn)行的進(jìn)程進(jìn)行交互,內(nèi)核提供了一組接口。透過該接口丛楚,應(yīng)用程序可以訪問硬件設(shè)備和其他操作系統(tǒng)資源。這組接口在應(yīng)用程序和內(nèi)核之間扮演了使者的角色魂挂,應(yīng)用程序發(fā)送各種請求,而內(nèi)核負(fù)責(zé)滿足這些需求(或者讓應(yīng)用程序暫時擱置)。實(shí)際上提供這組接口主要是為了保證系統(tǒng)穩(wěn)定可靠,避免應(yīng)用程序恣意妄行顽决,惹出大麻煩短条。

系統(tǒng)調(diào)用在用戶空間進(jìn)程和硬件設(shè)備之間添加了一個中間層,該層的主要作用有三個才菠。第一慌烧,它為用戶空間提供了一種硬件的抽象接口。第二鸠儿,系統(tǒng)調(diào)用保證了系統(tǒng)的穩(wěn)定和安全。作為硬件設(shè)備和應(yīng)用程序之間的中間人厕氨,內(nèi)核可以基于權(quán)限和其他一些規(guī)則對需要的訪問進(jìn)行裁決进每。第三,每個進(jìn)程都運(yùn)行在虛擬系統(tǒng)中命斧,而在用戶空間和系統(tǒng)的其余部分提歐共這樣的一層公共接口田晚,也是出于這種考慮,如果應(yīng)用程序可以隨意訪問硬件爾內(nèi)核又對此一無所知的話国葬,幾乎就沒法實(shí)現(xiàn)多任務(wù)和虛擬內(nèi)存贤徒,當(dāng)然也不可能實(shí)現(xiàn)良好的穩(wěn)定性和安全性。

API汇四、POSIX和C庫

1接奈、一般情況下,應(yīng)用程序通過在用戶空間實(shí)現(xiàn)的應(yīng)用編程接口(API)而不是直接通過系統(tǒng)調(diào)用來編程通孽。一個API定義了一組應(yīng)用程序使用的編程接口序宦。它可以實(shí)現(xiàn)成一個系統(tǒng)調(diào)用,也可以通過調(diào)用多個系統(tǒng)調(diào)用來實(shí)現(xiàn)背苦,而完全不使用任何系統(tǒng)調(diào)用也不存在任何問題互捌。

2、在Unix系統(tǒng)中行剂,最流行的應(yīng)用程序編程接口是基于POSIX標(biāo)準(zhǔn)的秕噪。

3、Linux的系統(tǒng)調(diào)用作為C庫的一部分提供厚宰。C庫實(shí)現(xiàn)了Unix系統(tǒng)主要API腌巾,包括標(biāo)準(zhǔn)C庫函數(shù)和系統(tǒng)調(diào)用接口。

4固阁、應(yīng)用編程與系統(tǒng)調(diào)用無關(guān)緊要壤躲,但內(nèi)核只跟系統(tǒng)調(diào)用打交道;庫函數(shù)及應(yīng)用程序是怎么使用系統(tǒng)調(diào)用的备燃,不是內(nèi)核所關(guān)心的碉克。

5、Unix接口設(shè)計(jì)有一句格言:“提供機(jī)制而不提供策略”并齐,換句話說漏麦,Unix系統(tǒng)調(diào)用抽象出了用于完成某種確定目的的函數(shù)客税。至于這些函數(shù)怎么使用完全不用內(nèi)核關(guān)心。

系統(tǒng)調(diào)用

系統(tǒng)調(diào)用(在Linux種常稱作syscalls)通常通過函數(shù)進(jìn)行調(diào)用撕贞。它們通常都需要定義一個或者多個參數(shù)更耻,而且可能產(chǎn)生一些副作用,例如寫某個文件或向給定的指針拷貝數(shù)據(jù)等等捏膨。系統(tǒng)調(diào)用還會通過一個long類型的返回值來表示成功或者錯誤秧均。通常,用一個負(fù)的返回值來表示錯誤号涯。返回一個0值表示成功目胡。Unix系統(tǒng)調(diào)用在出現(xiàn)錯誤的時候,C庫會把錯誤碼寫入errno全局變量链快,通過調(diào)用perror()庫函數(shù)誉己,可以把變量翻譯成用戶可以理解的錯誤字符串。

當(dāng)然域蜗,系統(tǒng)調(diào)用最終具有一種明確的操作:例如getpid() 系統(tǒng)調(diào)用巨双,根據(jù)定義它會返回當(dāng)前進(jìn)程的PID,內(nèi)核中他的實(shí)現(xiàn)非常簡單:

/**
 * sys_getpid - return the thread group id of the current process
 *
 * Note, despite the name, this returns the tgid not the pid.  The tgid and
 * the pid are identical unless CLONE_THREAD was specified on clone() in
 * which case the tgid is the same in all threads of the same group.
 *
 * This is SMP safe as current->tgid does not change.
 */
SYSCALL_DEFINE0(getpid)
{
    return task_tgid_vnr(current);
}

SYSCALL_DEFINE0只是一個宏霉祸,它定義了一個無參數(shù)的系統(tǒng)調(diào)用(這里數(shù)字為0)筑累,展開后的代碼如下:

asmlinkage long sys_getpid(void);

首先,必須在聲明中使用asmlinkage限定詞脉执,這是一個編譯指令疼阔,通知編譯器僅從棧中提取該函數(shù)的參數(shù),所有的系統(tǒng)調(diào)用都需要這個詞半夷。

其次婆廊,函數(shù)返回值。為了保證32位和64位系統(tǒng)的兼容巫橄,系統(tǒng)調(diào)用在用戶空間和內(nèi)核空間有不同的返回值類型淘邻,用戶空間為int,內(nèi)核空間為long湘换。

最后宾舅,系統(tǒng)調(diào)用應(yīng)該被定義與sys_XX的形式。這是Linux種所有系統(tǒng)調(diào)用都應(yīng)該遵守的命名規(guī)則彩倚。
(1)系統(tǒng)調(diào)用號
1筹我、在Linux中,每個系統(tǒng)調(diào)用被賦予一個系統(tǒng)調(diào)用號帆离。通過這個系統(tǒng)調(diào)用號可以關(guān)聯(lián)系統(tǒng)調(diào)用蔬蕊。

2、系統(tǒng)調(diào)用號非常重要哥谷,一旦分配就不能再有任何變更岸夯,否則編譯好的應(yīng)用程序就會崩潰麻献。

3、如果一個系統(tǒng)調(diào)用被刪除猜扮,它所占用的系統(tǒng)調(diào)用號也不允許被回收利用勉吻,否則,以前編譯過的代碼會調(diào)用此系統(tǒng)調(diào)用旅赢,但事實(shí)上卻調(diào)用另一個系統(tǒng)調(diào)用齿桃。

4、Linux中有 一個“未實(shí)現(xiàn)”系統(tǒng)調(diào)用 sys_ni_syscall()煮盼,它除了返回-ENOSYS外不做任何事源譬,此錯誤號就是專門針對無效的系統(tǒng)調(diào)用而設(shè)的。

5孕似、內(nèi)核記錄了系統(tǒng)調(diào)用表中的所有已注冊過的系統(tǒng)調(diào)用的列表,存儲在sys_call_tabe中刮刑。

(2)系統(tǒng)調(diào)用性能
Linux系統(tǒng)調(diào)用比其他許多操作系統(tǒng)執(zhí)行的要快喉祭。

系統(tǒng)調(diào)用處理函數(shù)

1、應(yīng)用程序通知內(nèi)核的機(jī)制是靠軟中斷實(shí)現(xiàn)的:通過引發(fā)一個異常來促使系統(tǒng)切換到內(nèi)核態(tài)去執(zhí)行異常處理程序雷绢。此時的異常處理程序?qū)嶋H上就是系統(tǒng)調(diào)用處理程序泛烙。

2、指定恰當(dāng)?shù)南到y(tǒng)調(diào)用
1)翘紊、僅僅陷入內(nèi)核空間是不夠的蔽氨。必須把系統(tǒng)調(diào)用號一并傳給內(nèi)核。
2)帆疟、在X86上鹉究,系統(tǒng)調(diào)用號是通過eax寄存器傳遞給內(nèi)核的。在陷入內(nèi)核之前踪宠,用戶空間就把相應(yīng)系統(tǒng)調(diào)用所對應(yīng)的號放入eax中自赔。
3)、system_call函數(shù)通過將給定的系統(tǒng)調(diào)用號與NR_syscalls作比較來檢查其有效性柳琢。如果它大于或等于NR_syscalls绍妨,該函數(shù)就返回-ENOSYS。否則柬脸,就執(zhí)行相應(yīng)的系統(tǒng)調(diào)用他去。

call  *sys_call_table( , %rax,  8);

3、參數(shù)傳遞
1)倒堕、除了系統(tǒng)調(diào)用號之外灾测,大部分系統(tǒng)調(diào)用還需要一些外部參數(shù)的輸入。再發(fā)生陷入的時候涩馆,應(yīng)該把這些參數(shù)從用戶空間中傳給內(nèi)核行施。
2)允坚、用寄存器傳遞系統(tǒng)調(diào)用。在X86系統(tǒng)上蛾号,ebx稠项、ecx、edx鲜结、esi和edi按順序存放前五個參數(shù)展运。留個或留個以上參數(shù)不常見。此外精刷,應(yīng)該用一個單獨(dú)的寄存器存放指向所有這些參數(shù)在用戶空間地址的指針拗胜。

系統(tǒng)調(diào)用的實(shí)現(xiàn)

1)、實(shí)現(xiàn)一個新的系統(tǒng)調(diào)用的第一步是決定它的用途怒允,它要做些什么埂软?每個系統(tǒng)調(diào)用應(yīng)該有一個明確的用途,Linux中不提倡多用途的系統(tǒng)調(diào)用(一個系統(tǒng)調(diào)用通過傳遞不同的參數(shù)值來完成不同的工作)纫事,ioctl 就是一個典型的反例勘畔。

2)、系統(tǒng)調(diào)用必須仔細(xì)檢查它們所有的參數(shù)是否合法有效丽惶。最重要的一種檢查就是檢查用戶提供的指針是否有效炫七,在接收一個用戶空間的指針之前,內(nèi)核必須保證:
I钾唬、指針指向的內(nèi)存區(qū)域?qū)儆谟脩艨臻g万哪。
II、指針指向的內(nèi)存區(qū)域在進(jìn)程的地址空間里抡秆。
III奕巍、如果是讀,改內(nèi)核應(yīng)被標(biāo)記為可讀儒士;如果是寫伍绳,改內(nèi)核應(yīng)被標(biāo)記為可寫;如果是可執(zhí)行乍桂,改內(nèi)核應(yīng)被標(biāo)記為可執(zhí)行冲杀。
3)、內(nèi)核提供了兩個方法來完成必須的檢查和內(nèi)核空間與用戶空間之間數(shù)據(jù)的來回拷貝睹酌。
I权谁、copy_to_user();
II、copy_from_user();
III憋沿、如果執(zhí)行失敗旺芽,這兩個函數(shù)返回的都是沒能完成拷貝的數(shù)據(jù)的字節(jié)數(shù)。如果成功,則返回0.當(dāng)出現(xiàn)上述錯誤時采章,系統(tǒng)調(diào)用返回標(biāo)準(zhǔn)-EEAULT运嗜。
4)、檢查針對是否有合法權(quán)限悯舟。
系統(tǒng)允許檢查針對特定資源的特殊權(quán)限担租,調(diào)用者可以使用ns_capable()函數(shù)來檢查是否有權(quán)能對特定的資源進(jìn)行操作。
例如:下面reboot的系統(tǒng)調(diào)用抵怎,第一步是判斷是否具有CAP_SYS_BOOT的權(quán)能奋救?

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
        void __user *, arg)
{
    struct pid_namespace *pid_ns = task_active_pid_ns(current);
    char buffer[256];
    int ret = 0;

    /* We only trust the superuser with rebooting the system. */
    if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
        return -EPERM;

    /* For safety, we require "magic" arguments. */
    if (magic1 != LINUX_REBOOT_MAGIC1 ||
            (magic2 != LINUX_REBOOT_MAGIC2 &&
            magic2 != LINUX_REBOOT_MAGIC2A &&
            magic2 != LINUX_REBOOT_MAGIC2B &&
            magic2 != LINUX_REBOOT_MAGIC2C))
        return -EINVAL;

    /*
     * If pid namespaces are enabled and the current task is in a child
     * pid_namespace, the command is handled by reboot_pid_ns() which will
     * call do_exit().
     */
    ret = reboot_pid_ns(pid_ns, cmd);
    if (ret)
        return ret;

    /* Instead of trying to make the power_off code look like
     * halt when pm_power_off is not set do it the easy way.
     */
    if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
        cmd = LINUX_REBOOT_CMD_HALT;

    mutex_lock(&reboot_mutex);
    switch (cmd) {
    case LINUX_REBOOT_CMD_RESTART:
        kernel_restart(NULL);
        break;

    case LINUX_REBOOT_CMD_CAD_ON:
        C_A_D = 1;
        break;

    case LINUX_REBOOT_CMD_CAD_OFF:
        C_A_D = 0;
        break;

    case LINUX_REBOOT_CMD_HALT:
        kernel_halt();
        do_exit(0);
        panic("cannot halt");

    case LINUX_REBOOT_CMD_POWER_OFF:
        kernel_power_off();
        do_exit(0);
        break;

    case LINUX_REBOOT_CMD_RESTART2:
        ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
        if (ret < 0) {
            ret = -EFAULT;
            break;
        }
        buffer[sizeof(buffer) - 1] = '\0';

        kernel_restart(buffer);
        break;

#ifdef CONFIG_KEXEC
    case LINUX_REBOOT_CMD_KEXEC:
        ret = kernel_kexec();
        break;
#endif

#ifdef CONFIG_HIBERNATION
    case LINUX_REBOOT_CMD_SW_SUSPEND:
        ret = hibernate();
        break;
#endif

    default:
        ret = -EINVAL;
        break;
    }
    mutex_unlock(&reboot_mutex);
    return ret;
}
系統(tǒng)調(diào)用上下文

系統(tǒng)調(diào)用運(yùn)行在進(jìn)程上下文,所以可以休眠反惕,可以被搶占尝艘,所以要保證該系統(tǒng)調(diào)用時可重入的。
1姿染、綁定一個系統(tǒng)調(diào)用的最后步驟
1)背亥、把系統(tǒng)調(diào)用注冊成一個正式的系統(tǒng)調(diào)用:
I、首先悬赏,在系統(tǒng)調(diào)用表的最后加入一個表項(xiàng)隘梨。
II、對于所支持的各種體系結(jié)構(gòu)舷嗡,系統(tǒng)調(diào)用號都必須定義于<asm/unistd.h>中。
III嵌莉、系統(tǒng)調(diào)用必須被編譯進(jìn)內(nèi)核映像(不能編譯成模塊)进萄。

2、從用戶空間訪問系統(tǒng)調(diào)用
1)锐峭、用戶程序通過包含標(biāo)準(zhǔn)頭文件和C庫連接中鼠,就可以使用系統(tǒng)調(diào)用。
2)沿癞、Linux本身提供一個宏援雇,用于直接對系統(tǒng)調(diào)用進(jìn)行訪問。
以Android系統(tǒng)一個reboot系統(tǒng)調(diào)用為例椎扬,應(yīng)用程序調(diào)用reboot系統(tǒng)調(diào)用的方法如下:

ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);

系統(tǒng)調(diào)用號的宏定義:位于文件/bionic/libc/kernel/uapi/asm-arm/asm/unistd.h
其中:

#define __NR_reboot 142

匯編定義相關(guān)函數(shù)的中斷調(diào)用過程:syscall位于文件/bionic/libc/arch-arm64/bionic/syscall.S惫搏,內(nèi)容如下:

ENTRY(syscall)
    /* Move syscall No. from x0 to x8 */
    mov     x8, x0
    /* Move syscall parameters from x1 thru x6 to x0 thru x5 */
    mov     x0, x1
    mov     x1, x2
    mov     x2, x3
    mov     x3, x4
    mov     x4, x5
    mov     x5, x6
    svc     #0

    /* check if syscall returned successfully */
    cmn     x0, #(MAX_ERRNO + 1)
    cneg    x0, x0, hi
    b.hi    __set_errno_internal

    ret
END(syscall)

3、不通過系統(tǒng)調(diào)用的方式實(shí)現(xiàn)的原因蚕涤。
盡量不要自己添加系統(tǒng)調(diào)用筐赔,有很多其他方法可以替代:
1)實(shí)現(xiàn)一個設(shè)備節(jié)點(diǎn),對設(shè)備進(jìn)行read,write操作揖铜,使用ioctl對特定的設(shè)置進(jìn)行操作茴丰。
2)把增加的信息作為一個文件放在sysfs中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贿肩,隨后出現(xiàn)的幾起案子峦椰,更是在濱河造成了極大的恐慌,老刑警劉巖汰规,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汤功,死亡現(xiàn)場離奇詭異,居然都是意外死亡控轿,警方通過查閱死者的電腦和手機(jī)冤竹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茬射,“玉大人鹦蠕,你說我怎么就攤上這事≡谂祝” “怎么了钟病?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刚梭。 經(jīng)常有香客問我肠阱,道長,這世上最難降的妖魔是什么朴读? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任屹徘,我火速辦了婚禮,結(jié)果婚禮上衅金,老公的妹妹穿的比我還像新娘噪伊。我一直安慰自己,他們只是感情好氮唯,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布鉴吹。 她就那樣靜靜地躺著,像睡著了一般惩琉。 火紅的嫁衣襯著肌膚如雪豆励。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天瞒渠,我揣著相機(jī)與錄音良蒸,去河邊找鬼。 笑死伍玖,一個胖子當(dāng)著我的面吹牛诚啃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播私沮,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼始赎,長吁一口氣:“原來是場噩夢啊……” “哼和橙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起造垛,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤魔招,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后五辽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體办斑,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年杆逗,在試婚紗的時候發(fā)現(xiàn)自己被綠了乡翅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡罪郊,死狀恐怖蠕蚜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悔橄,我是刑警寧澤靶累,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站癣疟,受9級特大地震影響挣柬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睛挚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一邪蛔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扎狱,春花似錦侧到、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荣回。三九已至遭贸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間心软,已是汗流浹背壕吹。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留删铃,地道東北人耳贬。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像猎唁,于是被迫代替她去往敵國和親咒劲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 一、為什么會有系統(tǒng)調(diào)用腐魂? 用戶進(jìn)程(如瀏覽器)免不了要使用系統(tǒng)資源帐偎,如打開文件讀取內(nèi)容等操作,但是計(jì)算機(jī)的資源由操...
    開發(fā)者共讀閱讀 327評論 0 0
  • 計(jì)算機(jī)系統(tǒng)漫游 代碼從文本到可執(zhí)行文件的過程(c語言示例):預(yù)處理階段蛔屹,處理 #inlcude 削樊, #defin...
    willdimagine閱讀 3,594評論 0 5
  • 原來自己多年來一直使用的庫函數(shù)竟有如此復(fù)雜的機(jī)制。這個機(jī)制的設(shè)計(jì)者思考的如此深入兔毒,屏蔽了底層硬件的差異漫贞,也是費(fèi)勁心...
    athorn閱讀 1,139評論 0 1
  • 漫友支持擂红!每日一畫仪际! 《鐵拐戒之二》寬48高68厘米
    漫悟閱讀 261評論 0 4
  • 又是一個春暖花開的新年,和家人們踏著濃濃的年味向云南旅游目的地出發(fā)了昵骤,一切都是意外的驚喜树碱,只為記錄這美好的發(fā)...
    靜靜成長說閱讀 249評論 0 3