程序是怎么跑起来的

前言

本篇文章从整体流程上描述一下一个程序是怎么在计算机中运行的,整个流程分为三大块:

  • 程序的创造
  • 程序的编译
  • 程序的运行

程序的创造

一般来说,创造一个程序是代码工程师的责任,虽然现在有很多工具可以不经过编码就能创造一个可运行的程序甚至游戏。但是归根揭底,最终的程序都回归到一门编程语言上。

一般的情况,这是一门高级编程语言,可以是C/C++,可以是Python,可以是Object-C/Swift,也可以是Java等等。

经过前期的调研,立项,产品设计,UI设计,代码编写,产品测试等等一系列复杂的程序,多个人的集体合作,经过一定的时间以后,我们这个产品可能已经算是完成。

但是一个程序的创造从代码编写完成就已经结束了,测试使用的一般是经过编译后的产品,也就是已经经过了第二阶段

程序的编译

程序的编译普遍来说是编译器的责任,几乎没有人会手动进行代码的编译工作。并且绝大部分的开发其实是使用IDE进行编译的。从源代码到可运行的程序需要经过四步的工作,具体的流程可以参考文章代码到可执行文件的流程概述:
我们在整个过程中以下面的代码作为例子进行描述:

#include <stdio.h>
unsigned long sum(int* src, int size);
int main(int argv, char** argc)
{	int a[]={1,2,3,4,5,6,7,8,9,10};printf("a=%lu\n",sum(a,sizeof(a)/sizeof(int)));return 0;
}unsigned long sum(int* src, int size)
{unsigned long result = 0;for(int i = 0;i < size;i++){result += *(src++);}return result;
}
  • 源码阶段~预处理阶段
    这一步是由编译器的预处理器部分进行处理的,基本就是宏的替换,生成的文件为.i文件,一般这个文件比较大,因为包含了嵌套很多层的头文件信息,就像上面的代码,包含了头文件stdio.h,而stdio.h头文件又包含了别的头文件,只要是没有包含过的头文件都会按顺序依次添加进来。因为预处理后的文件太大,就不粘贴代码了,但是我看了一下,预处理后的文件有18Kb之多。
  • 预处理阶段~编译阶段
    这一阶段是将预处理后的文件编码成汇编语言显示的汇编文件,也就是.s文件,汇编文件是我们最后能操作的文件了(应该没有人还在敲机器指令),汇编文件由一系列的段组成,不同的段包含不同的数据,代码数据,全局数据,符号表都包含在段中。
    汇编文件也是我们在进行代码优化能够对比的文件,比如对于switch的使用,对于递归的优化等,下面的代码是前面的例子生成的汇编代码
    注意:汇编代码已经去掉了没有使用到的头文件的信息,包括变量和函数的声明
    	.file	"test1.c".text.globl	sum.type	sum, @function
    sum:
    .LFB14:.cfi_startprocmovl	$0, %edxmovl	$0, %eaxjmp	.L2
    .L3:movslq	(%rdi), %rcxaddq	%rcx, %raxaddl	$1, %edxleaq	4(%rdi), %rdi
    .L2:cmpl	%esi, %edxjl	.L3rep ret.cfi_endproc
    .LFE14:.size	sum, .-sum.section	.rodata.str1.1,"aMS",@progbits,1
    .LC0:.string	"a=%lu\n".text.globl	main.type	main, @function
    main:
    .LFB13:.cfi_startprocsubq	$56, %rsp.cfi_def_cfa_offset 64movl	$1, (%rsp)movl	$2, 4(%rsp)movl	$3, 8(%rsp)movl	$4, 12(%rsp)movl	$5, 16(%rsp)movl	$6, 20(%rsp)movl	$7, 24(%rsp)movl	$8, 28(%rsp)movl	$9, 32(%rsp)movl	$10, 36(%rsp)movl	$10, %esimovq	%rsp, %rdicall	summovq	%rax, %rdxmovl	$.LC0, %esimovl	$1, %edimovl	$0, %eaxcall	__printf_chkmovl	$0, %eaxaddq	$56, %rsp.cfi_def_cfa_offset 8ret.cfi_endproc
    .LFE13:.size	main, .-main.ident	"GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4".section	.note.GNU-stack,"",@progbits
    
  • 汇编代码~机器代码
    这一步编译器会把我们上面的汇编代码编译成机器代码,也就是.o文件,.o文件的文件组成没什么变化,还是由段组成,就是汇编指令被替换成了机器指令,也就是一条条的二进制数据,为什么是一条条呢,因为所有的汇编指令都有编码规则,就是一条汇编指令对应指定字节数的二进制序列,这个对应关系是固定的,但是不同的指令可以有不同的长度,这一步是编码,后面计算机执行程序的时候还有译码阶段。
    下面是.o文件的部分代码,也就是我们的代码数据,这只是一小部分,因为还有系统函数的调用,以及需要和别的库链接的符号表等信息。
    000000000040055d <sum>:40055d:	ba 00 00 00 00       	mov    $0x0,%edx400562:	b8 00 00 00 00       	mov    $0x0,%eax400567:	eb 0d                	jmp    400576 <sum+0x19>400569:	48 63 0f             	movslq (%rdi),%rcx40056c:	48 01 c8             	add    %rcx,%rax40056f:	83 c2 01             	add    $0x1,%edx400572:	48 8d 7f 04          	lea    0x4(%rdi),%rdi400576:	39 f2                	cmp    %esi,%edx400578:	7c ef                	jl     400569 <sum+0xc>40057a:	f3 c3                	repz retq 000000000040057c <main>:40057c:	48 83 ec 38          	sub    $0x38,%rsp400580:	c7 04 24 01 00 00 00 	movl   $0x1,(%rsp)400587:	c7 44 24 04 02 00 00 	movl   $0x2,0x4(%rsp)40058e:	00 40058f:	c7 44 24 08 03 00 00 	movl   $0x3,0x8(%rsp)400596:	00 400597:	c7 44 24 0c 04 00 00 	movl   $0x4,0xc(%rsp)40059e:	00 40059f:	c7 44 24 10 05 00 00 	movl   $0x5,0x10(%rsp)4005a6:	00 4005a7:	c7 44 24 14 06 00 00 	movl   $0x6,0x14(%rsp)4005ae:	00 4005af:	c7 44 24 18 07 00 00 	movl   $0x7,0x18(%rsp)4005b6:	00 4005b7:	c7 44 24 1c 08 00 00 	movl   $0x8,0x1c(%rsp)4005be:	00 4005bf:	c7 44 24 20 09 00 00 	movl   $0x9,0x20(%rsp)4005c6:	00 4005c7:	c7 44 24 24 0a 00 00 	movl   $0xa,0x24(%rsp)4005ce:	00 4005cf:	be 0a 00 00 00       	mov    $0xa,%esi4005d4:	48 89 e7             	mov    %rsp,%rdi4005d7:	e8 81 ff ff ff       	callq  40055d <sum>4005dc:	48 89 c2             	mov    %rax,%rdx4005df:	be 84 06 40 00       	mov    $0x400684,%esi4005e4:	bf 01 00 00 00       	mov    $0x1,%edi4005e9:	b8 00 00 00 00       	mov    $0x0,%eax4005ee:	e8 6d fe ff ff       	callq  400460 <__printf_chk@plt>4005f3:	b8 00 00 00 00       	mov    $0x0,%eax4005f8:	48 83 c4 38          	add    $0x38,%rsp4005fc:	c3                   	retq   4005fd:	0f 1f 00             	nopl   (%rax)
    
  • 机器代码~链接
    这是编译器工作的最后一步,将不同的.o文件链接起来,生成完整的可执行文件,在linux系统就是.out文件,在windows系统就是.exe文件。上面的代码经过一系列编译以后,使用命令./test运行程序,显示:
    a=55
    

至此,一个完整的程序结束了,接下来,我们介绍一下计算机是怎么执行程序的

程序的运行

程序的启动是操作系统的责任,但是先别着急双击程序,考虑程序在计算机中是怎么执行的,先必须了解几个概念:

存储器

存储器是能够保存数据的设备,我们熟悉的内存就是一种存储器,我们的程序代码就是保存到内存中的,另外一类存储器叫做通用寄存器,通常保存我们的机器指令执行的临时数据。还有一类存储器叫做状态寄存器,保存我们程序执行的状态

程序计数器

也是一个存储器,保存当前要执行的机器指令在内存中的地址

流程

因为我们只是介绍计算机执行程序的过程,很多涉及组合电路或者逻辑电路的问题我们先不介绍,后面一系列文章我会详细说明,下面看一下程序的运行过程:

  1. 我们双击一个应用程序或者使用命令行运行一个程序
  2. 操作系统的加载器会执行一系列操作
    • 在内存中开辟空间,将我们的程序数据加载到内存
    • 备份之前程序的数据,包括寄存器数据和程序计数器数据
    • 初始化程序运行的环境,比如分配缓冲区等
    • 找到程序的入口点,也就是我们的main函数,把入口位置赋值给程序计数器
  3. 经过操作系统的一系列操作,现在我们程序运行的环境已经具备了,程序计数器已就位,所有寄存器都可用。
  4. 取指:计算机根据程序计数器的值从内存中取出一条指令,然后根据这条指令最前面的操作码来判断当前是一条什么指令,可能是movq指令,或者是addq指令,或者是pushq指令都有可能,但是不同的操作码会决定计算机接下来的操作,也决定了程序计数器距离下一条指令的位置,除了操作码以外,指令还可能包含用到的寄存器编码或者内存位置编码或者常数编码
  5. 译码:我们取指完成后,如果当前指令有寄存器的操作,我们需要通过寄存器的编码拿到寄存器的数值,译码阶段,计算机会根据寄存器编码从寄存器中获取到数值,然后输出到下一阶段。
  6. 执行:执行阶段会执行算术操作或者逻辑操作,这些都是在中央处理器的逻辑/算数处理单元进行的。该操作可能输出一个计算后的数值,也可能修改状态寄存器的值
  7. 访存:该阶段会根据指令类别进行内存的读取或者写入
  8. 写回:该阶段会根据指令的类别修改寄存器的值
  9. 更新PC:该阶段会根据执行结果修改下一条指令的位置,写入程序计数器,至此,一步指令的执行就完成了
  10. 然后再次执行4

需要注意的问题

  1. 上面讲的流程只是最简单的执行,现在处理器使用了很多设计来加速指令的执行,但是上面的流程确实是一个程序在计算机执行的过程
  2. 程序执行过程中可能会需要操作系统的参与,这在指令中叫做中断,比如文件的读写,我们必须通过中断执行操作系统的代码才可以

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/224452.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高分辨率台阶仪,精准掌控细节测量

什么是台阶仪&#xff1f; 台阶仪是一款超精密接触式微观轮廓测量仪&#xff0c;可以对微米和纳米结构进行膜厚和薄膜高度、表面形貌、表面波纹和表面粗糙度等的测量。 什么是台阶仪分辨率&#xff1f; 台阶仪分辨率是指在台阶仪的测量范围内&#xff0c;仪器能够精确分辨出的…

等保2.0的变化

1法律地位得到确认 《中华人民共和国网络安全法》第21条规定“国家实行网络安全等级保护制度”&#xff0c;要求“网络运营者应当按照网络安全等级保护制度要求&#xff0c;履行安全保护义务”&#xff1b;第31条规定“对于国家关键信息基础设施&#xff0c;在网络安全等级保护…

1-SaaS通识

云计算 讲SaaS必须先讲云计算。云计算通过互联网提供计算服务&#xff0c;包括服务器、存储、数据库、网络、应用等&#xff0c;采用按需付费的定价模式。 云计算的4种部署模式 公有云&#xff1a;由云服务商拥有和管理&#xff0c;就好比水电&#xff0c;居民共享&#xff…

外包干了2年,技术退步明显...

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

TaskWeaver:代码优先的代理框架

大型语言模型 (LLM) 在自然语言理解和生成方面表现出了令人印象深刻的能力&#xff0c;导致它们在聊天机器人和虚拟助理等应用中得到使用。然而&#xff0c;现有的法学硕士框架在处理具有丰富数据结构的特定领域数据分析任务时面临局限性。此外&#xff0c;他们还难以灵活地满足…

5.2 Linux FTP 服务

1、概念介绍 FTP&#xff08;File Transfer Protocol:文件传输协议&#xff09;作用Internet 上用来传送文件的协议 FTP Server&#xff08;File Transfer Protocol Server&#xff09;是在互联网/局域网上提供文件存储和访问服务的计算机&#xff0c;它们依照FTP协议提供服务…

Sqoop安装与配置-shell脚本一键安装配置

文章目录 前言一、使用shell脚本一键安装1. 复制脚本2. 增加执行权限3. 执行脚本4. 加载用户环境变量5. 查看是否安装成功 总结 前言 本文介绍了如何使用Shell脚本一键安装Sqoop。Sqoop是一个用于在Apache Hadoop和结构化数据存储&#xff08;如关系数据库&#xff09;之间传输…

优先考虑泛型

Java中的泛型&#xff08;Generics&#xff09;提供了一种参数化类型的机制&#xff0c;使得你可以编写更灵活、类型安全的代码。下面是一个例子&#xff0c;说明在Java中优先考虑泛型的好处&#xff1a; 考虑一个简单的容器类&#xff0c;它可以存储任意类型的元素&#xff0…

【Avue】点击新增再点击表单得radio选项出现新表单,且编辑页面关不掉新表单处理方法

一、问题描述 1、点击新增 2、 点击radio选择值 1、点击否得时候没反应 2、点击是得时候出现新表单 2.1、旧代码 {label: 是否危险源,prop: isBigdanger,searchLabelWidth: 120,overHidden: true,span: 24,rules: [{required: true,message: 请选择是否重大危险源,trigger: bl…

离散事件仿真库SimPy的执行逻辑介绍

文章目录 内容介绍详细执行逻辑分析大致仿真流程Simpy核心类的细节Environment 类Event 类Process类&#xff08;Event&#xff09; 基于案例详细介绍仿真逻辑env.run() 方法逻辑env.process() 方法逻辑 参考文章&#xff1a; SimPy Discrete event simulation for Pythonpyth…

孟德尔随机化+WGCNA+预后模型,7+轻松get

今天给同学们分享一篇生信文章“Exploring the causality and pathogenesis of systemic lupus erythematosus in breast cancer based on Mendelian randomization and transcriptome data analyses”&#xff0c;这篇文章发表在Front Immunol期刊上&#xff0c;影响因子为7.3…

浅显易懂 @JsonIgnore 的作用

1.JsonIgnore作用   在json序列化/反序列化时将java bean中使用了该注解的属性忽略掉 2.这个注解可以用在类/属性上   例如&#xff1a;在返回user对象时&#xff0c;在pwd属性上使用这个注解&#xff0c;返回user对象时会直接去掉pwd这个字段&#xff0c;不管这个属性有没…

FFmpeg的AVOutputFormat

还是和前文一样&#xff0c;先把架构勾勒出来&#xff0c;后期慢慢讲一下代码流程&#xff0c;可以进行各类网络协议的推流&#xff0c;各类容器的封装 结构体 其实就一些回调函数&#xff0c;看到priv_data_size没&#xff0c;这个指向了AVFormatContext的priv_data typedef…

laravel使用ajax登录,和自定义生成验证码

使用larave框架操作ajax发送get请求&#xff0c;和自义定验证码 1. 后端登录代码 <?phpnamespace CriusWeb\FzUserAdmin\Http\Controllers;use App\Models\Admin; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Routing\Controller; use I…

【Idea】SpringBoot项目中,jar包引用冲突异常的排查 / SM2算法中使用bcprov-jdk15to18的报错冲突问题

问题描述以及解决方法&#xff1a; 项目中使用了bcprov-jdk15to18 pom依赖&#xff0c;但是发现代码中引入的版本不正确。 追溯代码发现版本引入的是bcprov-jdk15on&#xff0c;而不是bcprov-jdk15to18&#xff0c;但是我找了半天pom依赖也没有发现有引入bcprov-jdk15on依赖。…

MySQL常见死锁的发生场景以及如何解决

死锁的产生是因为满足了四个条件&#xff1a; 互斥占有且等待不可强占用循环等待 这个网站收集了很多死锁场景 接下来介绍几种常见的死锁发生场景。其中&#xff0c;id 为主键&#xff0c;no&#xff08;学号&#xff09;为二级唯一索引&#xff0c;name&#xff08;姓名&am…

Vue.js 使用基础知识

Vue.js 是一款用于构建用户界面的渐进式框架&#xff0c;它专注于视图层。Vue.js 不同于传统的 JavaScript 框架&#xff0c;它采用了组件化的开发方式&#xff0c;使得开发者可以更加高效和灵活地构建交互式的 Web 应用程序。 目录 什么是 Vue.js安装 Vue.jsVue 实例模板语法插…

bat 脚本的常用特殊符号

1、 命令行回显屏蔽符 2、% 批处理变量引导符 3、> 重定向符 4、>> 重定向符 5、<、>&、<& 重定向符 6、| 命令管道符 7、^ 转义字符 8、& 组合命令 9、&& 组合命令 10、|| 组合命令 11、"" 字符串界定符 12、, 逗号…

csp 如此编码 C语言(回归唠嗑版)

熟悉的开篇废话&#xff0c;最近其实在研究那个web开发这一块&#xff0c;导致csp联系就减少了&#xff0c;好久没更csp的帖子了&#xff0c;尽管明天就要考了&#xff0c;但是嘞&#xff0c;能看一道是一道呗对吧。 等过段时间我把web开发这一块整明白了就发帖子&#xff0c;…

数据库表1和表2对比出差异列 将表1的插入表2

SQLserver2019表1和表2对比出差异列&#xff0c;将表1的插入表2 写成存储过程&#xff0c;传的参为表名 两个表名一致&#xff0c;表结构可能不一致&#xff0c;可能一致&#xff0c;如何快速对比两个表&#xff0c;将需要的字段自动添加至需要的表中 字段大小是一致的吧 -- …