• 系统调用上


    1.用户态、内核态和中断处理过程

    1.1 用户态和内核态简介

    一般现代CPU都有几种不同的指令执行级别。在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态;而在相应的低级别执行状态下,代码的掌控范围会受到限制,只能在对应级别允许的范围内活动,这种CPU执行级别就对应着用户态

    例如:intel*86CPU有4种不同的执行级别0,1,2,3,Linux只使用了其中的0级和3级分别表示内核态和用户态。

    1.2 区分用户态和内核态

    显著的区分方法是看cs:eip,在内核态时,cs:eip可以是任意的值。

    一般来讲在linux中,地址空间(逻辑地址)是一个显著的标志:0xc0000000以上的地址空间只能在内核态下访问,0x00000000——0xbfffffff的地址空间可在两种状态下访问。

    1.3 中断处理

    1.3.1 用户态进入内核态

    用户态进入内核态的方式:中断、调用系统调用。

    中断处理是从用户态进入内核态主要的方式,系统调用是一种特殊的中断。

    1.3.2 寄存器的上下切换

    用户态进入内核态时会发生寄存器的上下切换,要保存用户态的寄存器上下文。

    中断/int指令会在堆栈上保存一些寄存器的值,如用户态栈顶地址、当前的状态字、当时的es:eip的值等。

    中断发生后的第一件事就是保存现场(进入中断程序,保存需要用到的寄存器的数据),中断处理程序结束前最后一件事就是恢复现场(退出中断程序,恢复、保存寄存器的数据)。

    1.3.2 中断处理的完整过程

    //系统调用,保存cs:eip,当前堆栈段、栈顶、标志寄存器到内核堆栈,同时加载中断信号或系统调用,相关联的中断服务例程、ss:eip

    interrupt(ex:int 0x80)-save cs:eip/ss:esp/eflags(current)to kernel stack,then load cs:eip(entry of specific ISR)and ss:eip(point to kernel stack).

    //完成上述步骤之后,当前CPU在执行下一条指令,就在执行中断处理程序的入口,对堆栈的操作不再是用户态的堆栈,开始操作内核态堆栈

    save_all

    //内核代码,完成中断服务,发生进程调度。如果完成中断服务,没有发生进程调度,就会返回到原来的状态;如果发生进程调度,当前的状态

    //就会暂时保存

    restore_all

    iret-pop cs:eip/ss:eip/eflags from kernel stack

    2.系统调用概述

    2.1系统调用的意义

    系统调用:操作系统为用户态进程与硬件设备进行交互提供了一组接口。

    把用户从底层的硬件编程中解放出来;极大的提高了系统安全性;使用户程序具有可移植性。

    2.2应用程序编程接口(application program inteface,API)

    API和系统调用

    • API只是一个函数定义;
    • 系统调用通过软中断向内核发出一个明确的请求;
    • 不是每个API对应一个特定的系统调用。如API可能直接提供用户态的服务(如,一些数学函数);一个单独的API可能调用几个系统调用;不同的API可能调用了同一个系统调用。
    • 返回值。大部分封装例程返回一个整数,其含义依赖与相应的系统调用;-1在多数情况下表示内核不能满足进程的请求。

    图解:

    image

    函数xyz()是系统调用对应的API,API中封装了系统调用,会触发一个int 0x80的中断,0x80这个中断向量对应着system_call这个内核代码的入口起点。

    2.3获取当前系统时间

    #include <stdio.h>
    #include <time.h>
    
    int main(){
        time_t tt;
        struct tm *t;
        //tt=time(NULL);
        asm volatile(
            "mov $0,%%ebx
    	"
            "mov $0xd,%%eax
    	"
            "int $0x80
    	"
            "mov %%eax,%0
    	"
            : "=m" (tt)
        );
        t=localtime(&tt);
        printf("%d,%d,%d",t->tm_year+1900,t->tm_mon,t->tm_wday);
        return 0;
    }

    分析嵌入式汇编代码:

    首先ebx清0(系统调用传递的第一个参数使用ebx,这里是NULL),然后0xd放在eax中(eax是传递系统调用号,这里time是0xd(13)),

    返回值通过eax寄存器返回,eax放在%0即tt这个变量。

  • 相关阅读:
    【3】网站搭建:分页功能
    mapserv和mapserv.exe的区别
    WMS请求GetCapabilities,变成下载mapserv.exe解决办法
    get和post的区别
    实现ajax异步请求
    Thinkphp3.2 Widget的扩展
    Thinkphp3.2 路由是使用
    Thinkphp3.2 TagLib的使用
    Thinkphp下实现Rbac
    Thinkphp下实现分页
  • 原文地址:https://www.cnblogs.com/boyiliushui/p/5473601.html
Copyright © 2020-2023  润新知