V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rookiemaster
V2EX  ›  C

c 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?要怎么知道 OS 给这个 c 程序进程分配的虚拟地址的大小呢?

  •  
  •   rookiemaster · 281 天前 · 2428 次点击
    这是一个创建于 281 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    #include<stdio.h>
    int main(){
    	int *p = (int *)0x1;
        printf("%p\n", p);
        *p = 1;           // segmentation fault
        printf("%d", *p); // segmentation fault
    }
    
    
    17 条回复    2024-03-21 10:55:32 +08:00
    iOCZS
        1
    iOCZS  
       281 天前
    是虚拟的,每个进程都有自己的虚拟地址空间,范围从 0x000'0000000 到 0x7FF'FFFFFFFF 。
    内存里放着很多的段,像代码段被设置为只读的话,你去写入就会段错误。
    proxytoworld
        2
    proxytoworld  
       281 天前
    有 api 读取内存的属性
    llh880808
        3
    llh880808  
       281 天前
    1. c 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?
    是的,应用程序看到的地址都是虚拟地址
    2. 要怎么知道 OS 给这个 c 程序进程分配的虚拟地址的大小呢?
    每一个应用都会分配到整个物理内存大小的虚拟内存
    3. 如何知道哪一块内存大小是可以读可以写?
    使用更高特权等级(默认的 user 等级应该是无权访问的),查询 PMP 相关配置可以拿到内存地址的读写权限
    BeiChuanAlex
        4
    BeiChuanAlex  
       281 天前
    你有没有想过把这个转成汇编,汇编基本上一目了然
    yanqiyu
        5
    yanqiyu  
       281 天前   ❤️ 1
    @llh880808 #3
    > 每一个应用都会分配到整个物理内存大小的虚拟内存
    并不准确,分配的空间大小和物理内存大小没关系。精简点的程序的用户地址空间甚至可能就只有程序和动态库的映射+自己的栈,花哨点的可以上来就要 TB 量级的匿名 mmap 自己分配。

    > 使用更高特权等级(默认的 user 等级应该是无权访问的),查询 PMP 相关配置可以拿到内存地址的读写权限
    其实可以读自己的 maps 文件/smaps 文件来查询(怎么看怎么不清真)
    或者想办法直接干了然后捕获 SIGSEGV 就知道是不是可读可写了...
    yanqiyu
        6
    yanqiyu  
       281 天前
    @BeiChuanAlex 这和汇编没关系,虚拟地址分配,虚拟地址到物理地址的映射和 segmentation fault 都发生在 mmu 和 OS 内部,对你的程序是透明的
    gcl123
        7
    gcl123  
       281 天前
    进入 x86 实模式,就能打出实际内存地址了
    chouxw112233
        8
    chouxw112233  
       281 天前
    1. 虚拟地址
    2. 不知道,否则函数传递数组,被退化指针,也不需要额外再传一个 size 了
    3. 不是你申请出来的,就是不可以读写的,即使读写不出现 segfault ,也会很危险
    leonshaw
        9
    leonshaw  
       281 天前
    linux 的话读 procfs 里的 maps
    dangyuluo
        10
    dangyuluo  
       281 天前
    可以通过读某个寄存器的值再进行对比来获得 stack 已用的大小,至于 heap 的大小的话忘了,用`brk`和`sbrk`的值来判断?
    geelaw
        11
    geelaw  
       281 天前 via iPhone   ❤️ 1
    > C 语言中打印指针的值打印的是 OS 分配的虚拟地址的值吗?

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

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

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

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

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

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

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

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

    http://heguangyu5.github.io/my-linux/html/20-init_post.html
    PTLin
        17
    PTLin  
       281 天前
    给你解释一下为什么你的代码会发生 segmentation fault ,以加强你对整个体系的认识。

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

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

    这个函数会查找这个地址是否属于某个 vma ,然而没有查询到,所以调用了 bad_area_nosemaphore 向这个进程发送了 SIGSEGV 信号。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5438 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 08:40 · PVG 16:40 · LAX 00:40 · JFK 03:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.