首页 > 学院 > 开发设计 > 正文

Linux系统调用------通过time系统调用理解系统调用的执行过程

2019-11-06 06:41:58
字体:
来源:转载
供稿:网友

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

一、重点知识 (1)用户栈与内核栈 内核栈:存在于内核空间,当进程在内核栈里运行时,CPU栈顶指针寄存器里面的内容是内核栈空间地址,使用内核栈。 用户栈:存在于用户空间,当进程在用户栈里运行时,CPU栈顶指针寄存器里面的内容是用户栈空间地址,使用用户栈。 (2)用户态与内核态 用户态:用户在非特权模式下,访问会资源会受到限制。 内核栈:用户在特权模式下,可随意访问资源 为什么要区分用户态与内核态? 因为在操作系统中有很多很重要的代码要保证安全性,不可被任意修改,区别用户态和内核态可访问的空间,保证了系统的安全,防止因不当操作而造成的系统崩溃。 在Linux中有0和3两种运行级别:0表示内核态,3表示用户态 (3)利用地址空间区别内核态和用户态 只有内核态可以访问0xc0000000以上的地址空间,0x00000000到0xbfffffff在两种状态下都可以访问 (4)内核态与用户态的切换 当进程因为硬件中断或者系统调用,而从用户态转变为内核态时,进程所使用的堆栈要从用户栈变为内核栈,此时进程进入内核态后,首先,在内核态上保存用户态堆栈上的地址,设置堆栈指针寄存器的内容为当前进程的内核栈地址,这样就完成了用户栈向内核栈的切换。 当进程从内核态恢复到用户态时,将内核态保存的用户态的堆栈地址恢复到栈指针寄存器,这样就完成了内核态向用户态的切换。 (5)系统调用 1.系统调用与API(应用程序编程接口)区别 API只是一个函数定义,而系统调用通过软中断向内核发出一个明确的请求。 不是每个API都对应一个特定的系统调用。首先,API可能直接提供用户态的服务(比如一些数学函数),其次,一个单独的API可能调用几个系统调用,不同的API可能调用了同一个系统调用。 2.系统调用的优点 系统调用为用户提供了调用与硬件设备等进行交互的接口,可以将用户从底层的硬件编程中解放出来,提高系统的安全性,是用户程序具有更好的可移植性。 3.系统调用时执行的操作 1)在进程的内核态堆栈中保存大多数寄存器的内容(即保存恢复进程到用户态执行所需要的上下文) 2)根据用户态传递的系统调用号,确定系统调用的服务例程 3)调用名为系统调用服务例程的相应的C函数来处理系统调用 4)从系统调用返回 4.系统调用的过程描述 这里写图片描述 简单描述系统调用的执行过程:在用户态下执行某个API函数,在API函数中有一个int 0x80的中断向量,触发后,由用户态进入内核态,保存现场,查找system_ call表找到相应的系统调用号,找到后执行相应的系统调用服务例程,执行过后返回,恢复现场,回到用户态。 这里写图片描述 可以发现:用户模式下的API函数xyz()中在执行时触发了中断向量int 0x80,随后进入了内核态,保存现场,执行相应的服务例程。 中断向量0x80与system_call连接起来,system _call包含了很多系统调用,通过系统号找到特定的服务例程。系统调用号将xyz与sys _xyz关联起来。 5.系统调用的参数传递(从用户态向内核态传递参数)规则: (1)每个参数的残毒不能超过寄存器的长度 (2)在系统调用号(eax)外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp),如果超过6个,将某一个寄存器作为一个指针指向一块内存,到达内核栈后,通过这块内存来传递参数 二、实验内容 1.简单的系统调用time,获得系统时间

//This PRogram tests syscall call such as time#include <stdio.h>#include <time.h>int main(){ time_t tt; struct tm *t; tt = time(NULL);//time系统调用多的当前系统时间 t = localtime(&tt); //将tt这个size_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;}

实验执行结果:gcc …… -m32,64位的系统按32位编译程序 这里写图片描述 2.利用内嵌汇编代码深层了解系统调用过程

#include <stdio.h>#include <time.h>//this program :use asm to call timeint main(){ time_t tt; struct tm *t; asm volatile( "mov $0,%%ebx/n/t" //ebx用来传递系统调用的第一个参数,这里传递的是NULL代表当前时间 "mov $0xd,%%eax/n/t" //time系统调用的系统调用号是13(d),用eax来传递系统调用号 "int $0x80/n/t" //触发系统调用 "mov %%eax,%0/n/t" //将系统调用的返回值存储到%0(tt变量中) :"=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;}

实验执行结果 这里写图片描述 在这段内嵌汇编代码中,模仿了time系统调用的功能,在编写代码时我们用ebx来传递系统调用的第一个参数,用eax来传递系统调用号,通过int $0x80产生系统中断,执行系统调用号为13的系统调用,将返回的结果保存下来,这就完成了一次系统调用的模拟。 三、实验总结 今天的实验简单的了解了系统调用的基本知识,包括内核态与用户态、内核栈与用户栈以及在系统调用过程中,如何从内核态过渡到用户态等。 形象化的记忆将系统调用可以看成三层皮:API函数,system_ call,sys _xxx()系统调用服务例程。 要清楚系统调用的执行过程。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表