首页 > 其他 > 详细

作业4:扒开系统调用的三层皮(上) 20135115臧文君

时间:2016-03-16 00:50:26      阅读:215      评论:0      收藏:0      [点我收藏+]

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

 

注:作者:臧文君,原创作品转载请注明出处,《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+1951,t->tm_mon,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+1951,t->tm_mon, 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个,就将某一个寄存器作为指针,指向一块内存,在进入内核态之后,可以访问所有的地址空间,通过内存来传递参数。

 

作业4:扒开系统调用的三层皮(上) 20135115臧文君

原文:http://www.cnblogs.com/CatherineZang/p/5281675.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!