并且如何知道哪一块内存大小是可以读可以写,以避免出现下面程序的 Segmentation Fault 呢?
#include<stdio.h>
int main(){
int *p = (int *)0x1;
printf("%p\n", p);
*p = 1; // segmentation fault
printf("%d", *p); // segmentation fault
}
1
iOCZS 276 天前
是虚拟的,每个进程都有自己的虚拟地址空间,范围从 0x000'0000000 到 0x7FF'FFFFFFFF 。
内存里放着很多的段,像代码段被设置为只读的话,你去写入就会段错误。 |
2
proxytoworld 276 天前
有 api 读取内存的属性
|
3
llh880808 276 天前
1. c 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?
是的,应用程序看到的地址都是虚拟地址 2. 要怎么知道 OS 给这个 c 程序进程分配的虚拟地址的大小呢? 每一个应用都会分配到整个物理内存大小的虚拟内存 3. 如何知道哪一块内存大小是可以读可以写? 使用更高特权等级(默认的 user 等级应该是无权访问的),查询 PMP 相关配置可以拿到内存地址的读写权限 |
4
BeiChuanAlex 276 天前
你有没有想过把这个转成汇编,汇编基本上一目了然
|
5
yanqiyu 276 天前 1
@llh880808 #3
> 每一个应用都会分配到整个物理内存大小的虚拟内存 并不准确,分配的空间大小和物理内存大小没关系。精简点的程序的用户地址空间甚至可能就只有程序和动态库的映射+自己的栈,花哨点的可以上来就要 TB 量级的匿名 mmap 自己分配。 > 使用更高特权等级(默认的 user 等级应该是无权访问的),查询 PMP 相关配置可以拿到内存地址的读写权限 其实可以读自己的 maps 文件/smaps 文件来查询(怎么看怎么不清真) 或者想办法直接干了然后捕获 SIGSEGV 就知道是不是可读可写了... |
6
yanqiyu 276 天前
@BeiChuanAlex 这和汇编没关系,虚拟地址分配,虚拟地址到物理地址的映射和 segmentation fault 都发生在 mmu 和 OS 内部,对你的程序是透明的
|
7
gcl123 276 天前
进入 x86 实模式,就能打出实际内存地址了
|
8
chouxw112233 276 天前
1. 虚拟地址
2. 不知道,否则函数传递数组,被退化指针,也不需要额外再传一个 size 了 3. 不是你申请出来的,就是不可以读写的,即使读写不出现 segfault ,也会很危险 |
9
leonshaw 276 天前
linux 的话读 procfs 里的 maps
|
10
dangyuluo 275 天前
可以通过读某个寄存器的值再进行对比来获得 stack 已用的大小,至于 heap 的大小的话忘了,用`brk`和`sbrk`的值来判断?
|
11
geelaw 275 天前 via iPhone 1
> C 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?
不是,它打印的是这个指针的整数表示。没有要求指针的整数表示必须等于操作系统级别的虚拟地址。当然,最常见的实现里,指针的整数表示就是它指向的对象的内存位置的虚拟地址。 > 要怎么知道 OS 给这个 C 程序进程分配的虚拟地址的大小呢? 操作系统不给“C 程序”分配虚拟地址,“虚拟地址”的大小可能也不是你想要的那个答案。操作系统为每个进程提供虚拟内存,每个程序都以为自己占有全部寻址空间的虚拟内存,虚拟地址的大小和处理器有关。 有意义的问题是:一个进程如何知道自己的虚拟内存里有多少页是可读的、可写的、可执行的?这个问题无法从 C 语言的抽象层级回答,你需要操作系统的 API 。 > 并且如何知道哪一块内存大小是可以读可以写,以避免出现下面程序的 Segmentation Fault 呢? 在 C 语言看来,只有 malloc 等分配得到且未 free 、realloc 等的、取静态存储期对象的地址得到的、取自动存储期对象地址得到且该对象还未离开作用域的指针,以及它进行有定义算术运算得到的指针,才是有效的指针。解引用无效指针的效果是未定义行为,段错误仅仅是它的一种表现方式。 当然,结合操作系统 API 之后有更多获得有效指针的方式。但楼主本来的这个问题意义不明——如果你不知道一个指针怎么来的,使用它有什么意义呢?如果你知道一个指针怎么来的,那你当然知道这个指针是否有效。 |
12
MeePawn666 275 天前 via Android
搜索 MMU ,你就都明白了
|
13
dhb233 275 天前
如果是要好好写程序,指针就不应该随便乱用啊。。。指针只应该指向你知道大小的内存,并且是在编码阶段就能明确内存大小的内存。比如 malloc 的一块内存,你申请的时候是知道有多大的;栈上的一个变量,你应该知道变量的 sizeof ;一个给定长度的数组,最大就是数组的 size ;无论哪种,你在写代码的时候,都要知道这块内存有多大。
同样的,传递指针的时候,要么是一个结构体的指针,要么一定加一个长度参数。 你非要用一些骚操作来解决,可以直接捕获段错误的信号啊,没有段错误就是没越界。但是这个也仅代表你没有越分配内存的界。如果越界写到了其他数据结构,那还是没办法。 |
14
sbldehanhan 275 天前
你需要内存就向操作系统申请一片内存,它会告诉你可用的内存地址,使用完就把它释放掉,至于哪片内存可用,哪片不可用,那是操作系统的事。如果这件事由程序员自己做,那要操作系统干啥?再说,你管理的过来吗?到时候程序不得走一步崩两次?
|
15
4king 275 天前 via Android
pmap 可以看进程内存分布,得到虚拟地址对照看就行
|
16
heguangyu5 275 天前
过一遍一个可执行文件(ELF)是怎么被操作系统(linux kernel)加载并执行的,就很清楚了.
http://heguangyu5.github.io/my-linux/html/20-init_post.html |
17
PTLin 275 天前
给你解释一下为什么你的代码会发生 segmentation fault ,以加强你对整个体系的认识。
在 Linux 中,地址空间会被分成一系列的段,例如映射到可执行文件段,映射到共享库的段,匿名映射(通常被用于堆)的段,这一系列段由叫 vma 结构的集合组成。可以从 proc 文件系统对应进程号的 maps 文件看到。 对于 x86 来讲虚拟地址会通过页表进行地址映射。倘若在页表里地址对应的条目不存在将会引发 page_fault 中断。 在中断的处理过程内,由于你的地址 0x1 是用户地址所以跳转到了处理用户地址的函数 do_user_addr_fault 。 这个函数会查找这个地址是否属于某个 vma ,然而没有查询到,所以调用了 bad_area_nosemaphore 向这个进程发送了 SIGSEGV 信号。 |