• 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用


    使用库函数APIC代码中嵌入汇编代码两种方式使用同一个系统调用

    符钰婧 原创作品转载请注明出处 

     《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”

     此实验使用了122号系统调用uname来获取当前UNIX系统的名称、版本和主机等信息。

    一、实验过程

    1、使用库函数API进行调用的代码【namel.c】如下:

    #include <sys/utsname.h>
    #include <stdio.h>
    #include <stdlib.h>

    int main()
    {
        struct utsname testbuff;
        int fb=0;

        fb=uname(&testbuff);
        if(fb<0)
        {
            perror("uname");
            return 0;
        }

        else

         {
            printf("sysname:%s  nodename:%s  release:%s  version:%s  machine:%s  ",testbuff.sysname,testbuff.nodename,testbuff.release,testbuff.version,testbuff.machine);

    #if _UTSNAME_DOMAIN_LENGTH - 0
    # ifdef __USE_GNU
        printf(" domainame:%s  ",testbuff.domainname);
    # else
        printf(" __domainame:%s  ",testbuff.__domainname);
    # endif
    #endif
        }
        return 0;
    }

    编译后输出结果为:

     

    2、然后将部分代码修改之后变为使用汇编方式触发系统调用,部分代码【namel-asm.c】如下:

     int fb;

              asm volatile(

                   "mov $0,%%ebx "

                   "mov $0x7A,%%eax "

                   "int $0x80 "

                   "mov %%eax,%0 "

                   : "=m" (testbuff)

              );

        fb=uname(&testbuff);

    l 下面来分析汇编代码调用系统调用的工作过程。

    第一个move是先将ebx清零,即令ebx为NULL;

    第二个move是将0x7A(uname是122号系统调用)放到eax中(eax是传递系统调用号的),即用eax传递参数;

    执行int $0x80指令开始进行系统调用,返回值用eax存储;

    然后将eax放到%0(即变量testbuff)中。

    编译后输出结果为:

     

    二、总结

    从视频和实验的学习中大概了解了系统调用的工作机制,尽管明白的肯定不够透彻,也足够帮助我消化这周的学习内容。

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

    中断处理的完整过程(由中断信号或者int指令完成):

    将cs:eip的值,堆栈段寄存器当前的栈顶(ss:eip)和当前的标志寄存器(eflags)保存到内核堆栈中;同时将当前中断服务例程的入口加载到cs:eip中,当前堆栈段和eip也加载到CPU中。

    执行完以上以上步骤之后,当前CPU在执行下一条指令时,就已经开始执行整个中断处理程序的入口了。

    此时已经开始操作内核态的堆栈了。

    若完成中断服务之后不发生进程调度,则继续执行指令(RESTORE_ALL和iret),然后返回到原来的状态;

    若发生进程调度,那么当前发生的状态都会暂时的保存在系统中,当下一次发生调度再次回到当前进程时就继续执行指令RESTORE_ALL和iret。

    2系统调用的工作机制:

     

    用户态中xyz()函数就是系统调用对应的API;

    这个API中封装了一个系统调用,这个系统调用会触发int 0x80的一个中断;

    0x80这个中断向量就对应着system_call(内核代码的入口起点);

    内核代码中可能会执行到对应的中断服务程序sys_xyz();

    在中断服务程序执行完之后,可能会执行ret指令,此时可能会发生进程调度;

    如果没有发生进程调度,就执行iret,返回到用户态接着执行其他指令。

    总结:系统调用的三层皮:xyz(API)、system_call(中断向量)和sys_xyz(服务程序)。

  • 相关阅读:
    【前端】原生event对象和jquery event对象的区别
    【前端】js代码模拟用户键盘鼠标输入
    【前端】回到顶部
    【前端】Three.js
    【前端】三种复制数组的方法
    【Python】Django
    【前端】CommonJS的模块加载机制
    注释声明:TODO HACK XXX FIXME REVIEW
    【Python】Python3中的str和bytes
    【前端】iterable类型的 forEach方法
  • 原文地址:https://www.cnblogs.com/fuyujing/p/5286342.html
Copyright © 2020-2023  润新知