为什么我执行sbrk系统调用不起作用?

时间:2015-03-03 06:19:30

标签: linux malloc system-calls dynamic-memory-allocation sbrk

我尝试编写一个非常简单的操作系统来更好地理解基本原理。我需要实现用户空间malloc。所以一开始我想在我的linux机器上实现并测试它。

首先,我通过以下方式实现了sbrk()函数

void* sbrk( int increment ) {
    return ( void* )syscall(__NR_brk, increment );
}

但是这段代码不起作用。相反,当我使用os给出的sbrk时,这样可以正常工作。

我尝试使用sbrk()的另一个实现

static void *sbrk(signed increment)  
{  
    size_t newbrk;  
    static size_t oldbrk = 0;  
    static size_t curbrk = 0;  

    if (oldbrk == 0)  
        curbrk = oldbrk = brk(0);  

    if (increment == 0)  
        return (void *) curbrk;  

    newbrk = curbrk + increment;  

    if (brk(newbrk) == curbrk)  
        return (void *) -1;  

    oldbrk = curbrk;  
    curbrk = newbrk;  

    return (void *) oldbrk;  
}  

从这个函数调用的sbrk

static Header *morecore(unsigned nu)  
{  
    char *cp;  
    Header *up;  

    if (nu < NALLOC)  
        nu = NALLOC;  

    cp = sbrk(nu * sizeof(Header));  
    if (cp == (char *) -1)  
        return NULL;  

    up = (Header *) cp;  
    up->s.size = nu;  // ***Segmentation fault
    free((void *)(up + 1));  

    return freep;  
}  

此代码也不起作用,就行(***)我得到分段错误。 哪里有问题?

全部谢谢。我已经使用sbrk的新实现解决了我的问题。给定的代码工作正常。

void* __sbrk__(intptr_t increment)
 {
     void *new, *old = (void *)syscall(__NR_brk, 0);

     new = (void *)syscall(__NR_brk, ((uintptr_t)old) + increment);

     return (((uintptr_t)new) == (((uintptr_t)old) + increment)) ? old :
         (void *)-1;
 }

1 个答案:

答案 0 :(得分:2)

第一个sbrk应该有一个long increment。而你忘了处理错误(并设置errno

第二个sbrk函数不会更改address space(如sbrk那样)。您可以使用mmap进行更改(但使用mmap代替sbrk将不会像sbrk那样更新内核的数据段结束视图。您可以使用cat /proc/1234/maps来查询pid 1234进程的地址空间。或甚至从您的计划内阅读(例如fopen&amp; fgets/proc/self/maps

BTW,sbrk已过时(大多数malloc实现使用mmap),根据定义,每个system call(在syscalls(2)中列出)由内核执行(for { {1}} 内核维护“数据段”限制!)。所以你无法避免内核,我甚至不明白为什么要模拟任何系统调用。几乎按照定义,您无法模拟系统调用,因为它们是从用户应用程序与内核交互的唯一方式。在用户应用程序中,每个系统调用都是一个原子初级操作(由单个sbrk机器指令完成,并在机器寄存器中包含适当的内容。)

您可以使用strace(1)来了解正在运行的程序所执行的实际系统调用。

顺便说一句,GNU libcfree software。你可以查看它的源代码。 musl-libc更简单libc,其代码更易读。

最后使用SYSENTER进行编译并使用gcc -Wall -Wextra -g调试器(如果需要,甚至可以查询寄存器)。也许请阅读x86/64-ABI specificationLinux Assembly HowTo