一个程序在计算机中是如何运行的?

2020-03-25 15:13发布

3条回答
Anonyem
2楼 · 2020-05-12 15:01

一、程序编译的过程




二、程序加载进CPU的过程




三、CPU的组成


累加寄存器(AC) :主要进行加法运算。

标志寄存器(PSW) :记录状态,做逻辑运算。

程序计数器(PC) :是用于存放下一条指令所在单元的地址的地方。

基质寄存器(BX) :储存当前数据内存开始的位置。

变址寄存器 :储存基质寄存器的相对位置。

通用寄存器(GPRs):支持有所的用法。

指令寄存器(IR) :CPU专用,储存指令。

堆栈寄存器(SP) :记录堆栈的起始位置。




CPU是由四大部分所构成的:寄存器、控制器、运算器、时钟。


寄存器

CPU内部的内存,程序加载进CPU内部的寄存器中从而被用来解释和运行。

控制器

计算机的指挥中心,负责决定执行程序的顺序,给出执行指令时机器各部件需要的操作控制命令。

运算器

计算机中执行各种算术和逻辑运算操作的部件。

时钟

它是处理操作的最基本的单位,影响着指令的取出和执行时间。


卡卡
3楼 · 2020-10-12 09:32





程序是如何运行的

现代计算机存储和处理的信息以二值信号表示,程序对于计算机而言就是一串二进制数据流,以流水线的方式进入CPU进行运算。主要在;CPU与内存之间进行数据传递。本文将从程序源码的结构与表现形式开始,到编译生成可执行文件,再到执行文件的加载,最终到执行文件的运行整个过程进行梳理。

1程序的结构与表现形式

大多数计算使用8位的块,即字节(byte),作为最小的可寻址的内存单元。程序对象,即程序数据、指令和控制信息的字节集合,编译器和系统运行时将存储空间划分成更可管理的单元来存储程序对象。

计算机执行机器代码,用字节序列编码低级的操作,包括处理数据、管理内存、读写存储设备上的数据、以及利用网络通信。程序源码会经过编译器生成机器代码,编译器基于编程语言的规则、目标机器的指令集合和操作系统遵循的惯例,经过一系列的阶段生成机器代码。汇编代码是机器代码的文本表示,给出程序中的每一条指令。

计算机系统使用了多种不同形式的抽象,利用抽象模型来隐藏实现的细节。对于机器级编程来说,两个重要的抽象:1.指令集架构(InstructionSetArchitecture,ISA)定义机器级别格式和行为,处理器状态、指令的格式,以及每条指令对状态的影响。2.虚拟内存地址,程序使用的内存地址是虚拟地址,提供内存模型看上去是一个非常打的字节数组。实际上又许多个硬件存储器和操作系统软件组合起来。

以C语言为例,编写程序mstore.c

longmult2(long,long);


voidmultistore(longx,longy,long*dest){

longt=mult2(x,y);

*dest=t;

}

经过gcc编译器,产生一个汇编文件mstore.s

multstore:

pushq%rbx

movq%rdx,%rbx

callmult2

movq%rax,(%rbx)

popq%rbx

ret

上面代码中每行对于一条机器指令,比如,pushq指令应该将%rbx的内容压入程序栈中。

再将改mstore.c编译并汇编成目标代码文件mstore.o,该二进制文件中,又一段14个字节的序列,它的十六进制表示为:

534889d3e8000000004889035bc3

为了弄清这些14个字节表示的含义,可以通过objdump反汇编该mstore.o文件

可以看到,这14个字节分成若干组,左边是一条指令,右边是等价的汇编语言。

程序中包含过程、控制

过程是软件中一种重要的抽象。它提供了一种封装代码的方式,用一组制定的参数和一个可选的返回值实现了某一功能。然后,可以再程序中不同的地方调用这个函数。设计良好的软件用过程作为抽象机制,隐藏某个行为的具体实现,同时又提供清晰简洁的接口定义,说明计算的是哪些值,过程会对程序状态产生什么样的影响。不同编程语言中,过程的形式多样;函数(funciton)、方法(method)、子例程(subroutine)、处理函数(handler)等等。

要提供对过程的机器级支持,必须要处理许多不同的属性。为了讨论方便,假设过程P调用过程Q,Q执行后返回到P。这些动作包括一下一个或多个机制:

传递控制。再进入过程Q的时候,程序计数器必须被设置位Q的代码的起始地址,然后在返回时,要把程序计数器设置位P中调用Q后面那条指令的地址。传递数据。P必须能够向Q提供一个或多个参数,Q必须能够向P返回一个值分配和释放内存。在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些存储空间。

x86-64的过程实现包括一组特殊的指令和一些对机器资源(寄存器和程序内存)使用的约定规则。

控制程序中的控制逻辑,例如条件语句ifelse,循环fordo-while等。机器级指令的执行,有两种方式实现条件控制,一种将控制条件进行传递,一种是将不同条件计算结构进行传递。后一种方式在现代计算机中能提高程序运行的效率,代码中的指令都是按照在程序中出现的次序,顺序执行的,使用jump指令可以改变一组机器代码指令的执行顺序,从而实现条件操作。

为了实现条件控制,CPU中维护了一组单个位的条件码(conditioncode)寄存器,它们描述了最近的算数或逻辑操作的属性。可以通过检测这些寄存器来执行条件分支指令,通常条件码有,CF:进位标志;ZF:零标志。SF:符号标志;OF:溢出标志。

运行时调用栈大多数语言过程调用机制采用栈数据结构提供的后进先出的内存管理原则。过程P调用过程Q的过程,如果上图所示。

2程序代码的编译过程


预处理阶段,主要是修改原始程序,例如将#include命令告诉预处理读取系统stdio.h的文件,并将它直接插入到程序文本中。结果得到的另一个C程序,以.i作为扩展名;编译阶段,编译器ccl将文本hello.i翻译成文本hello.s,它包含一个汇编语言程序;汇编阶段,汇编器将.s文件编译成一个二进制的文件,把这些指令打包成一种叫做可重定位的目标程序的格式,并将结果保存在目标文件.o文件中。链接阶段,将各种代码和数据片段手机并组合并成可以执行的目标文件,简称可执行文件,可以被加载到内存中,由系统执行。

本节主要讨论链接阶段。链接可以执行与编译时,即将源代码翻译成机器代码时;可以执行与加载时,即程序被加载器加载到内存时;可以执行与运行时,也就是由应用程序来执行。

链接器是的分离编译(separatecompilation)成为可能。我们不用将一个大型的应用程序组织成一个巨大的源文件,而时可以把它分解为更小、更好管理的模块。理解链接的工作原理可以帮助我们避免一些危险的编程错误、理解语言的作用域规则、理解一些重要概念(加载、运行程序、虚拟内存、分页、内存映射)、有助于理解共享库。

为了构造可执行的文件,链接器必须完成两个主要任务:

符号解析(symbolresolution)。目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量。符号解析的目的时将每个符号引用正好和一个符号定义关联起来;重定位(relocaiotn)。编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得他们指向内存这个内存位置。链接器使用汇编器产生的重定位条目(relocationentry)的详细指令,不加甄别地执行这样的重定位。

目标文件分为三种:

可重定位目标文件,包含二进制代码和数据,其形式在编译时可以与其他可重定位目标文件合并起来,创建一个可执行目标文件;可执行目标文件,包含二进制代码和数据,其形式可以被直接复制到内存并执行;共享目标文件,一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载进内存并链接。

3执行文件

可执行的目标文件,通过加载器,加载到内存,共CPU调用运行。

进程是执行中程序的一个具体实例,程序总是运行在某个进程的上下文中。

进程提供了给应用程序的关键抽象:

一个独立的逻辑控制流,程序计数器PC值序列叫做逻辑控制流,每个PC值对应可执行目标文件中的指令,或者是包含在运行是动态链接到程序的共享对象中的指令。一个私有的地址空间,进程位每个程序提供一种假象,好像它独占地使用系统地址空间。例如,在一台64位地址的机器上,地址空间是


264

[removed]2^64[removed]个可能地址的集合。进程为每个程序提供它自己的私有地址空间。一般而言,其他进程是不能访问该进程的地址空间所关联的内存字节。每个私有地址空间有相同通用的结构,如下图所示地址空间底部是保留给用户程序的,包括通常的代码、数据、堆和栈段。代码段总是从地址0x00400000开始。地址空间顶部保留给内核(操作系统常驻内存的部分)。地址空间的这部分包含内核在代表进程执行指令时使用的代码、数据和栈。

为了使操作系统内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。处理器通常是用某个控制寄存器的一个模式为来提供这种功能,该寄存器描述了进程当前享有的特权。

进程运行有两种模式:1.内核模式(超级用户模式)2.用户模式

当设置了模式位时,进程就运行在内核模式中(超级用户模式)。一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统的任何内存位置。

没有设置模式位时,进程运行在用户模式中。用户模式中的进程不允许执行特权指令(privilegedinstruction),比如停止处理器、改变模式位、或者发起一个I/O操作。也不允许用户模式中的进程直接引用地址空间中内核区内的代码和数据。任何这样的尝试都会导致致命的保护故障。反之,用户程序必须通过系统调用接口间接地访问内核代码和数据。

操作系统内核使用一种称为上下文切换的较高层形式的异常控制流来实现多任务。内核为每个进程维持一个上下文明。上下文就是内核重新启动一个被抢占的进程所需状态。进程切换包含三个操作

保存当前进程的上下文恢复某个先前被抢占的进程被保存的上下文将控制传递给这个新恢复的进程


当进程A开始读取磁盘文件时,会通过执行系统调用read陷入到内核。内核中的陷进处理程序请求来自磁盘控制器的DMA传输,并且安排在磁盘控制器完成从磁盘到内存的数据传输后,磁盘终端处理器。

每个执行的程序,即系统中的进程,进程总可以处于下面三种状态:

运行,进程要么在CPU上执行,要么在等待被执行且最终会被内核调度;停止,进程的执行被挂起,且不会被调度,当收到SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信号时,进制就会停止,并且保持停止知道它收到一个SIGCONT信号,在这个时刻,进程再一次开始运行。终止,进程永远地停止了。进程会因为三种原因终止:1)收到进程终止的信号,2)从主程序返回;3)调用exit函数。

加载并运行程序

当使用execve函数在当前进程的上下文中加载并运行一个新程序。(fork是在父进程下,创建一个新的上下文运行子进程)

#include

intexecve(constchar*filename,constchar*argv[],constchar*envp[]);

当使用execve加载filename之后,启动代码设置栈,并将控制传递给新程序的主函数。用户栈的典型组织结构:


信号除了操作系统利用异常来支持进程上下文切换的异常控制流形式,另外一种更高层次的软件形式的异常,成为Linux信号,它运行进程和内核中断其他进程。

一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。每种信号类型都对应于某种系统事件。底层的硬件异常是由内核异常处理程序处理的,正常情况下,对于用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常。比如,当进程在前台运行时,你键入Ctrl+C,那么内核就会发送一个SIGINT信号强制终止它。当一个子进程终止或者停止时,内核会发送一个SIGCHLD信号给父进程。

传送一个信号到目的进程是由两个不同步骤组成的

发送信号,内核通过更新目的进程上下文种的某个状态,发送一个信号给目的进程。发送信号可以由如下两种原因:1)内核检测到一个系统事件,比如除零错误。2)一个进程调用kill,显示要求内核发送一个信号给目的进程。一个进程可以发送信号给它自己接受信号,当目的进程被内核强波以某种方式对信号的发送做出反应时,它就接收了信号。进程可以忽略这个信号,终止或者通过之心一个称为信号处理程序的用户曾函数捕获这个信号。

一个发出而没有被接收的信号,叫做处理信号,在任何时刻,一种类型至多只会由一个待处理信号。重复发送在等待的信号,将会被内核抛弃。linux提供两种阻塞机制,隐式和显式

隐式,即内核默认会阻塞当前处理程序接受到的待处理信号,正好与该待处理信号类型相同的信号已经被该处理程序所捕获。显示阻塞机制,应用程序可以使用singprocmask函数和它的辅助函数,明确地阻塞和解除阻塞选定的信号。

通过本文我们阐述了,程序在计算机种运行的一些基本概念、逻辑流、内存等。然后,系统种程序往往不是独立运行的,不仅仅是包含最小限度的输入和输出。在现实世界中,应用程序利用操作系统提供的服务来与I/O设备以及其他程序通信。

下一篇,详细探讨Unix操作系统提供的基本I/O服务,以及如何用这些服务来构造应用程序,例如Web客户端和服务器。


靓猴一枚
4楼 · 2021-11-18 15:59

通过操作系统送给CPU运行,CPU来调度其他硬件设备配合工作。

相关问题推荐

  • 什么是Python列表2020-12-03 10:47
    回答 20

    在python中列表(list)是使用最频繁的数据类型,在其他语言中通常叫做数组。列表由一系列按特定顺序排列的元素组成。你可以创建包含字母表中所有字母、数字0~9或所有家庭成员姓名的列表;也可以将任何东西加入列表中,其中的元素之间可以没有任何关系。...

  • 回答 16

    简单来讲,爬虫就是一个探测机器,它的基本操作就是模拟人的行为去各个网站溜达,点点按钮,查查数据,或者把看到的信息背回来。就像一只虫子在一幢楼里不知疲倦地爬来爬去。...

  • 回答 17

    因为Python可以做数据分析  数据挖掘,数据挖掘的话简单来说就是爬虫工程师

  • 回答 13

    1)学习爬虫,可以私人订制一个搜索引擎,并且可以对搜索引擎的数据采集工作原理进行更深层次地理解。有的朋友希望能够深层次地了解搜索引擎的爬虫工作原理,或者希望自己能够开发出一款私人搜索引擎,那么此时,学习爬虫是非常有必要的。简单来说,我们学会了...

  • 回答 4

    大部分数据都是可以爬的,不过有些网站对数据的加密做的非常好,在解析的过程中要消耗很长的时间。

  • 爬虫是什么?2020-08-26 10:11
    回答 10

    抓取数据,进行统计和分析

  • 成都爬虫好找工作吗2020-10-14 10:23
    回答 12

    在成都找网络爬虫工作也是可以的,自己可以先到招聘软件上看看当地招聘的需求量以及薪资待遇水平怎么样,要是能达到自己的预期效果可以在当地工作,要不可以到北上广深找工作,就业机会能更多些。...

  • 回答 9

    两种方式,一种是MySQL自带的命令行窗口,一种是图形用户管理工具,前者类似于一个cmd窗口,日常管理维护数据库不是很方便,后者就是一个图形用户管理软件,种类繁多,使用起来也相对容易一些...

  • 回答 4

    不可以所有编程最起码都得学函数,分支,循环之后才可以编写代码

  • 回答 2

    一台主机  多台从机

  • 回答 3

    一、分布式爬虫架构在了解分布式爬虫架构之前,首先回顾一下Scrapy的架构,如下图所示。Scrapy单机爬虫中有一个本地爬取队列Queue,这个队列是利用deque模块实现的。如果新的Request生成就会放到队列里面,随后Request被Scheduler调度。之后,Request交给Down...

  • 回答 7

    代理ip网址http://www.goubanjia.com/http://www.ip181.com/https://www.kuaidaili.com/python 环境安装requests库安装bs4库proxies设置代理服务器地址proxies = {'http':  'http://61.155.164.110:3128'}http://www.goub......

  • 回答 2

    import  requestsimport  jsonfrom bs4 import BeautifulSoupfrom    urllib.request import urlretrieve#构造函数def sk():    #请求头    header={        'user-Agent':'Mozilla/5.0 (Windows NT 10.0; W...

  • 回答 3

    针对字符串中本身含有双引号,我们可以利用单引号进行字符串的定义来解决这个问题。使用单引号作为字符串的界限就不会引起识别异常问题,保证字符串的合理定义

  • 回答 2

    要用unicode编码来替换。

没有解决我的问题,去提问