zoukankan      html  css  js  c++  java
  • 作业4:扒开系统调用的三层皮(上) 20135115臧文君

    扒开系统调用的三层皮(上

    注:作者:臧文君,原创作品转载请注明出处,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

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

    1、权限级别

    有权限级别的划分?

    当系统中所有程序员编写的代码都可以使用特权指令,系统很容易崩溃。

    也是让系统更稳定的机制。

    2、在Linux代码中如何区分用户态和内核态?

    判断cs:eip的值:

    在内核态中,cs:eip可以是任意的值,在32位的x86系统中有4G的地址空间。

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

    例:硬件中断,系统调用(是一种特殊的中断)。

    4、从用户态切换到内核态时:必须保存用户态的寄存器上下文!

     

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

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

    恢复现场就是退出中断程序,恢复保存寄存器的数据。

    iret指令与中断信号(包括int指令)发生时的CPU做的动作正好相反。

    5、中断处理的完整过程

    int0x80:指系统调用

    保存:将当前的cs:eip、ss:esp、eflags保存到内核堆栈中。

    加载:把当前的中断信号或系统调用相关联的中断处理程序的入口加载到cs:eip中,同时将当前的堆栈段ss和esp也加载到CPU中。

    二、系统调用概述

    1、系统调用的意义

    2、操作系统提供的API和系统调用的关系

    3、应用程序、封装例程、系统调用处理程序及系统调用服务例程之间的关系

    系统调用的三层皮:API,中断向量对应的中断服务程序,系统调用服务程序。

    例:xyz、system_call和sys_xyz。

    4、系统调用程序及服务例程

    中断向量0x80与system_call绑定起来。

    系统调用号将xyz和sys_xyz关联起来了。

    5、系统调用的参数传递方法

    注:函数调用传递参数使用的是压栈的方式。

     

    若超过6个,就将某一个寄存器作为指针,指向一块内存,在进入内核态之后,可以访问所有的地址空间,通过内存来传递参数。

    三、使用库函数API和C代码中嵌入汇编代码触发同一个系统调用

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

     1 #include<stdio.h>
     2 #include<time.h>
     3 int main(){
     4     time_t tt;
     5     struct tm *t;
     6     tt = time(NULL);
     7     t = localtime(&tt);
     8     printf(“time:%d:%d:%d:%d:%d:%d\n”,t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
     9     return 0;
    10 }

    命令:

    编写time程序:vi time.c

    编译:gcc time.c -o time -m32

    执行:./time

    2、C代码中嵌入汇编代码的写法

     1 #include <stdio.h>
     2 #include <time.h>
     3 int main(){
     4     time_t tt;
     5     struct tm *t;
     6     asm volatile(
     7     "mov $0,%%ebx\n\t"
     8     "mov $0xd,%%eax\n\t"  //使用eax传递系统调用号,这里time是13
     9     "int $0x80\n\t"
    10     "mov %%eax,%0\n\t"  //系统调用的返回值使用eax存储,和普通函数一样
    11     : "=m" (tt)
    12     );
    13     t = localtime(&tt);
    14     printf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900,t->tm_mon+1, t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
    15     return 0;
    16 }

    命令:

    vi time-asm.c

    gcc time-asm.c -o time-asm -m32

    ./time-asm

    实验:

    1、库函数API中的break中断函数

    代码如下:

     1 #include <stdio.h>
     2 
     3 int main(){
     4 
     5     int i,a;
     6 
     7     for(i=0;i<100;i++){
     8 
     9         i=4*i+3;
    10 
    11         if(i>100){
    12 
    13             break;
    14 
    15         }
    16 
    17     }
    18 
    19     printf(“i=%d > 100\n”,i);
    20 
    21     return 0;
    22 
    23 }

    截图:

    2、在test.c代码中嵌入汇编代码,break的系统调用号为17(0x11)

    代码如下:

     1 #include <stdio.h>
     2 
     3 int main(){
     4 
     5     int i,a;
     6 
     7     for(i=0;i<100;i++){
     8 
     9         i=4*i+3;
    10 
    11         if(i>100){
    12 
    13             asm volatile(
    14 
    15             "mov $0,%%ebx\n\t"
    16 
    17             "mov $0x11,%%eax\n\t"  //使用eax传递系统调用号,这里break是17
    18 
    19             "int $0x80\n\t"
    20 
    21             "mov %%eax,%0\n\t"  //系统调用的返回值使用eax存储,和普通函数一样
    22 
    23             : "=m" (a)
    24 
    25             );
    26     
    27         }
    28 
    29     }
    30 
    31     printf(“i=%d > 100\n”,i);
    32 
    33     return 0;
    34 
    35 }        

    截图:

    总结:

          这次课主要学习的是系统调用,我认为系统调用的工作机制是:当用户态进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数,由API、中断向量和中断处理程序协调完成。

          而在系统调用的过程中,系统调用号使用eax寄存器传递参数。寄存器在传递参数时也有如下限制:

    1)每个参数的长度不能超过寄存器的长度,即32位。

    2)在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp),若超过6个,就将某一个寄存器作为指针,指向一块内存,在进入内核态之后,可以访问所有的地址空间,通过内存来传递参数。

  • 相关阅读:
    【YBTOJ】守卫挑战
    【YBTOJ】【Luogu P6089】[JSOI2015]非诚勿扰
    【Luogu P4092】[HEOI2016/TJOI2016]树
    【YBTOJ】【Luogu P3232】[HNOI2013]游走
    【CodeForces 396B】On Sum of Fractions
    【P2579】【ZJTSC05】沼泽鳄鱼
    【YBTOJ】【USACO03MAR】最大均值
    【YBTOJ】防具布置
    VC 静态库与动态库(二)静态库创建与使用
    VC 静态库与动态库(一)介绍
  • 原文地址:https://www.cnblogs.com/CatherineZang/p/5281675.html
Copyright © 2011-2022 走看看