计算机CPU如何执行软件应用程序



我扩展了关于计算机程序运行时会发生什么的问题?以及来自斯坦福CS101网站软件:运行程序的讨论。CS101站点报价

机器代码定义了一组单独的指令。每个机器代码指令是极其原始的,比如加两个数字或测试一个数字是否等于零。存储时,每个指令只占用几个字节。当我们之前说CPU每秒可以执行20亿次操作,我们的意思是CPU可以每秒执行20亿行机器代码。

像Firefox这样的程序是由数百万个这些非常简单的机器代码指令。这有点难相信像Firefox这样丰富而复杂的东西是可以构建的只需将两个数字相加或进行比较的指令就是它的工作原理。当从远处看,即使单个沙粒极其简单。

我不明白的是,如何将Firefox窗口或GUI转换为只添加或比较两个数字的简单CPU指令?如何知道CPU执行的实际指令来打开Firefox窗口?在搜索栏中输入用户搜索结果如何?这可以转化为CPU指令吗?

如果Firefox是一个复杂的例子,那么像记事本这样的简单应用程序怎么样?从运行Notepad到键入ABCDEFGHIJ并将其保存为test.txt,是否真的可以看到正在执行的所有指令?

正如我在评论中提到的,在Linux中使用像objdump -d这样的反汇编工具可以帮助您获取二进制/可执行文件,并生成包含整个程序的汇编指令集。

例如,如果您在notepad.exe上使用objdump -d(这不会完全准确或有见地,因为objdump用于Linux,Notepad是Windows程序),您将看到:

notepad.exe:     file format pei-x86-64

Disassembly of section .text:
0000000140001000 <.text>:
140001000:   cc                      int3   
140001001:   cc                      int3   
140001002:   cc                      int3   
140001003:   cc                      int3   
140001004:   cc                      int3   
140001005:   cc                      int3   
140001006:   cc                      int3   
140001007:   cc                      int3   
140001008:   40 55                   rex push %rbp
14000100a:   48 8d 6c 24 e1          lea    -0x1f(%rsp),%rbp
14000100f:   48 81 ec d0 00 00 00    sub    $0xd0,%rsp
140001016:   48 8b 05 8b 14 03 00    mov    0x3148b(%rip),%rax        # 0x1400324a8
14000101d:   48 33 c4                xor    %rsp,%rax
...

我使用objdump是因为我在Linux上,但正如@PeterCordes在评论中指出的那样,汇编指令应该与Windows反汇编程序相同。

objdump输出有超过43k条汇编指令,因此破译汇编的每一部分都需要很长时间。这是记事本可以执行的全部指令集。因此,如果您想知道在执行类似类型ABC的操作并保存它时执行了哪些程序集指令以及以何种顺序执行,则需要使用某种跟踪器(例如gdb)来仅遍历那些特定执行的指令。

简而言之,Firefox窗口使用系统调用。系统调用指令使程序从用户模式跳转到内核中。它跳转到LSTAR64寄存器中指定的地址。系统调用可以是写入屏幕、写入文件等的调用。键盘本身由Intel的xHC轮询,当软件(操作系统)检测到按键被按下时,它将在当前具有焦点的应用程序的消息队列上发送消息。从内核模式来看,不同的硬件,如写入屏幕的GPU或读取/写入USB设备的xHC,将使用DMA PCI设备的MMIO(内存映射IO)进行交互。今天一切都是PCI。PCI是DMA,因为它直接写入RAM。它也是MMIO,因为要与PCI设备交互,只需在传统位置写入RAM即可。这允许读取/写入这些PCI设备的一些特殊寄存器,并告诉他们做一些事情(将按下的键写入RAM中的这个位置,使像素改变颜色,等等)。

较长的答案相当复杂。我会试着把它分解成小块。此外,我可能会说一些错误的话(因为我主要是从头开始写的),但我会尽力提供真实的信息。请随意纠正我可能说的任何错误。在这个答案中,我将以x86-64上的Linux为例。在Windows上,情况也会类似。

系统调用

机器代码定义了一组单独的指令。每一条机器代码指令都是极其原始的,比如加两个数字或测试一个数字是否等于零。

x86-64处理器有一组称为指令集的指令。所有x86处理器大多有两个制造商:AMD和Intel。AMD从英特尔获得x86架构的许可,用于制造自己的处理器。

寻呼机制是在最初只有分段的32位处理器之后引入的。分页机制允许在每个页面表中设置/取消设置一个位,以确定页面是主管还是用户。显然,不能从用户页面访问主管页面。这允许通过将内核与用户模式隔离来提供安全性(https://wiki.osdev.org/Paging)。

指令集中的一条指令是syscall指令,它有一个特定的二进制编码(我不知道)。大多数汇编语言都支持将系统调用指令汇编成正确的二进制格式。

syscall指令使处理器跳转到LSTAR64 MSR(型号特定寄存器)中指定的地址。这提供了一种从用户模式跳转到内核的安全机制。内核将在此寄存器中设置特定入口点的地址。Linux的入口点是文件/arch/x86/entry/entry_64.s。该文件在汇编中定义,将调用C函数来完成主要工作。

每种类型的调用都有一个在RAX寄存器中传递的号码,这些号码因操作系统而异。在Windows上,系统调用会有不同的编号(甚至可能有一个不同的寄存器来传递系统调用编号)。

最后,一旦执行了syscall指令,处理器现在就处于内核模式代码中,并且该代码可以访问整个RAM和所有IO设备。

引导

要了解GUI是如何启动的,您需要了解引导过程。如今,计算机使用UEFI引导。UEFI标准将引导时可用的系统调用定义为某种小型操作系统。UEFI固件因此设置了这个小型操作系统,以允许操作系统在引导时设置计算机。

这些UEFI系统调用允许从磁盘读取文件、获取一些ACPI表、设置图形模式、获取内存映射等。UEFI固件内置了驱动程序,可支持当今计算机上的所有硬件。这允许为操作系统提供一个引导接口,以便能够从磁盘获取内核的文件,而不需要在引导加载程序本身中使用巨大的临时驱动程序。

因此,操作系统开发人员提供了一个使用EDK2或gnu-efi编译(在实践中)的UEFI应用程序。UEFI应用程序将使用引导期间出现的系统调用编译为代码,以从磁盘获取内核的文件,然后跳到内核的入口点。

然后内核将控制一切并设置自己的系统调用接口。

对于Linux来说,引导是非常重要的,特别是自从systemd出现以来。Linux内核将启动sbin/init作为计算机的第一个进程。在最近的发行版中,sbin/init是systemd的符号链接。systemd程序将从磁盘中读取单元文件,这些文件是告诉systemd要做什么和启动其他进程的特殊文件。在要启动的进程中,是主GUI(桌面)本身。

X服务器

X服务器是一个特殊的程序,它在几乎所有Linux发行版的第一个进程中启动。X服务器充当本地服务器(也可以不是本地服务器),以便能够使用套接字与其通信。套接字实现存在于libstdc++中,以便在C++中使用。

X服务器还有一个名为X11的库,它定义了一组要调用的函数,这些函数通过套接字与X服务器通信。

X服务器使用/dev/input/目录及其内的字符设备从不同的输入设备获取输入。

为了写入屏幕,X服务器在libdrm中进行调用,libdrm本身进行系统调用。libdrm库将使用/dev/dri/中名为card0或card1的文件(card0是集成GPU,card1是分立GPU)。因此,库将使用ioctl调用(https://man7.org/linux/man-pages/man2/ioctl.2.html)在卡*文件上直接控制显卡(http://betteros.org/tut/graphics1.php)。

Mesa3D项目一直在尝试用开源驱动程序支持几种显卡。它与NVIDIA失败了,因为他们没有合作。NVIDIA显卡有自己的封闭源代码驱动程序,即使在内核运行时也可以作为模块安装。

这些闭源代码驱动程序提供了OpenGL的库实现。因此,一旦您启用某个封闭源代码驱动程序,X服务器将开始在库中调用OpenGL以写入屏幕。这还需要链接到glx库。否则,它将使用图形卡的帧缓冲模式或VESA模式。

字符设备

你可能听说过这样一句话:在Linux中,一切都是一个文件。这是由于虚拟文件系统将大多数设备作为文件呈现给用户模式。有几种类型的虚拟文件,其中包括角色设备。

字符设备具有打开、读取、写入和ioctl调用。因此,X服务器将从字符设备中读取,以收集来自系统的不同输入设备的输入。

驱动程序

阅读以下内容:在linux中如何加载内核模块?

PCI

你今天的鼠标和键盘可能是USB。计算机通过最初由英特尔创建的可扩展主机控制器(xHC)与USB进行交互。我不知道AMD是生产自己版本的芯片,还是从英特尔购买芯片。

你可以在那里阅读我的答案,了解它的工作原理:https://cs.stackexchange.com/questions/141870/when-are-a-controllers-registers-loaded-and-ready-to-inform-an-i-o-operation/141918#141918

最新更新