美文网首页
4系统调用的工作机制

4系统调用的工作机制

作者: 夏天的篮球 | 来源:发表于2017-03-19 22:13 被阅读0次

    安大大 + 原创作品转载请注明出处 + 《Linux操作系统分析》MOOC课程

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

    程序员通过库函数的方式和系统调用打交道,库函数把系统调用给封装起来了。
    一般现代CPU都有几种不同的指令执行级别
    ♦ 在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态
    ♦ 而在相应的低级别执行状态下,代码的掌控范围会受到限制。只能在对应级别允许的范围内活动
    ♦ 举例:intel x86 CPU有四种不同的执行级别0-3,Linux只使用了其中的0级和3级分别来表示内核态和用户态


    为什么有权限级别的划分

    防止程序员非法访问系统或者是其它资源而使得系统崩溃

    Linux中怎么区分用户态和内核态:

    ♦ cs寄存器的最低两位表明了当前代码的特权级
    ♦ CPU每条指令的读取都是通过cs:eip这两个寄存器:
    其中cs是代码段选择寄存器,eip是偏移量寄存器。
    ♦ 上述判断由硬件完成
    ♦ 一般来说在Linux中,地址空间是一个显著的标志:
    0xc0000000以上的地址空间只能在内核态下访问,0x00000000-0xbfffffff的地址空间在两种状态下都可以访问
    注意:这里所说的地址空间是逻辑地址而不是物理地址

    在内核态时,cs和eip可以是任意的地址


    中断处理是从用户态进入内核态主要的方式

    用户态进入内核态一般来说都是用中断来触发的,可能是硬件中断。也可能是用户态程序运行当中调用了系统调用进入了内核态(trap)。系统调用是一种特殊的中断。

    ♦ 寄存器上下文
    – 从用户态切换到内核态时
    • 必须保存用户态的寄存器上下文,同时内核态相应的值放到CPU中
    • 要保存哪些?
    • 保存在哪里?
    ♦ 中断/int指令会在堆栈上保存一些寄存器的值
    – 如:用户态栈顶地址、当时的状态字、当时的cs:eip的值

    中断发生后第一件事就是保存现场 SAVE_ALL

    保护现场 就是进入中断程序,保存需要用到的寄存器的数据
    恢复现场 就是退出中断程序,恢复保存寄存器的数据

    中断处理结束前最后一件事是恢复现场 RESTORE_ALL
    中断处理的完整过程

    系统调用概述

    以系统调用为例,看看中断服务具体是怎么执行的:

    系统调用的意义

    • 操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用
    • 把用户从底层的硬件编程中解放出来
    • 极大的提高了系统的安全性
    • 使用户程序具有可移植性
    操作系统提供的API和系统调用的关系

    应用编程接口(application program interface, API) 和系统调用是不同的

    • API只是一个函数定义
    • 系统调用通过软中断(trap)向内核发出一个明确的请求
      Libc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用)
    • 一般每个系统调用对应一个封装例程
    • 库再用这些封装例程定义出给用户的API
      不是每个API都对应一个特定的系统调用。
    • API可能直接提供用户态的服务,如一些数学函数
    • 一个单独的API可能调用几个系统调用
    • 不同的API可能调用了同一个系统调用
      返回值
    • 大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用
    • -1在多数情况下表示内核不能满足进程的请求
    • Libc中定义的errno变量(error number)包含特定的出错码
      应用程序、封装例程、系统调用处理程序及系统调用服务例程之间的关系

    左边是用户态User Mode,右边是内核态Kernel Mode,最左边api:xyz()封装了一个系统调用,这个系统调用会触发一个0x80的中断。0x80这个中断向量就对应着system_call这个内核代码的入口起点。这个内核代码里可能有SAVE_ALL,sys_xyz()中断服务程序,在中断服务程序执行完后,可能ret_from_sys_call,在return的过程中可能发生进程调度,这是一个进程调度的时机。如果没有发生系统调度,就会iret,再返回到用户态接着执行。
    系统调用的三层皮:xyz(api)、system_call(中断向量)和sys_xyz


    当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。
    • 在Linux中是通过执行int $0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常
      系统调用号讲xyz和sys_xyz关联起来了

    传参:

    内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数
    -使用eax寄存器

    系统调用的参数传递方法

    系统调用也需要输入输出参数,例如:
    • 实际的值
    • 用户态进程地址空间的变量的地址
    • 甚至是包含指向用户态函数的指针的数据结构的地址

    如果是函数调用的时候,它可以把函数压栈的方式来传递。而用户态到内核态的函数传递的方法:

    system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号
    • 一个应用程序调用fork()封装例程,那么在执行int $0x80之前就把eax寄存器的值置为2(即__NR_fork)。
    • 这个寄存器的设置是libc库中的封装例程进行的,因此用户一般不关心系统调用号
    • 进入sys_call之后,立即将eax的值压入内核堆栈

    •寄存器传递参数具有如下限制:
    •1)每个参数的长度不能超过寄存器的长度,即32位
    •2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)
    •超过6个怎么办? 如果超过六个,某一个寄存器作为一个指针指向一块内存,进入到内核态以后,它可以访问所有的内存空间,可以通过这块内存来传递数据。


    使用库函数API来获取系统当前时间

    简单的系统调用time,来获取当前系统的时间:

    #include <stdio.h>
    #include <time.h>
    
    int main()
    {
        time_t tt;// tt只是int型的数值 
        struct tm *t;// tm为了输出的时候变成可读的 
        tt = time(NULL);//time系统调用,返回值赋给tt//使用了time这个库函数,api 
        t = localtime(&tt);//把tt改成t这种格式的,即tm格式
        printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); 
        return 0;   
    } 
    
    Paste_Image.png

    使用库函数的方式比较简单,下边使用汇编的方式:

    用汇编方式触发系统调用获取系统当前时间

    #include <stdio.h>
    #include <time.h>
    
    int main()
    {
        time_t tt;
        struct tm *t;
        asm volatile(
            "mov $0,%%ebx\n\t"//把ebx清零 
            "mov $0xd,%%eax\n\t"//把0xd(13)放到eax里,eax是用来传递系统调用号的 
            "int $0x80\n\t"
            "mov %%eax,%0\n\t"//通过eax返回值 
            :"=m"(tt) 
        ); 
        t = localtime(&tt);
        printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900, t->tm_mon, t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); 
        return 0;   
    } 
    


    可以看到执行的效果是完全一样的。用户态进程向内核态传递了一个系统调用号。在那段汇编代码里,先是给ebx传参数,然后给eax传系统调用号,int指令,系统调用执行完后返回结果eax,这就完成了系统调用。

    系统调用号的定义在 /usr/include/asm/unistd.h 文件中

    相关文章

      网友评论

          本文标题:4系统调用的工作机制

          本文链接:https://www.haomeiwen.com/subject/ubbvnttx.html