linux内核分析——扒开系统调用的三层皮(上)

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

系统调用:库函数封装了系统调用,通过库函数和系统调用打交道

用户态:低级别执行状态,代码的掌控范围会受到限制。

内核态:高执行级别,代码可移植性特权指令,访问任意物理地址

为什么划分级别:如果全部特权,系统容易崩溃。。。可以让系统更稳定,

 

Linux 只有0和3级

如何区分:cs和eip

0x0000000以上地址空间仅有内核态可以访问,0x00000000——0xbffffff两种状态都可访问

 

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

切换时,保存用户态寄存器上下文,int指令在堆栈保存一些寄存器的值(用户态栈顶地址、当时的状态字、当时cs:eip的值)

中断发生后的第一件事就是保存现场,中断处理结束前最后一件事是恢复现场

 

系统调用的意义

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

 

API(应用编程接口):只是一个函数定义

系统调用是通过软中断向内核发出一个明确的请求

 

不是每个API都对应一个特定系统调用,

 

三层皮:API,中断向量,sys_xyz

 

当用户态进程调用一个系统调用,CPU切换到内核态并开始执行一个内核函数。指令:Int $0x80

 

系统调用参数传递:每个参数长度不能超过寄存器长度(32位);参数个数不能超过6个(ebp,ecx,edx,esi,edi,ebp)

 

 

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

本次试验我使用了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\n nodename:%s\n release:%s\n version:%s\n machine:%s\n\n ",testbuff.sysname,testbuff.nodename,testbuff.release,testbuff.version,testbuff.machine);

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

编译后输出结果为:

 

 

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

 int fb;

          asm volatile(

               "mov $0,%%ebx\n\t"

               "mov $0x7A,%%eax\n\t"

               "int $0x80\n\t"

               "mov %%eax,%0\n\t"

               : "=m" (testbuff)

          );

    fb=uname(&testbuff);

 

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

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

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

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

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

 

编译后输出结果为:

 

 

posted @ 2016-03-19 23:18  pottermqy  阅读(302)  评论(0编辑  收藏  举报