当前位置:首页 > CN2资讯 > 正文内容

windows 查看架构 windows10架构

2天前CN2资讯


系统调用

API(application program interface)即应用系统对操作系统功能的调用,也可以称为系统调用(system call)。

今天我们就来实现系统调用的功能,我们先来思考一下WIN10系统是如何实现系统调用的呢?

下图是简化版的Windows系统架构图:


Windows计算机中处理器有两种模式,分别为用户模式(用户态、目态)和内核模式(核心态、内核态、管态、系统模式、管理模式)。在用户模式下,处理器运行用户进程,不能使用特权指令,其中特权指令是指具有特殊权限的指令,一般不直接给用户使用,比如开/关中断指令、内存清零指令、停机指令等。而在内核模式下,处理器运行内核代码,可以使用特权指令。

在WIN10系统中,用户应用程序是通过子系统DLL来调用本地Windows服务的。Windows为用户模式中的应用程序提供一个虚拟地址块,被称为应用程序的用户空间。而应用程序不能直接访问的其余大块的地址被称为系统空间(内核空间)。

实现系统调用,我们需要CPU从用户空间进入到系统空间执行。而使CPU进入系统空间执行,有三种方式:

1.中断:当有来自外部设备的中断请求到来时,CPU会自动转入系统空间执行。(被动)

2.异常:当有执行指令发生异常时,CPU会进入系统空间执行。(被动)

3.自陷(陷入、陷阱):CPU通过自陷指令进入系统空间执行,自陷指令的执行相当于子程序调用,系统调用一般都是通过自陷指令实现的。(主动)

这里我们也是使用类似自陷的方式,来实现系统调用。

显示单个字符的API

我们先来通过API显示单个字符,实现这个功能我们先把需要显示字符编码存入寄存器,然后再让应用程序能够调用cons_putchar函数。这里存在两个问题,一个问题是函数没法接收存在寄存器上的字符编码,再一个问题是我们不知道cons_putchar函数的地址。所以我们先写一个函数_asm_cons_putchar,将寄存器的值推入栈中,再在这个函数中调用cons_putchar函数。

调用结构图如下:

在bootpack.map文件中我们能够查找到cons_putchar函数的地址,将它填入到代码中。

bootpack.map文件:

我们将地址填入应用程序中,需要注意的是,当应用程序通过API执行CALL指令实现函数调用时,需要加上段号。这里操作系统的段为“2*8”,使用far-CALL,同时指定段和偏移量。

hlt.nas文件:

[BITS 32] MOV AL,'A' ;这句就是API CALL 2*8:0xbe3 ;还有这句 fin: HLT JMP fin

cons_putchar函数的地址保存在内存中,这里保存在BOOTINFO之前的0x0fec。

console.c节选:

void console_task(struct SHEET *sheet, unsigned int memtotal) { (略) cons.sht = sheet; cons.cur_x = 8; cons.cur_y = 28; cons.cur_c = -1; *((int *) 0x0fec) = (int) &cons; /*cons_putchar函数的地址保存在0x0fec*/ }

我们使用了far-CALL调用_asm_cons_putchar函数,因此需要使用对应的far-RET返回,即最后使用RETF指令返回。

_asm_cons_putchar函数:

_asm_cons_putchar: PUSH 1 AND EAX,0xff ; 将AH和EAX的高位置0,将EAX置为已存入字符编码的状态 PUSH EAX PUSH DWORD [0x0fec] ; 读取内存并PUSH该值 CALL _cons_putchar ; 调用cons_putchar函数 ADD ESP,12 ; 将栈中的数据丢弃 RETF ; 使用RETF指令返回

其中CALL指令与JMP指令类似,是用来调用函数的指令,可以使用RET指令返回调用原先的位置。它会对当前指令的下一条指令进行压栈操作,即将要返回的目标地址PUSH到栈中,从而实现函数返回。

CLL指令相当于:

PUSH xxxxx

JMP xxxxxx。

修改完成,make run一下:

结束应用程序

目前在命令行窗口中输入hlt之后,执行HLT指令就没法继续输入指令了。我们来让应用程序结束后,能返回操作系统。由于需要调用的程序位于不同的段,应该使用far-CALL调用函数,相对的应用程序使用far-RET返回调用,这里只要把HLT指令改为RETF就可以了。

创建一个farcall函数,这个函数与far jmp类似:

naskfunc.nas节选:

_farcall: ; void farcall(int eip, int cs); CALL FAR [ESP+4] ; eip, cs RET

再将执行HLT指令的地方改为调用farcall,这里应用程序所在的段为“1003*8”。

console.c节选:

void cmd_hlt(struct CONSOLE *cons, int *fat) { (略) if (finfo != 0) { /* 找到文件的情况 */ (略) farcall(0, 1003 * 8); /* 调用farcall */ memman_free_4k(memman, (int) p, finfo->size); } else { /* 没有找到文件的情况 */ (略) } (略) }

由于修改了操作系统代码,要重新查找一下_asm_cons_putchar函数的地址:

再将HLT指令改为RETF指令,同时执行一些内容。

hlt.nas节选:

[BITS 32] MOV AL,'h' CALL 2*8:0xbe8 MOV AL,'i' CALL 2*8:0xbe8 MOV AL,' ' CALL 2*8:0xbe8 MOV AL,'m' CALL 2*8:0xbe8 MOV AL,'i' CALL 2*8:0xbe8 MOV AL,'n' CALL 2*8:0xbe8 MOV AL,'t' CALL 2*8:0xbe8 RETF

make run一下——

不随操作系统版本而改变的API

我们在IDT中找一个空闲的项,这里选择使用0x31号(0x30 ~ 0xff都是空闲的,随便选一个),再将_asm_cons_putchar函数注册到这里:

dsctbl.c节选:

void init_gdtidt(void) { (略) /* IDT的设置 */ set_gatedesc(idt + 0x20, (int) asm_inthandler20, 2 * 8, AR_INTGATE32); set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32); set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32); set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32); set_gatedesc(idt + 0x31, (int) asm_cons_putchar, 2 * 8, AR_INTGATE32); /* 注册asm_cons_putchar函数*/ return; }

再将hlt.nas文件中的“CALL 2*8:0xbe8 ”更改为“ INT 0x31”。

hlt.nas文件:

[BITS 32] MOV AL,'h' INT 0x31 MOV AL,'i' INT 0x31 MOV AL,' ' INT 0x31 MOV AL,'m' INT 0x31 MOV AL,'i' INT 0x31 MOV AL,'n' INT 0x31 MOV AL,'t' INT 0x31 RETF

在使用INT指令调用时,CPU认为执行了中断处理,会自动执行CLI禁止中断请求,因此在函数开头加入STI指令允许中断发生。函数中的RETF指令就无法返回,这里改为IRETD指令。IRETD用于从使用32位操作数大小的中断返回,对应的有IRET,用于从使用16位操作数大小的中断返回。

naskfunc.nas节选:

_asm_cons_putchar: STI PUSH 1 AND EAX,0xff ; 将AH和EAX的高位置0,将EAX置为已存入字符编码的状态 PUSH EAX PUSH DWORD [0x0fec] ; 读取内存并PUSH该值 CALL _cons_putchar ADD ESP,12 ; 丢弃栈中的数据 IRETD ; 这里改为IRETD指令

更改应用程序名称

现在应用程序的名字叫hlt已经不合适了,我们来实现这样的功能:先根据应用程序的名称来寻找对应的文件,如果找到就执行,找不到就提示输入错误”Input error“。

console.c节选:

void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, unsigned int memtotal) { if (strcmp(cmdline, "mem") == 0) { cmd_mem(cons, memtotal); } else if (strcmp(cmdline, "cls") == 0) { cmd_cls(cons); } else if (strcmp(cmdline, "dir") == 0) { cmd_dir(cons); } else if (strncmp(cmdline, "type ", 5) == 0) { cmd_type(cons, fat, cmdline); } else if (cmdline[0] != 0) { if (cmd_app(cons, fat, cmdline) == 0) { /* 不是命令,不是应用程序,也不是空行*/ putfonts8_asc_sht(cons->sht, 8, cons->cur_y, COL8_FFFFFF, COL8_000000, "Input error.", 12); cons_newline(cons); cons_newline(cons); } } return; }

修改cmd_hlt函数,得到cmd_app函数,令输入hi.hre和hi都能够运行程序。再将hlt.nas文件更名为hi.nas,然后汇编生成hi.hrb。

修改完成,执行make run——

太久没更,有些生疏了。技术也要时时勤拂拭,勿使惹尘埃!

注:本文参照《30天自制操作系统》制作,感谢各位的持续关注。

另附书籍源码(本文源码20_day文件夹):

链接:https://pan.baidu.com/s/1Lb-nWIdTvU0mYDgo0njqtQ
提取码:ghm2


    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/27139.html

    分享给朋友:

    “windows 查看架构 windows10架构” 的相关文章

    RackNerd VPS:超高性价比与稳定服务的完美选择

    引言 在如今的网络环境中,选择一家合适的VPS服务商无疑是一个关键决策。很多人可能对market上的多个选项感到无从选择。RackNerd作为一家美国成立的VPS主机商,凭借其超高性价比迅速进入了大众视野。特别是在一年一度的黑五促销活动中,RackNerd常常引发热潮,其 $10/年的价格实在让人不...

    如何使用RackNerd优惠码进行主机购买:节省开支的最佳策略

    RackNerd是一家成立于2017年的国外主机公司,作为一家新生力量,它迅速在市场上占据了一席之地。它的使命是为全球用户提供可靠且高性能的主机服务,帮助他们搭建自己的网络基础设施。我最喜欢RackNerd的地方是他们始终如一地致力于客户体验,这让我在使用他们的服务时非常安心。 RackNerd的服...

    AWS VPS Free: 如何利用AWS Free Tier免费服务轻松构建云计算项目

    当我第一次接触AWS (亚马逊网络服务) 的时候,最吸引我的就是他们提供的各种免费的VPS服务。AWS的VPS免费服务实际上是一种叫做AWS Free Tier的计划,它允许用户在一定条件下使用AWS的多种服务而无需支付费用。这项计划的意义在于,它为刚入门的开发者和小型企业提供了一个绝佳的机会,能够...

    AT&T VPS详解:稳定性与灵活性的完美选择

    在这篇文章中,我想和大家聊聊AT&T VPS,尤其是在更广泛的虚拟私有服务器市场中,AT&T VPS代表了什么。AT&T是一家美国知名的电信运营商,其提供的VPS服务在一定程度上依赖于它的网络基础设施。这种结合了高质量网络与虚拟服务器技术的服务,吸引了大量需要高稳定性和可扩展...

    Linode新加坡服务器速度与性能评测

    Linode新加坡服务器概述 在当今互联网的快速发展中,选择一款合适的服务器至关重要。Linode是一个备受欢迎的云服务提供商,凭借其强大的全球服务网络而受到许多用户的信任。尤其是Linode的新加坡服务器,以其优越的速度和性能而显得尤为突出。 Linode在多个国家和地区设有数据中心,其中新加坡的...

    VPS硬盘清理:提升服务器性能的全面指南

    当我的VPS(虚拟专用服务器)磁盘满了时,事情往往会变得非常棘手。这不仅会导致应用程序的运行速度变慢,甚至可能引发系统崩溃或数据丢失。这样的状况让我不得不思考,定期进行硬盘清理的重要性。其实,维护良好的磁盘使用状况,对于确保服务器的整体性能至关重要。 首先,当VPS磁盘满了,系统的反应速度会明显下降...