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

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

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

1、用户通过库函数与系统调用联系起来。

2、在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态。而在相应的低级别执行状态下代码的掌控范围受到限制。只能在对应级别允许的范围内活动。

3、intel x86 CPU有四种不同的执行级别0-3。Linux只取两种,0级是内核态,3级是用户态。

4、如何区分用户态与内核态?

cs寄存器的最低两位表明了当前代码的特权级

CPU每条指令的读取都是通过cs:eip这两个寄存器:cs是代码段选择寄存器,eip是偏移量寄存器

上述判断由硬件完成

一般来说在Linux中,地址空间是一个显著的标志:0xc0000000以上的空间只能在内核态下访问,0x00000000-0xbfffffff的地址空间在两种状态下都可以访问(这里所说的地址空间是逻辑地址而不是物理地址)。

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

6、寄存器上下文

从用户态切换到内核态时,必须保存用户态的寄存器上下文到内核堆栈中,同时会把当前内核态的一些信息加载,例如cs:eip指向中断处理程序入口。

如:用户态栈顶地址、当时的状态字、当时的cs:eip的值

7、中断发生后的第一件事就是保存现场 - SAVE_ALL

中断处理结束前最后一件事是恢复现场 - RESTORE_ALL

二、系统调用概述

1. 系统调用的意义:

操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用。

(1)把用户从底层的硬件编程中解放出来。

(2)极大地提高了系统的安全性

(3)使用户程序具有可移植性

2、API(应用编程接口)

与系统调用区别:

(1)API只是一个函数定义

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

(3)API可直接提供用户态服务;一个API调用几个系统调用;不同API可调用同一个系统调用。

3、Libc库

(1)定义的一些API引用了封装例程(唯一目的就是发布系统调用)

(2)一般每个系统调用对应一个封装例程。

(3)库再用这些封装例程定义出给用户的API;

4、返回值

(1)大多封装例程返回一个整数,其值依赖于相应的系统调用;

(2)-1表示内核不能满足进程的请求;

(3)Libc定义的errorno变量包含特定出错码;

5、系统调用的三层皮

(1)1API(xyz)

(2)中断向量(system_call)

(3)中断服务程序(sys_xyz)

(1)系统调用的服务例程中,中断向量0x80与system_call绑定起来。(Linux中可以通过执行int $128来执行系统调用。)

(2)system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即系统调用号。

(3)系统调用号将xyz与sys_xyz关联起来。调用号在eax寄存器中。

系统调用的参数传递:

(1)函数调用——压栈

(2)用户态到内核态——寄存器传递。

每个参数长度不能超过32位,个数不能超过6个。

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

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

使用time(),代码如下:

#include<stdio.h>

#include<time.h>

int  main()

{

time_t tt;

struct tm *t;   //构造一个结构体,方便读取

tt = time(NULL);   //time系统调用

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;

}

2、使用C代码中嵌入汇编代码触发系统调用获取系统当前时间

代码如下:

#include<stdio.h>

#include<time.h>

int  main()

{

time_t tt;

struct tm *t;

asm volatile(

"mov $0,%%ebx\n\t"   //把ebx清零,相当于传参数

"mov $0xd,%%eax\n\t"    //把0xd放入eax中,即系统调用号13,指time

"int $0x80\n\t"

"mov %%eax,%0\n\t"    //返回值是在eax中,%0指tt,返回值放到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;

}

四、总结

   系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口。当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数。在Linux 下三种发生系统调用的方法:

   1、通过 glibc 提供的库函数

        glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库。glibc 为程序员提供丰富的 API(Application Programming Interface),除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。

   2、使用 syscall 直接调用

    如果 glibc 没有封装某个内核提供的系统调用时,就没办法通过上面的方法来调用该系统调用。此时我们可以利用 glibc 提供的syscall 函数直接调用。

   3、通过int指令陷入

    如果我们知道系统调用的整个过程的话,应该就能知道用户态程序通过软中断指令int 0x80 来陷入内核态(在Intel Pentium II 又引入了sysenter指令),参数的传递是通过寄存器,eax 传递的是系统调用号,ebx、ecx、edx、esi和edi 来依次传递最多五个参数,当系统调用返回时,返回值存放在 eax 中。

  

时间: 2024-08-06 16:06:38

Linux内核分析——扒开系统调用的三层皮(上)的相关文章

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

20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 实验目的: 通过以一个简单的menu小程序,跟踪系统调用的过程,分析与总结系统调用的机制和三层进入的过程. 实验原理: 系统调用是通过软中断指令 INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中.(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,而不是由硬件外设引起的.)IN

第四周 扒开系统调用的三层皮(上)

用户态,内核态和中断 和系统调用打交道的方式:通过库函数,把系统调用给封装起来 用户态vs内核态: 一般现代CPU都有几种不同的指令执行级别 在高级别的状态下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别对应着内核态 在相应的低级别执行状态下,代码的掌控范围会受到限制,只能在对应级别允许的范围内活动 为什么有权限级别的划分:为了防止系统崩溃以及恶意代码的入侵,通过划分权限级别来让系统更稳定 举例:Intel x86 CPU有四种不同的执行级别0-3,Linux只使用了其中的0级和

20135327郭皓--Linux内核分析第四周 扒开系统调用的三层皮(上)

Linux内核分析第四周 扒开系统调用的三层皮(上) 郭皓 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一.用户态.内核态和中断 用户态:当进程在执行用户自己的代码时,则称其处于用户态,即此时处理器在特权级最低的(3级)用户代码中运行. 内核态:当一个进程执行系统调用而陷入内核代码中执行时,我们就称进程处于内核态,此时处理器处于特权级最高的(0级)内核代码中执行. PS:CPU指令

《Linux内核分析》第四周 扒开系统调用的“三层皮”

[刘蔚然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK FOUR(3.14——3.20)扒开系统调用的“三层皮” SECTION 1 用户态.内核态和中断处理过程 1.用户态.内核态区别 在高级别的状态下,代码可以执行特权指令,访问任意的物理地址: 在相应的低级别执行状态下,代码的掌控范围会受到限制. 为什么会有这种级别划分? 没有访问权限划分容易使得系统混乱(毕竟普通程序

《Linux内核分析》第四周笔记 扒开系统调用的三层皮(上)

扒开系统调用的三层皮(上) 一.用户态.内核态和中断 库函数将系统调用封装起来. 1.什么是用户态和内核态 一般现代CPU都有几种不同的指令执行级别. 在高执行级别下,代码可以执行特权指令,访问任意的物理地址,这种CPU执行级别就对应着内核态. 而在相应的低级别执行状态下(用户态),代码的掌控范围会受到限制.只能在对应级别允许的范围内活动.系统容易崩溃. 在intel X86CPU有四种不同的执行级别0,1,2,3,linux只使用了0级和3级分别来表示内核态和用户态. 2.在linux内核代码

实验五:扒开系统调用的三层皮(下)

实验五:扒开系统调用的三层皮(下) 王朝宪20135114 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.给MenuOS增加time和time-asm命令 1. 通过内核的方式(跟踪调试系统调用)来理解并使用系统调用. rm menu -rf //强制删除当前menu git clone http://github.com/mengning/menu.git //重新克隆新版本的m

第四周—扒开系统调用的“三层皮”

[洪韶武 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ]  第四周 扒开系统调用的“三层皮” 一.本周学习内容总结   1.系统调用的“三层皮” xyz函数—API systemcall—中断向量 sysxyz—中断服务程序 2.系统调用的意义及API与系统调用的关系 (1)系统调用的意义: 把用户从底层的硬件编程中解放出来 极大提高了系统安全性 使用户程序具有可移植性 (2)API

20135201李辰希 《Linux内核分析》第四周 扒开系统调用的“三层皮”

李辰希无转载 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.用户态.内核态和中断处理过程 1.我们与系统调用打交道是通过库函数的方式 2.一般现代CPU都有几种不同的指令执行级别 因为如果所有程序员写的代码都可以有特权指令的话,系统就会很容易崩溃. 3.区别: 在高级别的状态下,代码可以执行特权指令,访问任意的物理地址. 在相应的低级别执行状态下,代码的掌控范围会受到限制. Intel x86 CPU有四

Linux内核及分析 第四周 扒开系统调用的三层皮(上)

实验过程 选择20号系统调用getpid(取得进程识别码) 在网上查询getpid函数的C语言代码以及其嵌入式汇编语句 C语言代码: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, const char *argv[]) { pid_t tt; tt = getpid(); printf("%u\n&q