计算机底层知识一——从编程语言到可执行程序
好久没写博客了,近段时间事情比较杂,最近终于有时间回归了。其余代码写久了就会遇到许多奇奇怪怪的问题,这些问题绕不开许多底层知识,比如缺少动态依赖库、idea编译失败等等,虽然通过百度等搜索引擎,亦或是大模型工具可以解决问题,但是总是感觉缺少些什么。偶然间发现一本书,感觉内容还不错,通俗易懂,叫《计算机底层的秘密》。该专栏会根据书中内容整理总结下自己的理解,仅供参考。
1. 引言
编程语言是人类与计算机沟通的桥梁,它让我们能够用接近自然语言的方式来表达我们的想法,并将其转化为计算机可以理解和执行的指令。我们编写的代码是如何变成计算机可执行的程序呢?带着问题慢慢探索吧
2. 编程语言的分类
从是否需要编译上分,分为编译型和解释型两种(其它分法不做叙述)。
解释型语言:Python, JavaScript 等
特点:逐行解释执行,无需编译
优点:开发效率高,跨平台性好
缺点:执行效率相对较低
编译型语言:C, C++, Go 等
特点:需要编译成机器码后才能执行
优点:执行效率高
缺点:开发效率相对较低,平台依赖性较强
特别地,java是混合型语言,两种都涉及。以下我们先对编译型语言做介绍。
3. 主要过程
编译器第一步工作:
- 词法分析(Lexical Analysis):将每个符号切分出来,并将该符号与其附带的信息打包起来。这个包含相应符号信息的东西,有一个名称叫做:token。
- 语法分析:按照语法检测token是否合理,生成相应的语法树;
- 语义分析:在语法树生成后,还需要判断树是否合理,如类型是否一致,通过验证后,就不会出现编译错误了;
- 中间代码生成:语义分析后,编译器遍历语法树并用另一种形式来表示,即中间代码。在一些情况下,还会对上述中间代码进行优化。
- 代码生成:接下来编译器先将中间代码生成为汇编指令,最后编译器将汇编指令转为机器指令(java是字节码),此步骤也称之为汇编
链接器是第二步工作:
链接器分为:
- 符号决议:引用的内容(符号)必须能在其它模块中找到唯一的对应实现。
- 重定位:将原先的代号替换为真实的地址
- 生成可执行文件:所有内容汇总生成可执行文件
符号决议:
链接器关心的是全局变量(包括函数),包括引用外部的自己提供的。
目标文件中有两个部分的内容非常重要:
1)指令部分(来自源文件中定义的所有函数,简称代码区)
2)数据部分(源文件中全局变量,简称数据区,局部变量运行起来后在栈上维护,不出现在目标文件中)
编译器在编译过程中遇到外部定义的全局变量和函数时,只要能找到相应的变量声明即可。但链接器需要确定引用的变量的定义是否存在。
为了方便链接器工作,编译器还多做了一些工作,把一个源文件可以对外提供哪些符号,以及该文件引用了哪些符号都记录了下来,并将该信息放到一张表中,这张表就是符号表。
符号表在哪呢?-编译器将其放到了目标文件中。所以除了代码区和数据区,目标文件还保留了符号表。
以上就是链接器符号决议阶段。唯一的错误是:undefined reference to ‘func’,不会出现编译错误。
静态库、动态库与可执行文件
静态库:代码单独打包,在windows下是.lib,在linux上是以.a为后缀的文件
静态链接:将依赖的静态库复制到目标文件中,不依赖外部代码,加快项目编译速度。
生成的可执行文件也包含代码区和数据区,将各个目标文件的数据区和代码区都合并起来了。此外,可执行文件还包含一个特殊符号_start,CPU从这个地方开始运行。
静态链接缺点:
所有依赖于静态库的都需要重新打包进去,浪费空间;
代码改动,编译困难。
动态库:也包含代码区和数据区等。使用动态库可执行文件仅包含关于所引用的动态库的一些必要信息,如所引用动态库的名字、符号表及重定位信息等,不需要复制所有内容,节省了空间。
引用动态库的可执行文件中,包含的内容更加丰富:
(代码区、数据区及动态链接相关信息)
动态链接有两种可能出现的场景:
1)加载时动态链接
在程序加载时动态链接,这里指的是可执行文件的加载,就是可执行文件从磁盘到内存的过程,系统中有一个专门的程序来实现这个,称之为加载器。
加载器在加载过程中,能够检测到可执行文件是否为动态库,如果是,则启动动态连接器。
动态链接器:确定动态库是否存在,在哪里,以及引用符号的内存位置。如果不存在,会有提示信息。
加载时动态链接需要我们把可执行文件依赖哪些动态库告知编译器,如以下命令:gcc -o pro main.c /path/to/libfoo.so
2)运行时动态链接
在程序开始被CPU执行,到程序完成退出这段时间。程序在运行前根本不知道依赖了哪些动态库。由于生成可执行文件时么有提供所依赖的动态库,这就要求程序员在编写程序时,用特定的API来根据需求动态加载指定动态库。
动态库的优缺点:
优点:节省空间;更新方便;动态扩展功能(插件);多语言混编(对性能要求部分,使用动态库)
缺点:启动或者运行时加载,性能略弱于静态链接-间接寻址;动态库的可执行文件不可以单独执行
重定位:确定符号运行时地址
链接器在生成可执行指令时,必须确定该函数在程序运行时的地址;
问题:如何知晓函数在程序运行时的地址呢?
编译器是不知道确定地址的,只是简单的赋了个值,这为链接器埋了坑。当然,编译器也做了些事:
1)在.relo.text中记录和指令相关的不确定内存地址信息;
2)在.relo.data中记录和数据相关的不确定内存地址信息
编译器的工作完成后,需要链接器将所有目标文件同类型的区合并到一起。当合并到一起后,所有全局变量和机器指令在程序运行时的内存地址就确定了(这里猜测是虚拟地址)。
接下来,链接器逐个扫描各个目标文件中的relo.text段,其中不确定的机器指令都需要修正。
修正符号内存地址的过程即使重定位。
说到这里是不是又很疑惑,程序还没运行,为啥有内存地址,这里就是虚拟内存的作用。
由于每个程序的代码区地址都是从0x400000开始,A,B程序同时运行,如何确定是谁呢?
答案是CPU执行A时,从0x400000取到的就是A的,执行B,同样地址取到就是B的。
这是通过操作系统的虚拟内存技术实现的(虚拟内存是标准的定义的)。通过虚拟内存可以大大简化链接器的设计。
但我们得最终确定可执行程序加载到真实物理内存上运行,系统会记录可执行程序代码区的位置和虚拟内存的关系(由操作系统维护,不是为每个地址维护一份映射,而是以页为单位进行维护)。这种映射关系称为页表。每个进程都有属于自己的页表。
链接器的作用非常重要,参与了可执行文件的生成,是架设编译时与运行时(进程)之间的关键桥梁。
总结下:
- 编译型语言,编译器和链接器都非常重要,链接器虽然名称不显,但蕴含了虚拟内存的密码。
- 编译过程各种编译型语言大同小异,遵循语法规则编写,也遵循语法规则编译
- 通过了编译器后,就不会在后续运行期间出现编译错误了
其它:上述过程不能完全说明编译的整体过程,如一些语言的预处理过程。
相关文章:
计算机底层知识一——从编程语言到可执行程序
好久没写博客了,近段时间事情比较杂,最近终于有时间回归了。其余代码写久了就会遇到许多奇奇怪怪的问题,这些问题绕不开许多底层知识,比如缺少动态依赖库、idea编译失败等等,虽然通过百度等搜索引擎,亦或是…...
中性点直接接地电网接地故障Simulink仿真
1.模型简介 本仿真模型基于MATLAB/Simulink(版本MATLAB 2017Ra)软件。建议采用matlab2017 Ra及以上版本打开。(若需要其他版本可联系代为转换) 2.系统仿真图: 3.中性点直接接地电网接地故障基本概念(本仿…...
解决Jenkins默认终止Shell产生服务进程的问题
1、Windows环境 Jenkins进行Build steps的使用Execute Windows batch command启动微服务(Jar包),Jenkins会默认终止Shell产生的服务进程,而在命令行能够正常运行的服务进程。 1.1 使用命令行启动服务是正常 使用命令行执行 正常…...
Spring Boot 项目中 Redis 常见问题及解决方案
目录 缓存穿透缓存雪崩缓存击穿Redis 连接池耗尽Redis 序列化问题总结 1. 缓存穿透 问题描述 缓存穿透是指查询一个不存在的数据,由于缓存中没有该数据,请求会直接打到数据库上,导致数据库压力过大。 解决方案 缓存空值:即使…...
STM32 I2C驱动开发全解析:从理论到实战 | 零基础入门STM32第五十步
主题内容教学目的/扩展视频I2C总线电路原理,跳线设置,I2C协议分析。驱动程序与调用。熟悉I2C总线协议,熟练调用。 师从洋桃电子,杜洋老师 📑文章目录 引言一、I2C驱动分层架构二、I2C总线驱动代码精析2.1 初始化配置&a…...
RuleOS:区块链开发的“破局者”,开启Web3新纪元
RuleOS:区块链开发的“破冰船”,驶向Web3的星辰大海 在区块链技术的浩瀚宇宙中,一群勇敢的探索者正驾驶着一艘名为RuleOS的“破冰船”,冲破传统开发的冰层,驶向Web3的星辰大海。这艘船,正以一种前所未有的姿…...
manus本地部署使用体验
manus部署 https://github.com/mannaandpoem/OpenManus git clone https://github.com/mannaandpoem/OpenManus.git 或者手工下载zip包解压,包很小,只有几百K。 cd OpenManus-main #创建python环境,有python3的可以用python3 python -m ven…...
OpenCV 拆分、合并图像通道方法及复现
视频讲解 OpenCV 拆分、合并图像通道方法及复现 环境准备:安装 OpenCV 库(pip install opencv-python) 内容: 1. 读取任意图片(支持 jpg/png 等格式) 2. 使用 split () 函数拆解成 3 个单色通道…...
manus本地部署方法研究测试
Manus本地部署方法,Manus邀请码实在太难搞了,昨晚看到有一个团队,5个人3个小时,一个完全免费、无需排队等待的OpenManus就做好了。 由于也是新手,找了好几轮,实在是没有找到合适的部署方法,自己…...
基于Python实现的智能旅游推荐系统(Django)
基于Python实现的智能旅游推荐系统(Django) 开发语言:Python 数据库:MySQL所用到的知识:Django框架工具:pycharm、Navicat 系统功能实现 总体设计 系统实现 系统首页模块 统首页页面主要包括首页,旅游资讯,景点信息…...
C++--迭代器(iterator)介绍---主要介绍vector和string中的迭代器
目录 一、迭代器(iterator)的定义 二、迭代器的类别 三、使用迭代器 3.1 迭代器运算符 3.2 迭代器的简单应用:使用迭代器将string对象的第一个字母改为大写 3.3 将迭代器从一个元素移动到另外一个元素 3.4 迭代器运算 3.5 迭代器的复…...
SpringCloud——Consul服务注册与发现
一、为什么要引入服务注册中心 (1)为什么引入 微服务硬编码 IP / 端口的核心问题总结 环境变更敏感:当支付微服务的 IP 或端口修改时,订单微服务必须同步修改所有调用该支付服务的代码或配置,否则将无法正常通信无法…...
C语言_数据结构总结5:顺序栈
纯C语言代码,不涉及C 想了解链式栈的实现,欢迎查看这篇文章:C语言_数据结构总结6:链式栈-CSDN博客 这里分享插入一下个人觉得很有用的习惯: 1. 就是遇到代码哪里不理解的,你就问豆包,C知道&a…...
人工智能之数学基础:正交矩阵
本文重点 正交矩阵是线性代数中一个重要的特殊矩阵,它在许多领域都有广泛的应用。 什么是正交矩阵 如图所示,当矩阵A满足如上所示的条件的时候,此时我们就可以认为是正交矩阵,需要注意一点矩阵A必为方阵。 正交矩阵的充要条件 …...
前端打包优化相关 Webpack
前端打包优化相关 Webpack 打包时间的优化(基于 Vue CLI 4 Webpack 5) 1. Webpack 配置减少打包时间 1.1 对 JS 配置:排除 node_modules 和 src 中的打包内容 在开发环境下,修改 Webpack 的 JS 规则,排除 /node_m…...
抓包分析工具介绍
什么是抓包分析工具? 抓包分析工具,也称为网络数据包嗅探器或协议分析器,用于捕获和检查网络上传输的数据包。这些数据包包含了网络通信的详细信息,例如请求的资源、服务器的响应、HTTP 头信息、传输的数据内容等等。通过分析这些…...
Linux第一课
一、Linux背景与发展 1. 发展史 1968年,研究人员开发了Multics操作系统,为后续发展奠定了基础。 1969−1970年,Ken Thompson和Dennis Ritchie在Multics基础上开发了UNIX系统。 1991年,Linus Torvalds发布了Linux操作系统&#…...
2025/3/8 第 27 场 蓝桥入门赛 题解
1. 38红包【算法赛】 签到题: 算倍数就行了 #include <bits/stdc.h> using namespace std; int main() {int ans0;for(int i1;i<2025;i){if(i % 3 0)ans;else if(i % 8 0)ans;else if(i % 38 0)ans;}cout<<ans<<endl;return 0; } 2. 祝福…...
使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)
目录 1.安装Node.js和npm2.初始化项目3.安装Ollama4.下载DeepSeek模型5.创建Node.js服务器6.运行服务器7.Web UI对话-Chrome插件-Page Assist 1.安装Node.js和npm 首先确保我们机器上已经安装了Node.js和npm。如果未安装,可以通过以下链接下载并安装适合我们操作系…...
deepseek 3FS编译
3FS在ubuntu22.04下的编译(记录下编译过程,方便后续使用) 环境信息 OS ubuntu 22.04内核版本 6.8.0-52-genericlibfuse 3.16.1rust 1.75.0FoundationDB 7.1.66meson 1.0.0ninja 1.10.1 libfuse编译 以下建议均在root下执行 pip3 install…...
网安知识点
1.SQL注入漏洞产生的原因是? 前端传到后端的数据,没有经过任何处理,直接当作sql语句的一部分来执行 2.讲一下sql注入,写入webshell需要哪些前提条件 开启导入导出权限secure-file-priv 站点根目录位置/路径 mysql用户对站点根目…...
UniApp 运行的微信小程序如何进行深度优化
UniApp 运行的微信小程序如何进行深度优化 目录 引言性能优化 1. 减少包体积2. 优化页面加载速度3. 减少 setData 调用4. 使用分包加载 代码优化 1. 减少不必要的代码2. 使用条件编译3. 优化图片资源 用户体验优化 1. 优化交互体验2. 预加载数据3. 使用骨架屏 调试与监控 1. …...
leetcode-sql数据库面试题冲刺(高频SQL五十题)
题目: 577.员工奖金 表:Employee -------------------- | Column Name | Type | -------------------- | empId | int | | name | varchar | | supervisor | int | | salary | int | -------------------- empId 是该表中具有唯一值的列。 该表的每一行…...
图片分类实战:食物分类问题(含半监督)
食物分类问题 simple_class 1. 导入必要的库和模块 import random import torch import torch.nn as nn import numpy as np import os from PIL import Image #读取图片数据 from torch.utils.data import Dataset, DataLoader from tqdm import tqdm from torchvision impo…...
Java初级入门学习
JAVA学习 @[TOC](JAVA学习)**一、Java初级入门学习路径****1. Java基础语法****2. 面向对象编程(OOP)****3. 数据库与JDBC****4. Java Web基础****二、主流框架推荐与学习建议****1. Spring框架****2. Spring MVC****3. MyBatis****4. Spring Boot****三、后续学习建议****1.…...
每日一练之移除链表元素
题目: 画图解析: 方法:双指针 解答代码(注:解答代码带解析): //题目给的结构体 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* }…...
BM25原理概述
1️⃣设定:对于查询 Q { q 1 , q 2 , . . . , q n } Q\text{}\{q_1,q_2,...,q_n\} Q{q1,q2,...,qn}和段落集 P { P ( 1 ) , P ( 2 ) , … , P ( N ) } \mathscr{P}\text{}\left\{P^{(1)},P^{(2)},\ldots,P^{(\text{N})}\right\} P{P(1),P(2),…,P(N)}&#…...
PAT乙级真题(2014·冬)
大纲 1031、查验身份证-(解析)-简单题 1032、挖掘机技术哪家强-(解析)-细节题(┬┬﹏┬┬),太抠细节了 1033、旧键盘打字-(解析)-输入格式!这才是重点(┬┬﹏┬┬),让…...
力大砖飞,纯暴力搜索——蓝桥p2110(写着玩的)
#include<bits/stdc.h>const int N1000000;using namespace std;bool mp[2][N];int cnt0; int n;void dfs(int row,int col){cntcnt%1000000007;if(coln && row2){cnt;return ;}if(row>2){ //下一列 dfs(0,col1);return;}if(mp[row][col]1){ //下一行 dfs(row…...
如何计算两个向量的余弦相似度
参考笔记: https://zhuanlan.zhihu.com/p/677639498 日常学习之:如何计算两个向量或者矩阵的余弦相似度-CSDN博客 1.余弦相似度定理 百度的解释:余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估…...
