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

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

使用API实现time()函数的调用。

编写time.C代码如下:

#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+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
	
	return 0;
	
}

运行后显示的月份比实际月份少一月份,参考李明帅同学的博客后发现localtime函数定义里月份从0开始,查阅相关代码信息后得知应在tm—_mon后+1使得月份从一月开始。修改后的代码和运行结果如下:

使用C语言内嵌汇编实现time()

代码如下:

#include<stdio.h>
#include<time.h>

int main(){
	time_t tt;
	struct tm *t;
	asm volatile(
		"movl $0,%%ebx;\n\t"
		"movl $0xd,%%eax;\n\t"
		"int $0x80;\n\t"	
		"movl %%eax,%0;\n\t"
		:"=m"(tt)
	);
	t = localtime(&tt);
	printf("time:%d:%d:%d:%d:%d:%d:\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
	return 0;
}

编译运行后显示段错误,查阅相关博客后发现问题是堆栈溢出,对代码进行相应修改后代码图和运行结果如下:

使用API实现rename函数的调用

创建一个mbh.c文件,将其重命名为mbh20209306.c。
代码如下:

#include<stdio.h>
int main()
{
    char *oldname = "mbh.c";
    char *newname = "mbh20209306.c";
    int ret;
    ret=rename(oldname,newname);
    if(ret==0)
        printf("Rename successfully!\n");
    else
        printf("Unable to rename the file!\n");
    return 0;
}

运行结果如下:

使用C语言内嵌汇编实现rename

将mbh20209306.c重命名为mbh.c。
代码如下

#include <stdio.h>
int main()
{
    char* oldname="mbh20209306.c";
    char* newname="mbh.c";
    int ret;
    asm volatile( 
        "movl %1,%%ebx\n\t"
        "movl %2,%%ecx\n\t" 
        "movl $0x26,%%eax\n\t"  
        "int $0x80\n\t"
        :"=a"(ret) 
        :"b"(oldname),"c"(newname)
        );
    if(ret==0)
        printf("Rename Successfully!\n");
    else
        printf("Unable to rename the file!\n");
    return 0;
}

运行结果如下:

二、学习到的知识内容

1.用户态、内核态和中断

1.内核态:在高的执行级别下,代码可以执行特权指令,访问任意的物理地址,这时的CPU就对应内核态
2.用户态:在低级别的指令状态下,代码 只能在级别允许的特定范围内活动。在日常操作下,执行系统调用的方式是通过库函数,库函数封装系统调用,为用户提供接口以便直接使用。
3.在Linux下0级表示内核态,3级表示用户态。
4.内核态cs:eip的值是任意的,即可以访问所有的地址空间。用户态只能访问其中的一部分内存地址。
5.中断处理是从用户态进入内核态的主要方式,系统调用是一种特殊的中断。中断/int指令会在堆栈上保存用户态的寄存器上下文,其中包括用户态栈顶地址、当时的状态字、cs:eip的值,以及内核态的栈顶地址、当时的状态字、中断处理程序入口。中断处理结束前的最后一件事就是恢复现场,退出中断程序,恢复保存寄存器的数据。

2.系统调用概述

1.系统调用的意义
操作系统为用户态进程与硬件设备进行交互提供的一组接口——系统调用
·把用户从底层的硬件编程中解放出来
·极大的提高了系统的安全性
·使用户程序具有可移植性
2.API(应用程序编程接口)与系统调用的关系
·API是一个系统调用封装成的一个函数定义
·系统调用通过软中断向内核发出一个明确的请求
·Libc库定义的一些API引用了封装例程,目的是发布系统调用,让程序员写代码的时候可以通过函数调用而非汇编指令触发一个系统调用
·一般每个系统调用对应一个封装例程,库再用这些封装例程定义出给用户的API
3.返回值
·大部分封装例程返回一个整数,其值的含义依赖于相应的系统调用
·-1在多数情况下表示内核不能满足进程的请求
·Libc中定义的errno变量包含特定的出错码
·应用程序、封装例程、系统调用处理程序及系统调用服务例程之间的关系:

·系统调用的三层皮:xyz(API)、system_call(中断向量)、sys_xyz(中断服务程序)
4.传参
·内核实现了很多不同的系统调用。
·进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数
·使用eax寄存器传递系统调用号。

posted @ 2020-11-07 10:42  毛贲豪  阅读(115)  评论(0编辑  收藏  举报