write code

任何类型的指针变量都可以用来存储内存地址,通常用u_char *表示一个内存地址。

(void*)-1是将有符号整数-1强制转换为指向void类型的指针,这个指针的值为-1,它通常用于表示一个无效的指针或错误指针。

malloc

标准C库中的malloc函数维护了一张内存块链表(或者堆)来管理动态内存分配和释放。当程序调用malloc函数申请内存时,malloc会在链表中查找大小合适的空闲内存块,如果找到则使用该内存块;否则就会向操作系统请求更多的内存,并把这个新申请的内存块加入到链表中,以备后续使用。在这个过程中,malloc会把每个内存块的大小等信息记录下来,以便在后续的内存管理操作中使用。

free

调用free函数时,C库会根据该指针,找到相应的内存块,并把它标记为可用状态,以便下次又有其他程序需要使用该内存时可以再次被分配出去。因此,在使用free函数时,我们必须确保传入的指针是通过malloc或者realloc函数申请得到的,否则可能会发生内存泄漏或者非法操作的情况。

syscall

#include<unistd.h>

void *sbrk(intptr_t increment);
int brk(void *addr);

sbrk函数返回指向新分配内存的起始地址的指针,并且自动将堆末地址增加increment字节,brk函数将堆末指针移动到addr处。两个函数都返回0表示操作成功,返回-1表示失败并设置errno变量。

#include <unistd.h>

int main() {
    void* ptr1 = sbrk(10 * sizeof(int)); // 分配10个整型变量的内存空间
    if (ptr1 == (void*) -1) {
        // 内存分配失败
        return -1;
    }

    void* ptr2 = sbrk(20 * sizeof(int)); // 再次分配20个整型变量的内存空间
    if (ptr2 == (void*) -1) {
        // 内存分配失败
        brk(ptr1); // 释放ptr1之前分配的内存
        return -1;
    }

    // 使用ptr1和ptr2进行一些操作

    brk(ptr2); // 释放ptr2之前分配的内存
    brk(ptr1); // 释放ptr1之前分配的内存

    return 0;
}

malloc会调用brk/sbrk来增加堆区大小以满足分配需求,分配/回收完成进程的堆顶指针(brk指针)将被更新,指向新的堆顶位置。

kernel

即使调用brk()sbrk()函数移动了进程数据段的位置,Linux内核也不会立即为进程分配新的物理内存。Linux内核只有在进程需要访问这些新分配的虚拟地址时,才会将虚拟地址映射到物理内存。

在进程需要访问某个虚拟地址时,Linux内核首先会检查该虚拟地址是否已经被映射到物理内存,如果没有则会进行页面缺失(page fault)处理。这时,Linux内核会评估进程请求的内存块大小,并查找可用的物理内存块来满足进程的请求。如果有足够的可用物理内存,则Linux内核会将该内存块映射到进程的虚拟地址空间中,并允许进程对其进行读写操作。