我试图更深入地了解编程语言的低级操作是如何工作的,尤其是它们如何与OS/CPU交互.我可能已经在Stack Overflow上的每个堆栈/堆相关线程中阅读了每个答案,并且它们都很棒.但还有一件事我还没有完全理解.
在伪代码中考虑这个函数,这往往是有效的Rust代码;-)
fn foo() {
let a = 1;
let b = 2;
let c = 3;
let d = 4;
// line X
doSomething(a, b);
doAnotherThing(c, d);
}
Run Code Online (Sandbox Code Playgroud)
这就是我假设堆栈在X行上的样子:
Stack
a +-------------+
| 1 |
b +-------------+
| 2 |
c +-------------+
| 3 |
d +-------------+
| 4 |
+-------------+
Run Code Online (Sandbox Code Playgroud)
现在,我读到的关于堆栈如何工作的一切都是它严格遵守LIFO规则(后进先出).就像.NET,Java或任何其他编程语言中的堆栈数据类型一样.
但如果是这样,那么在X行之后会发生什么?显然,接下来我们需要的是使用a和b,但这意味着操作系统/ CPU(?)必须弹出d并c首先回到a和b.但是它会在脚下射击,因为它需要c并且d在下一行.
所以,我想知道幕后究竟发生了什么?
另一个相关问题.考虑我们传递对其他函数的引用,如下所示:
fn foo() {
let …Run Code Online (Sandbox Code Playgroud) 在低级裸机嵌入式上下文中,我想在内存中创建一个空格,在C++结构中并且没有任何名称,以禁止用户访问这样的内存位置.
现在,我已经通过设置一个丑陋的uint32_t :96;位域来实现它,它将方便地取代三个单词,但它会从GCC发出警告(Bitfield太大而不适合uint32_t),这是非常合理的.
虽然它工作正常,但是当你想要分发一个包含数百个警告的库时它不是很干净......
我该怎么做呢?
我正在研究的项目包括定义整个微控制器线路(STMicroelectronics STM32)的不同外设的存储器结构.为此,结果是一个类,它包含几个结构的并集,这些结构定义了所有寄存器,具体取决于目标微控制器.
一个非常简单的外设的一个简单示例如下:通用输入/输出(GPIO)
union
{
struct
{
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
};
struct
{
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
uint32_t :32;
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
uint32_t :64;
};
struct
{
uint32_t :192;
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
uint32_t :160;
};
};
Run Code Online (Sandbox Code Playgroud)
所有GPIO_MAPx_YYY都是宏,定义为uint32_t :32或寄存器类型(专用结构).
在这里,您可以看到uint32_t :192;哪个效果很好,但它会触发警告.
我可能已经用几个替换它uint32_t :32;(这里有6个),但我有一些极端情况,我有uint32_t :1344;(42)(其中).所以我宁愿不在8k其他行上添加大约100行,即使结构生成是脚本化的.
确切的警告信息是这样的:(
width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' …
我对那里的所有核心低级黑客都有疑问.我在博客中遇到了这句话.我并不认为这些来源很重要(如果你真的在意,那就是Haack),因为它似乎是一个常见的陈述.
例如,许多现代3D游戏都有他们用C++和Assembly编写的高性能核心引擎.
就程序集而言 - 是用汇编编写的代码,因为你不希望编译器发出额外的指令或使用过多的字节,或者你使用的是更好的算法,你无法在C中表达(或者没有表达编译器把它们搞砸了)?
我完全明白了解低级别的东西很重要.我只想在你理解它之后理解为什么程序在汇编.
我不想优化任何东西,我发誓,我只想出于好奇而问这个问题.我知道,在大多数硬件有位移(例如的组件的命令shl,shr),它是一个命令.但是,你转移了多少比特(纳秒级,或CPU技巧)是否重要?换句话说,在任何CPU上是否更快?
x << 1;
Run Code Online (Sandbox Code Playgroud)
和
x << 10;
Run Code Online (Sandbox Code Playgroud)
请不要因为这个问题而恨我.:)
任何使用位域的可移植代码似乎都区分了小端和大端平台.有关此类代码的示例,请参阅linux内核中struct iphdr的声明.我无法理解为什么位字节序是一个问题.
据我所知,bitfields纯粹是编译器构造,用于促进位级操作.
例如,考虑以下位域:
只是一种简洁易懂的说法
struct ParsedInt {
unsigned int f1:1;
unsigned int f2:3;
unsigned int f3:4;
};
uint8_t i;
struct ParsedInt *d = &i;
d->f2.
但是,无论架构如何,位操作都是明确定义的并且可以正常工作.那么,bitfields怎么不便携?
我真的很想学习装配.我非常擅长c/c ++,但希望更好地了解较低级别的内容.
我意识到之前已经提出了与装配相关的问题,但我只是在寻找一些特定于我的情况的方向:
我正在运行Windows 7,我对如何开始使用汇编感到困惑.我是否必须从x64开始,因为我正在运行Windows 7?有些人说"先从32位开始" - 我该如何做呢?我的操作系统与我为'32'或'64'位写入汇编的能力有什么关系.事实上,'n位'汇编意味着什么,其中n是一个数字?
编辑:
以下是一些帮助我开始组装的链接; 刚刚入门的其他人可能会发现它们很有帮助.随着我继续组装之旅,我将继续更新此列表:)
注意:正如我一直在学习的那样,我决定专注于使用masm32进行编程.因此,以下大多数资源都集中于此.
Agner Fog的软件优化资源,包括在不同平台(Windows与Linux/OS X)上调用约定的一些好东西,以及如何有效地执行特定事务的大量示例.不适合初学者,但对于中级到高级读者来说非常棒.
(他还为英特尔和AMD CPU的每条指令提供了详细的性能信息,非常适合严格的性能微优化.一些初学者可能想要了解其中的一些内容,开始考虑CPU的工作原理,以及为什么你可以做一些事情方式而不是另一种方式.)
创建操作系统似乎是一个庞大的项目.怎么会有人开始?
例如,当我将Ubuntu弹出到我的驱动器中时,我的计算机如何才能运行它? (我想,这是我真正想知道的.)
或者,从另一个角度来看,磁盘上可能存在的字节数最少,仍然可以作为操作系统"运行"?
(我很抱歉,如果这是模糊的.我只是不知道这个主题,所以我不能非常具体.我假装知道关于计算机如何工作的相当数量,但我对这个主题完全无能为力. )
为什么,在硬件执行操作的最低级别和所涉及的一般底层操作(即:运行代码时所有编程语言的实际实现的一般性),矢量化通常比循环更快?
当使用矢量化时,计算机在循环时做了什么(我说的是计算机执行的实际计算,而不是程序员编写的计算),或者它有什么不同的做法?
我一直无法说服自己为什么差异应该如此重要.我可能会说服矢量化代码在某处削减一些循环开销,但计算机仍然必须执行相同数量的操作,不是吗?例如,如果我们将大小为N的向量乘以标量,我们将使用N次乘法执行任一方式,不是吗?
是否有可能通过使用纯位加法,减法除以10的无符号整数,也许繁衍?使用资源非常有限且速度慢的处理器.
我读了以下声明:
x86架构包括称为任务状态段(TSS)的特定段类型,用于存储硬件上下文.尽管Linux不使用硬件上下文切换,但它仍然被迫为系统中的每个不同CPU设置TSS.
我想知道:
最后一如既往,感谢您的耐心和回复.
- - - - - -添加 - - - - - - -
http://wiki.osdev.org/Context_Switching得到了一些解释.
像我这样迷茫的人可以看看它.8 ^)
low-level ×10
c ×4
assembly ×3
performance ×3
c++ ×2
cpu ×2
x86 ×2
bare-metal ×1
bit ×1
bit-fields ×1
callstack ×1
linux ×1
linux-kernel ×1
math ×1
osdev ×1
portability ×1
x86-64 ×1