2020-2021-1 20209322《Linux内核原理与分析》第五周作业 系统调用的三层机制(上) 一、用户态、内核态和中断 二、系统调用概述 三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

一、用户态、内核态和中断

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

1.1用户态和内核态

Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核)。内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程序运行的环境。用户态即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。linux中采用0、3两个特权级别,0为内核态,3为用户态,内核态下可以访问所有的地址空间,但是在用户态下只能访问0x00000000-0xbfffffff的地址空间,0xc0000000以上的地址空间只能在内核态下访问。

1.2 中断

中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的 CPU 暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫Trap)。系统调用就是一种内部中断,当用户态切换到内核态时,就要把用户态寄存器上下文保存起来,同时把内核态寄存器的值放到当前cpu中。

二、系统调用概述

2.1系统调用的作用

  • 把用户从地城的硬件编程中解放出来
  • 极大地提高系统的安全性
  • 使用用户程序具有可移植性

2.2 API与系统调用

系统调用的库函数就是系统提供的API(应用程序编程接口),系统调用通过软中断向内核发出中断请求,libc库函数中定义的一些API内部使用了系统调用的封装例程,使程序员在写代码时不需要用汇编指令和寄存器传递参数来触发系统调用。一般每个系统调用对应一个系统调用的封装例程,库函数再用这些封装例程定义出程序员调用的API,系统调用最终封装为方便程序员使用的库函数。系统调用是为了方便使用操作系统的接口,而库函数则是为了人们编程的方便。一个API可以对应一个或多个系统调用,而一个系统调用也可以被多个API调用。不涉及与内核交互的API内部不会封装系统调用。

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

2.3 系统调参数传递方式

系统调用的参数传递是通过寄存器传递的,EAX用于传递系统调用号,参数按顺序赋值给EBX、ECX、EDX、ESI、EDI、EBP,最多不超过6个参数。如果超过6个,就把某一个寄存器作为指针指向内存,这样就可以通过内存传递更多的参数。

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

实验,通过调用系统函数time()来获取系统的时间,即使用库函数API time()来获取系统的当前时间。

3.1编写time.c

#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:
",t->tm_year+1900,t->tm_mon,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
        return 0;
}

调用了time.h中time()库函数来获取当前时间

struct tm
{    
	int tm_sec;  /*秒,正常范围0-59, 但允许至61*/    
	int tm_min;  /*分钟,0-59*/    
	int tm_hour; /*小时, 0-23*/    
	int tm_mday; /*日,即一个月中的第几天,1-31*/    
	int tm_mon;  /*月, 从一月算起,0-11 1+p->tm_mon;  */ 
	int tm_year;  /*年, 从1900至今已经多少年 1900+ p->tm_year;  */ 
	int tm_wday; /*星期,一周中的第几天, 从星期日算起,0-6*/    
	int tm_yday; /*从今年1月1日到目前的天数,范围0-365*/    
	int tm_isdst; /*日光节约时间的旗标*/
};

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

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

//rename 系统调用原型
asmlinkage long sys_rename(const char _user *oldname,const char _user *newname);

这里选择触发rename系统调用,把系统调用号38(0x26)存入EAX寄存器,oldname存入EBX,newname存入ECX,执行int来使系统陷入内核态
2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

#include <stdio.h>
int main(){
        int ret;
        char *oldname="hello.c";
        char *newname="newhello.c";
        asm volatile(
                "movl %2,%%ecx
	"
                "movl %1,%%ebx
	"
                "movl $0x26,%%eax
	"
                "int $0x80"
                :"=a"(ret)
                :"b"(oldname),"c"(newname)
        );
        return 0;
}

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用
尝试使用C中嵌入汇编代码触发mkdir的系统调用,mkdir 39号,0x27;

#include <stdio.h>
int main(){
        int ret;
        char *filename="newFile";
        asm volatile(
                "movl %1,%%ebx
	"
                "movl $0x27,%%eax
	"
                "int $0x80"
                :"=a"(ret)
                :"b"(filename)
        );
        return 0;
}

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

3.3通用的触发系统调用的库函数syscall

函数原型

extern long int syscall(long int sysno,...) _THROW

sysno是系统调用号,"..."是系统调用所带的参数。

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用

2020-2021-1 20209322《Linux内核原理与分析》第五周作业
系统调用的三层机制(上)
一、用户态、内核态和中断
二、系统调用概述
三、使用库函数API和C代码嵌入汇编代码触发同一个系统调用