前言

最近看了<现代操作系统>, 从内存管理那一章中获得了一些东西: 分页和分段

其中分页的概念让我对内存的管理有了更加清晰的认知

 

什么是分页?

就是将内存分为一个个小的页面(以4k为例).

在将磁盘数据加载进内存时, 以页为单位, 而将内存中的数据换出到磁盘中时, 也以页为单位.

对此, 一个具体的虚拟地址可以分为两部分:

其中, 页号会被替换, 而地址会被保留:

真实的地址存放到进程虚拟地址映射表中

emmm... 也就是说, 虚拟地址中, 有部分的地址是真实的

(如果我能知道那部分假的地址, 是否就有办法操作真实磁盘中的数据呢? 我突然有了不太好的想法 :) )

 

指针中是否会保留原始变量的地址?

指针中存放了数据, 而这样的数据能够找到指针所指向的变量

那么, 这样的数据是什么呢? 最直接的, 那么应该是地址, 考虑以下程序:

15打印的是指针本身内存中所指向的东西, 将它与变量i的地址做以下对比, 会相同么? 以下是输出结果:

i的地址是 0x7fff12ca4efc, 而p中保存的值(我将它解释为整数)是: 315248380

整型数字不怎么直观, 将它转为hex试试? => 0x12ca4efc

有没有觉得熟悉? 0x7fff12ca4efc ----- 0x12ca4efc

除前面的 0x7fff, 后面的数字是一样的, 所以我们可以说指针保存了变量的地址, 但是并不准确

那么 0x7fff 就是那个页号么? 指针中只会存放真是地址? 好像不那么对...

emmm... 好像可以继续尝试, 因为当前环境是32位的, 这个地址数字明显超出了32位的表现范围

(我居然忽略了这一点 = =...)

或许我能获得更多的数据? 在代码中加入了下面一行:

得到了数据: 32767 => 0x7fff

所以指针中直接保存了变量的地址(我们之前并没有拿到完全的数据, 地址超过了int的大小)

指针就是地址

emmm... 好像这个笔记不是那么有意义做了...

我是不是太慢了? 这些东西应该是初学者就可以去钻研的内容

(@btw: 为什么是[1] 而不是 [-1])

 

是否有办法知道哪些数据是与页号有关的?

emmm... 那些东西与内核有关, 我现在没有办法获得

 

其他的耦合知识: volatile

在书中我还读到了一个非常有趣的知识, 那就是进程表项有一个是否缓存标志位

这个标志位的意思是, 如果该位是1, 表示该页不被缓存

意思是什么呢? 如果要访问的数据是在该页中的, 那么访问时会去访问磁盘

而写入的时候, 也会直接往磁盘中写入, 因为内存中不缓存该页的数据

仔细想想和什么东西有关? 嗯, c++的 volatile 关键字

我打赌, volatile的实现一定与这东西有关(至少实现类似) 不过我现在的能力暂时无法证实

同时还有一个疑问, volatile 修饰一个变量, 而一个页是4k的, 如何将这两个东西分开呢?

 

为什么是[1] 而不是 [-1]

上面的例子中, 我继续访问数据, 使用的是 [1] 而不是 [-1]

我一开始是用 [-1], 因为栈是往下增长的, 之后发现 [1] 是正确的, 为什么?

的确实际上应该是 [1], 不然局部数组的访问就要乱套了

但是栈的确是向下增长的, 从 fc 和 f0 中可以看出来

那么底层到底对我的代码做了什么? 或许可以从汇编中得到答案

那么了解了, 虽然栈是往下移动的, 分配栈帧时也是往下移动的

但是 [1] 这种位移时, 是往上移动的, 也就是往高地址移动的

因为变量本身的地址开始是在栈的低地址, 是往上移动的

没有问题, 是正常的 :)

但是我又发现一个问题, 这里指针所占用的空间为8字节!

不过又想了一下, 这好像也正常的, 毕竟64位嘛...

 

引用呢? 引用又是什么样子的呢?

时隔几天, 突然想到了这个东西, 引用又是什么样子的呢? 它和指针实质的区别?

参考以下代码:

emmm... 这很简单, 分别打印地址和值

我们来看看汇编:

引用和指针并无关键性的区别, 引用也会占用内存(废话 (눈_눈), 不过我记得培训时有个沙雕老师说不占)

当使用引用的值时, 它是像指针一样使用

而当对引用取地址时, 它是直接拿存储的数据, 而并非用存储的数据去寻址

(这应当是编译器的规定, 它这么编译了引用)

唯一不同是, 引用占了8字节, 这很合理, 但是为什么指针是12字节

(这是一个我在之前忽视了的点, 我曾看过那4字节中是什么, 结果是 0

(用指针的时候, 也用的是 movq, 这意味着只使用了 64 位, 即 8 字节, 为什么中空了 4 字节?)

或许我可以再试试赋值的时候, 引用和指针的不同之处

汇编:

也就是说, 赋值是一样的, 嗯, 完全一样

 

指针和引用的安全性

还记得为什么引用比指针安全么? 因为对于引用是像值一样去使用它, 它仅仅是别名

(其实不是别名, 如你所见, 有些时候访问引用其实还是访问的是引用所占的内存)

它不会出现意外的 delete, 因为管理了它本身数据的访问, 也不会出现一些指针原有的错误(空指针, 野指针...)

我更倾向于: 引用是一个加了顶层const的非空, 不可用于delete的指针

 

summary

引用和指针的本质都是地址

 

题外话: 尝试篡改引用指向的对象

等等, 引用的内存也是在栈中的, 虽然 c++ 不让我用光明正大的方式修改它

但是, 既然是在栈中的数据, 那么, 我应该是可以改的, 那么就来试一下

汇编就不用看了, 因为这个程序就是根据自己脑补汇编中的样子来编写的

同理, 常量, 常量指针, 这些东西只要绕过编译器设的障碍就可以修改 (突然感受到了指针的魅力)

注: 经测试, 代码在 4.4 版本下的编译器可以, 而 4.8 版本的就不行