2020-2021-1 20209318《Linux内核原理与分析》第五周作业

系统调用的三层机制

用户态、内核态和中断

用户态: 在低的执行级别下,代码能够掌控的范围有所限制,只能访问部分内存。

内核态: 在高的执行级别下,代码可以执行特权指令,访问任意的物理内存。

中断: 从用户态进入内核态的主要方式。

  • 硬件中断:在用户态进程执行时,硬件中断信号到来,进入内核态,就会执行这个中断对应的中断服务例程。
  • 软中断:在用户态进程执行过程中,调用了一个系统调用(一种特殊中断),进入内核态。

系统调用概述

系统调用的功能特性:

  • 把用户从底层的硬件编程中解放出来。操作系统为我们管理硬件,用户态进程不用直接与硬件设备打交道。
  • 极大地提高系统的安全性。如果用户态进程直接与硬件设备打交道,会产生安全隐患,可能引起系统崩溃。
  • 使用户程序具有可移植性。 用户程序与具体的硬件已经解耦合并用接口代替了,不会有紧密的关系,便于在不同系统间移植。

系统调用3层机制:

  • 第一步,系统调用的库函数就是读者使用的操作系统提供的API,调用软中断向内核发出中断请求;
  • 第二步,CPU切换到内核态并开始执行一个system_call和系统调用内核函数,具体通过int $0x80触发系统调用的执行;
  • 第三步,进入内核,通过系统调用号将API函数和系统调用内核函数关联起来进行调用。

参数传递方式:

系统调用从用户态切换到内核态,在两种执行模式下使用不同的堆栈,通过比较特殊的寄存器传递参数。在x86-32中,EAX用于传递系统调用号,其余参数按顺序赋值给EBX,ECX,EDX,ESI,EDI和EBP,参数个数一般不超过6个。如果超过6个就把一个寄存器作为指针指向内存,通过内存传递更多的参数。

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

使用库函数API触发一个系统调用

调用函数time()来获取当前系统时间

#include<stdio.h>
#include<time.h>
int main()
{
    time_t  tt;
    struct tm *t;
    tt = time(NULL);
    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;
}

执行结果

内嵌汇编语法

分类 限定符 描述
通用寄存器 “a” 将输入变量放入eax
- “b” 将输入变量放入ebx
- “c” 将输入变量放入ecx
- “d” 将输入变量放入edx
- “s” 将输入变量放入esi
- “D” 将输入变量放入edi
- “q” 将输入变量放入eax,ebx ,ecx ,edx中的一个
- “r” 将输入变量放入通用寄存器,也就是eax ,ebx,ecx,edx,esi,edi中的一个
- “A” 把eax和edx,合成一个64位的寄存器(uselong longs)
内存 “m” 内存变量
- “o” 操作数为内存变量,但是其寻址方式是偏移量类型,也即是基址寻址,或者是基址加变址寻址
- “V” 操作数为内存变量,但寻址方式不是偏移量类型
- “.” 操作数为内存变量,但寻址方式为自动增量
- “p” 操作数是一个合法的内存地址(指针)
寄存器或内存 “g” 将输入变量放入eax,ebx,ecx,edx中的一个或者作为内存变量
- “x” 操作数可以是任何类型
立即数 “i” 0-31 之间的立即数(用于32位移位指令)
- “J” 0-63 之间的立即数(用于64 位移位指令)
- “N” 0-255 ,之间的立即数(用于out 指令)
- “l” 立即数
- “n” 立即数,有些系统不支持除字以外的立即数,这些系统应该使用“n”
操作数类型 “=” 操作数在指令中是只写的(输出操作数)
- “+” 操作数在指令中是读写类型的(输入输出操作数)
浮点数 “f” 浮点数
- “t” 第一个浮点寄存器
- “u” 第二个浮点寄存器
- “G” 标准的80387
- “%” 该操作数可以和下一个操作数交换位置

示例

C代码中嵌入汇编代码触发一个系统调用

汇编代码分析

asm volatile(
    "mov $0,%%ebx"           /*把EBX寄存器清零*/
    "mov $0xd,%%eax"     /*把0xd放到EAX寄存器中,EAX寄存器用于传递系统调用号*/
    "int $0x80"                          /*触发系统调用陷入内核执行13号系统调用的内核处理函数*/
    "mov %%eax,%0"/*通过EAX寄存器返回系统调用值*/
    :"=m"(tt)
    ); 

含两个参数的系统调用

汇编分析:

asm volatile(
			"movl %2,%%ecx"//将newname存入ECX寄存器
			"movl %1,%%ebx"//将oldname存入EBX寄存器
			"movl $0x26,%%eax"//系统调用号38 (16 进制是0x26)存入EAX寄存器
			"int $0x80"//执行系统调用陷入内核态
			:"=a"(ret)
			:"b"(oldname),"c"(newname)
		    );

从运行结果可以看到9318.c文件被重命名为zhaoshu.c。

遇到的问题

课本中tim.c代码有错误,将t->tm_mda改为t->tm_mday;

需要注意localtime函数使用0表示一月,因此输出结果与日常情况不符;

linux 系统调用号表

posted @ 2020-11-07 23:16  20209318赵姝  阅读(107)  评论(0编辑  收藏  举报