16 条回复  ·  191 次点击
geelaw 小成 2024-3-21 04:22:27
> C 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?

不是,它打印的是这个指针的整数表示。没有要求指针的整数表示必须等于操作系统级别的虚拟地址。当然,最常见的实现里,指针的整数表示就是它指向的对象的内存位置的虚拟地址。

> 要怎么知道 OS 给这个 C 程序进程分配的虚拟地址的大小呢?

操作系统不给“C 程序”分配虚拟地址,“虚拟地址”的大小可能也不是你想要的那个答案。操作系统为每个进程提供虚拟内存,每个程序都以为自己占有全部寻址空间的虚拟内存,虚拟地址的大小和处理器有关。

有意义的问题是:一个进程如何知道自己的虚拟内存里有多少页是可读的、可写的、可执行的?这个问题无法从 C 语言的抽象层级回答,你需要操作系统的 API 。

> 并且如何知道哪一块内存大小是可以读可以写,以避免出现下面程序的 Segmentation Fault 呢?

在 C 语言看来,只有 malloc 等分配得到且未 free 、realloc 等的、取静态存储期对象的地址得到的、取自动存储期对象地址得到且该对象还未离开作用域的指针,以及它进行有定义算术运算得到的指针,才是有效的指针。解引用无效指针的效果是未定义行为,段错误仅仅是它的一种表现方式。

当然,结合操作系统 API 之后有更多获得有效指针的方式。但楼主本来的这个问题意义不明——如果你不知道一个指针怎么来的,使用它有什么意义呢?如果你知道一个指针怎么来的,那你当然知道这个指针是否有效。
MeePawn666 小成 2024-3-21 04:23:35
搜索 MMU ,你就都明白了
dhb233 小成 2024-3-21 10:10:19
如果是要好好写程序,指针就不应该随便乱用啊。。。指针只应该指向你知道大小的内存,并且是在编码阶段就能明确内存大小的内存。比如 malloc 的一块内存,你申请的时候是知道有多大的;栈上的一个变量,你应该知道变量的 sizeof ;一个给定长度的数组,最大就是数组的 size ;无论哪种,你在写代码的时候,都要知道这块内存有多大。
同样的,传递指针的时候,要么是一个结构体的指针,要么一定加一个长度参数。

你非要用一些骚操作来解决,可以直接捕获段错误的信号啊,没有段错误就是没越界。但是这个也仅代表你没有越分配内存的界。如果越界写到了其他数据结构,那还是没办法。
sbldehanhan 小成 2024-3-21 10:29:56
你需要内存就向操作系统申请一片内存,它会告诉你可用的内存地址,使用完就把它释放掉,至于哪片内存可用,哪片不可用,那是操作系统的事。如果这件事由程序员自己做,那要操作系统干啥?再说,你管理的过来吗?到时候程序不得走一步崩两次?
4king 小成 2024-3-21 10:45:30
pmap 可以看进程内存分布,得到虚拟地址对照看就行
heguangyu5 小成 2024-3-21 10:54:30
过一遍一个可执行文件(ELF)是怎么被操作系统(linux kernel)加载并执行的,就很清楚了.

http://heguangyu5.github.io/my-linux/html/20-init_post.html
PTLin 小成 2024-3-21 10:55:32
给你解释一下为什么你的代码会发生 segmentation fault ,以加强你对整个体系的认识。

在 Linux 中,地址空间会被分成一系列的段,例如映射到可执行文件段,映射到共享库的段,匿名映射(通常被用于堆)的段,这一系列段由叫 vma 结构的集合组成。可以从 proc 文件系统对应进程号的 maps 文件看到。

对于 x86 来讲虚拟地址会通过页表进行地址映射。倘若在页表里地址对应的条目不存在将会引发 page_fault 中断。
在中断的处理过程内,由于你的地址 0x1 是用户地址所以跳转到了处理用户地址的函数 do_user_addr_fault 。

这个函数会查找这个地址是否属于某个 vma ,然而没有查询到,所以调用了 bad_area_nosemaphore 向这个进程发送了 SIGSEGV 信号。
12
返回顶部