程序员的自我修养-内存

程序员的自我修养-内存

内存布局

Linux的进程内存布局如下图,栈往下生长,堆往上生长

一个典型的栈结构如下

 

反编译程序

假设一段函数如下

int foo() {
    return 123;
}

反编译后的结果图如下:

整个执行逻辑如下

  1. 先保存rbp寄存器,因为rbp,rsp是指向同样位置的,所以push rbp,再将rbp赋给rsp
  2. 开辟一块新空间,也就是 sub rsp 0xC0H,因为栈是往下生长的所以要减
  3. 保存寄存器,rbx,rsi,rdi,这一步是可选的
  4. 加入一些调试信息
  5. 将返回值赋给rax,这步才是函数中真正的逻辑
  6. 将保存的寄存器还原,也就是pop rdi,pop rsi等
  7. 恢复rbp,rsp
  8. ret返回函数的值

多个函数调用的关系栈图

 

对于函数返回一个很大的值,比如几百字节,超过了寄存器容量,参考下面这个例子


typedef struct big_thing {
    char buf[128];
}big_thing;
 
 
big_thing return_test() {
    big_thing b;
    b.buf[0] = 0;
    return b;
}
 
 
int main() {
    big_thing n = return_test();
}
                                   

main函数的反汇编如下:

大致思路是

  1. main函数在栈上额外开辟了一篇空间,将这块空间的一部分作为传递返回值的临时对象比如temp
  2. 将temp对象的地址作为隐藏参数传递给return_test函数
  3. return_test函数将数据拷贝给temp对象,并将temp对象的地址用rax传递出来
  4. return_test返回之后,main函数将rax指向的temp对象内容拷贝给n
void return_test(void* temp) {
    big_thing b;
    b.buf[0] = 0;
    memcpy(temp, &b);
    rax = temp;
}
 
 
int main() {
    big_thing temp;
    big_thing n;
    return_test(&temp);
    memcpy(&n,rax,sizeof(big_thing));
}
                                          

传递流程如下:

 

内存分配算法

Linux的堆内存申请需要系统调用,从性能上来说频繁的调用系统函数获取内存不好
实际的做法是有个应用程序级别的管理程序,每次需要内存就找个代理程序
这个代理程序会一次性向操作系统批量申请一批内存,然后管理释放内存,等不够了再找系统申请

int brk(void* end_data_segment)
//glic中还有一个sbrk,是对brk的包装,可以传入负数

mmap函数最早是最为映射到某个文件的,当它不映射到某个文件时,这个空间快就是匿名的,就可以用来作为堆使用

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
//前面两个参数用于申请空间的起始地址和长度
//prot和flag用于设置申请的空间权限(可读,可写,可执行)以及映射类型(文件映射,匿名空间)
//最后两个用于文件映射时指定文件描述符和文件偏移量
直接用mmap实现malloc功能
void *malloc(size_t nbytes) {
    void* ret = mmap(0, nbytes, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if(ret == MAP_FAILED) {
        return 0;
    }
    return ret;
}
                                                                                          

堆分配算法

1.空闲链接方式
2.位图,用bit标识一块内存是否被分配,但会出现很多碎片问题
3.对象池
对象池可以使用空闲链表也可以使用对象池
对于glic来说,申请小于64字节的空闲是使用对象池的方式,而大于12字节是最佳适配算法,大于128K的申请会使用mmap机制

空闲表分配方式

位图分配方式

 

参考

每个程序员都应该了解的内存知识
《程序员的自我修养-链接,加载和库》

 

0 次阅读

发表评论

电子邮件地址不会被公开。 必填项已用*标注