操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    在現(xiàn)代操做系統(tǒng)里,因?yàn)橄到y(tǒng)資源可能同時(shí)被多個(gè)應(yīng)用程序訪問,若是不加保護(hù),那各個(gè)應(yīng)用程序之間可能會(huì)產(chǎn)生沖突,對(duì)于惡意應(yīng)用程序更可能致使系統(tǒng)奔潰。這里所說的系統(tǒng)資源包括文件、網(wǎng)絡(luò)、各類硬件設(shè)備等。好比要操做文件必須借助操做系統(tǒng)提供的api(好比linux下的fopen)。html

    系統(tǒng)調(diào)用在咱們工做中無時(shí)無刻不打著交道,那系統(tǒng)調(diào)用的原理是什么呢?在其過程當(dāng)中作了哪些事情呢?linux

    本文將闡述系統(tǒng)調(diào)用原理,讓你們對(duì)于系統(tǒng)調(diào)用有一個(gè)清晰的認(rèn)識(shí)。c++

    更多文章見我的博客:/…git

    概述

    現(xiàn)代cpu一般有多種特權(quán)級(jí)別,通常來講特權(quán)級(jí)總共有4個(gè),編號(hào)從Ring 0(最高特權(quán))到Ring 3(最低特權(quán)),在Linux上之用到Ring 0和RIng 3,用戶態(tài)對(duì)應(yīng)Ring 3,內(nèi)核態(tài)對(duì)應(yīng)Ring 0。程序員

    普通應(yīng)用程序運(yùn)行在用戶態(tài)下,其諸多操做都受到限制,好比改變特權(quán)級(jí)別、訪問硬件等。特權(quán)高的代碼能將本身降至低等級(jí)的級(jí)別,但反之則是不行的。而系統(tǒng)調(diào)用是運(yùn)行在內(nèi)核態(tài)的有哪些是系統(tǒng)進(jìn)程調(diào)用,那么運(yùn)行在用戶態(tài)的應(yīng)用程序如何運(yùn)行內(nèi)核態(tài)的代碼呢?操做系統(tǒng)通常是經(jīng)過中斷來從用戶態(tài)切換到內(nèi)核態(tài)的。學(xué)過操做系統(tǒng)課程的同窗對(duì)中斷這個(gè)詞確定都不陌生。

    中斷通常有兩個(gè)屬性,一個(gè)是中斷號(hào),一個(gè)是中斷處理程序。不一樣的中斷有不一樣的中斷號(hào),每一個(gè)中斷號(hào)都對(duì)應(yīng)了一個(gè)中斷處理程序。在內(nèi)核中有一個(gè)叫中斷向量表的數(shù)組來映射這個(gè)關(guān)系。當(dāng)中斷到來時(shí),cpu會(huì)暫停正在執(zhí)行的代碼,根據(jù)中斷號(hào)去中斷向量表找出對(duì)應(yīng)的中斷處理程序并調(diào)用。中斷處理程序執(zhí)行完成后,會(huì)繼續(xù)執(zhí)行以前的代碼。api

    中斷分為硬件中斷和軟件中斷,咱們這里說的是軟件中斷,軟件中斷一般是一條指令,使用這條指令用戶能夠手動(dòng)觸發(fā)某個(gè)中斷。例如在i386下,對(duì)應(yīng)的指令是int,在int指令后指定對(duì)應(yīng)的中斷號(hào),如int 0x80表明你調(diào)用第0x80號(hào)的中斷處理程序。數(shù)組

    中斷號(hào)是有限的,全部不會(huì)用一個(gè)中斷來對(duì)應(yīng)一個(gè)系統(tǒng)調(diào)用(系統(tǒng)調(diào)用有不少)。Linux下用int 0x80觸發(fā)全部的系統(tǒng)調(diào)用,那如何區(qū)分不一樣的調(diào)用呢?對(duì)于每一個(gè)系統(tǒng)調(diào)用都有一個(gè)系統(tǒng)調(diào)用號(hào),在觸發(fā)中斷以前,會(huì)將系統(tǒng)調(diào)用號(hào)放入到一個(gè)固定的寄存器,0x80對(duì)應(yīng)的中斷處理程序會(huì)讀取該寄存器的值,而后決定執(zhí)行哪一個(gè)系統(tǒng)調(diào)用的代碼。bash

    在.5(具體版本不是很肯定)以前的版本,是使用int 0x80這樣的方式實(shí)現(xiàn)系統(tǒng)調(diào)用的,但其實(shí)int指令這樣的形式性能不太好,緣由以下(出自這篇文章):網(wǎng)絡(luò)

    在 x86 保護(hù)模式中,處理 INT 中斷指令時(shí),CPU 首先從中斷描述表 IDT 取出對(duì)應(yīng)的門描述符,判斷門描述符的種類,而后檢查門描述符的級(jí)別 DPL 和 INT 指令調(diào)用者的級(jí)別 CPL,當(dāng) CPL<=DPL 也就是說 INT 調(diào)用者級(jí)別高于描述符指定級(jí)別時(shí),才能成功調(diào)用,最后再根據(jù)描述符的內(nèi)容,進(jìn)行壓棧、跳轉(zhuǎn)、權(quán)限級(jí)別提高。內(nèi)核代碼執(zhí)行完畢以后,調(diào)用 IRET 指令返回,IRET 指令恢復(fù)用戶棧,并跳轉(zhuǎn)會(huì)低級(jí)別的代碼。
    其實(shí),在發(fā)生系統(tǒng)調(diào)用,由 Ring3 進(jìn)入 Ring0 的這個(gè)過程浪費(fèi)了很多的 CPU 周期,例如,系統(tǒng)調(diào)用必然須要由 Ring3 進(jìn)入 Ring0(由內(nèi)核調(diào)用 INT 指令的方式除外,這多半屬于 Hacker 的內(nèi)核模塊所為),權(quán)限提高以前和以后的級(jí)別是固定的,CPL 確定是 3,而 INT 80 的 DPL 確定也是 3,這樣 CPU 檢查門描述符的 DPL 和調(diào)用者的 CPL 就是徹底不必。
    復(fù)制代碼

    正是因?yàn)槿绱?,?5開始支持一種新的系統(tǒng)調(diào)用,其基于Intel 奔騰2代處理器就開始支持的一組專門針對(duì)系統(tǒng)調(diào)用的指令/。 指令用于由 Ring3 進(jìn)入 Ring0,指令用于由 Ring0 返回 Ring3。因?yàn)闆]有特權(quán)級(jí)別檢查的處理,也沒有壓棧的操做,因此執(zhí)行速度比 INT n/IRET 快了很多。

    本文分析的是int指令,新型的系統(tǒng)調(diào)用機(jī)制能夠參見下面幾篇文章:

    基于int的系統(tǒng)調(diào)用 觸發(fā)中斷

    咱們以系統(tǒng)調(diào)用fork為例,fork函數(shù)的定義在glibc(2.17版本)的.h

    /* Clone the calling process, creating an exact copy. Return -1 for errors, 0 to the new process, and the process ID of the new process to the old process. */
    extern __pid_t fork (void) __THROWNL;
    復(fù)制代碼

    fork函數(shù)的實(shí)現(xiàn)代碼比較難找,在nptl\\unix\sysv\linux\fork.c中有這么一段代碼

    weak_alias (__libc_fork, __fork)
    libc_hidden_def (__fork)
    weak_alias (__libc_fork, fork)
    復(fù)制代碼

    java調(diào)用c#進(jìn)程_面注冊(cè)調(diào)用大漠插件有進(jìn)程嗎_有哪些是系統(tǒng)進(jìn)程調(diào)用

    其做用簡(jiǎn)單的說就是將看成的別名,因此fork函數(shù)的實(shí)現(xiàn)是在中,核心代碼以下

    #ifdef ARCH_FORK
      pid = ARCH_FORK ();
    #else
    # error "ARCH_FORK must be defined so that the CLONE_SETTID flag is used"
      pid = INLINE_SYSCALL (fork, 0);
    #endif
    復(fù)制代碼

    咱們分析定義了的狀況,定義在nptl\\unix\sysv\linux\i386\fork.c中,代碼以下:

    #define ARCH_FORK() \ INLINE_SYSCALL (clone, 5, \ CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0, \ NULL, NULL, &THREAD_SELF->tid)
    復(fù)制代碼

    代碼在\unix\sysv\linux\i386\.h

    #undef INLINE_SYSCALL
    #define INLINE_SYSCALL(name, nr, args...) \ ({ \ unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args); \ if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (resultvar, ), 0)) \ { \ __set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, )); \ resultvar = 0xffffffff; \ } \ (int) resultvar; })
    復(fù)制代碼

    主要是調(diào)用同文件下的

    # define INTERNAL_SYSCALL(name, err, nr, args...) \ ({ \ register unsigned int resultvar; \ EXTRAVAR_##nr \ asm volatile ( \ LOADARGS_##nr \ "movl %1, %%eax\n\t" \ "int $0x80\n\t" \ RESTOREARGS_##nr \ : "=a" (resultvar) \ : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc"); \ (int) resultvar; })
    復(fù)制代碼

    #define __NR_clone 120
    復(fù)制代碼

    這里是一段內(nèi)聯(lián)匯編代碼, 其中__NR_##name的值為 即120。這里主要是兩個(gè)步驟:

    設(shè)置eax寄存器的值為120 執(zhí)行int $0x80陷入中斷

    int $0x80指令會(huì)讓cpu陷入中斷,執(zhí)行對(duì)應(yīng)的0x80中斷處理函數(shù)。不過在這以前,cpu還須要進(jìn)行棧切換。

    由于在linux中,用戶態(tài)和內(nèi)核態(tài)使用的是不一樣的棧(能夠看看這篇文章),二者負(fù)責(zé)各自的函數(shù)調(diào)用,互不干擾。在執(zhí)行int $0x80時(shí),程序須要由用戶態(tài)切換到內(nèi)核態(tài),因此程序當(dāng)前棧也要從用戶棧切換到內(nèi)核棧。與之對(duì)應(yīng),當(dāng)中斷程序執(zhí)行結(jié)束返回時(shí),當(dāng)前棧要從內(nèi)核棧切換回用戶棧。

    這里說的當(dāng)前棧指的就是ESP寄存器的值所指向的棧。ESP的值位于用戶棧的范圍,那程序的當(dāng)前棧就是用戶棧,反之亦然。此外寄存器SS的值指向當(dāng)前棧所在的頁(yè)。所以,將用戶棧切換到內(nèi)核棧的過程是:

    將當(dāng)前ESP、SS等寄存器的值存到內(nèi)核棧上。 將ESP、SS等值設(shè)置為內(nèi)核棧的相應(yīng)值。

    反之,從內(nèi)核棧切換回用戶棧的過程:恢復(fù)ESP、SS等寄存器的值,也就是用保存在內(nèi)核棧的原ESP、SS等值設(shè)置回對(duì)應(yīng)寄存器。

    面注冊(cè)調(diào)用大漠插件有進(jìn)程嗎_有哪些是系統(tǒng)進(jìn)程調(diào)用_java調(diào)用c#進(jìn)程

    中斷處理程序

    在切換到內(nèi)核棧以后,就開始執(zhí)行中斷向量表的0x80號(hào)中斷處理程序。中斷處理程序除了系統(tǒng)調(diào)用(0x80)還有如除0異常(0x00)、缺頁(yè)異常(0x14)等等,在arch\i386\\traps.c文件的方法中描述了中斷處理程序向中斷向量表注冊(cè)的過程:

    void __init trap_init(void) {
    #ifdef CONFIG_EISA
    	void __iomem *p = ioremap(0x0FFFD9, 4);
    	if (readl(p) == 'E'+('I'<<8)+('S'<<16)+('A'<<24)) {
    		EISA_bus = 1;
    	}
    	iounmap(p);
    #endif
    #ifdef CONFIG_X86_LOCAL_APIC
    	init_apic_mappings();
    #endif
    	set_trap_gate(0,÷_error);
    	set_intr_gate(1,&debug);
    	set_intr_gate(2,&nmi);
    	set_system_intr_gate(3, &int3); /* int3-5 can be called from all */
    	set_system_gate(4,&overflow);
    	set_system_gate(5,&bounds);
    	set_trap_gate(6,&invalid_op);
    	set_trap_gate(7,&device_not_available);
    	set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
    	set_trap_gate(9,&coprocessor_segment_overrun);
    	set_trap_gate(10,&invalid_TSS);
    	set_trap_gate(11,&segment_not_present);
    	set_trap_gate(12,&stack_segment);
    	set_trap_gate(13,&general_protection);
    

    有哪些是系統(tǒng)進(jìn)程調(diào)用_面注冊(cè)調(diào)用大漠插件有進(jìn)程嗎_java調(diào)用c#進(jìn)程

    set_intr_gate(14,&page_fault); set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); #ifdef CONFIG_X86_MCE set_trap_gate(18,&machine_check); #endif set_trap_gate(19,&simd_coprocessor_error); set_system_gate(SYSCALL_VECTOR,&system_call); /* * Should be a barrier for any external CPU state. */ cpu_init(); trap_init_hook(); } 復(fù)制代碼

    定義以下:

    #define SYSCALL_VECTOR		0x80
    復(fù)制代碼

    因此0x80對(duì)應(yīng)的處理程序就是這個(gè)方法,該方法位于arch\i386\\entry.S

    ENTRY(system_call)
    	//code 1: 保存各類寄存器
    	SAVE_ALL
    	...
    	jnz syscall_trace_entry
    	//若是傳入的系統(tǒng)調(diào)用號(hào)大于最大的系統(tǒng)調(diào)用號(hào),則跳轉(zhuǎn)到無效調(diào)用處理
    	cmpl $(nr_syscalls), %eax
    	jae syscall_badsys
    

    有哪些是系統(tǒng)進(jìn)程調(diào)用_java調(diào)用c#進(jìn)程_面注冊(cè)調(diào)用大漠插件有進(jìn)程嗎

    syscall_call: //code 2: 根據(jù)系統(tǒng)調(diào)用號(hào)(存儲(chǔ)在eax中)來調(diào)用對(duì)應(yīng)的系統(tǒng)調(diào)用程序 call *sys_call_table(,%eax,4) //保存系統(tǒng)調(diào)用返回值到eax寄存器中 movl %eax,EAX(%esp) # store the return value ... restore_all: //code 3:恢復(fù)各類寄存器的值 以及執(zhí)行iret指令 RESTORE_ALL ... 復(fù)制代碼

    主要分為幾步:

    1.保存各類寄存器

    2.根據(jù)系統(tǒng)調(diào)用號(hào)執(zhí)行對(duì)應(yīng)的系統(tǒng)調(diào)用程序,將返回結(jié)果存入到eax中

    3.恢復(fù)各類寄存器

    其中保存各類寄存器的定義在entry.S中:

    #define SAVE_ALL \ cld; \ pushl %es; \ pushl %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ pushl %esi; \ pushl %edx; \ pushl %ecx; \ pushl %ebx; \ movl $(__USER_DS), %edx; \ movl %edx, %ds; \ movl %edx, %es;
    復(fù)制代碼

    定義在entry.S中:

    .data
    ENTRY(sys_call_table)
    	.long sys_restart_syscall	/* 0 - old "setup()" system call, used for restarting */
    	.long sys_exit
    	.long sys_fork
    	.long sys_read
    	.long sys_write
    	.long sys_open		/* 5 */
    

    java調(diào)用c#進(jìn)程_面注冊(cè)調(diào)用大漠插件有進(jìn)程嗎_有哪些是系統(tǒng)進(jìn)程調(diào)用

    ... .long sys_sigreturn .long sys_clone /* 120 */ ... 復(fù)制代碼

    就是系統(tǒng)調(diào)用表有哪些是系統(tǒng)進(jìn)程調(diào)用,每個(gè)long元素(4字節(jié))都是一個(gè)系統(tǒng)調(diào)用地址,因此 *(,%eax,4)的含義就是上偏移量為0+%eax*4元素所指向的系統(tǒng)調(diào)用,即第%eax個(gè)系統(tǒng)調(diào)用。上文中fork系統(tǒng)調(diào)用最終設(shè)置到eax的值是120,那最終執(zhí)行的就是這個(gè)函數(shù),注意其實(shí)現(xiàn)和第2個(gè)系統(tǒng)調(diào)用基本同樣,只是參數(shù)不一樣,關(guān)于fork和clone的區(qū)別能夠看這里,代碼以下:

    //kernel\fork.c
    asmlinkage int sys_fork(struct pt_regs regs) {
    	return do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL);
    }
    asmlinkage int sys_clone(struct pt_regs regs) {
    	unsigned long clone_flags;
    	unsigned long newsp;
    	int __user *parent_tidptr, *child_tidptr;
    	clone_flags = regs.ebx;
    	newsp = regs.ecx;
    	parent_tidptr = (int __user *)regs.edx;
    	child_tidptr = (int __user *)regs.edi;
    	if (!newsp)
    		newsp = regs.esp;
    	return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr);
    }
    復(fù)制代碼

    一次系統(tǒng)調(diào)用的基本過程已經(jīng)分析完,剩下的具體處理邏輯和本文無關(guān)就不分析了,有興趣的同窗能夠本身看看。

    總體調(diào)用流程圖以下:

    End

    想寫這篇文章的緣由主要是年前在看《《程序員的自我修養(yǎng)》》這本書,以前對(duì)于系統(tǒng)調(diào)用這塊有一些了解但很零碎和模糊,看完本書系統(tǒng)調(diào)用這一章后消除了我許多疑問。整體來講這是一本不錯(cuò)的書,但我相關(guān)的基礎(chǔ)比較薄弱,因此收獲很少。

網(wǎng)站首頁(yè)   |    關(guān)于我們   |    公司新聞   |    產(chǎn)品方案   |    用戶案例   |    售后服務(wù)   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區(qū)    電話:010-     郵箱:@126.com

備案號(hào):冀ICP備2024067069號(hào)-3 北京科技有限公司版權(quán)所有