【问题标题】:Why my implementation of sbrk system call does not work?为什么我的 sbrk 系统调用的实现不起作用?
【发布时间】:2015-05-03 17:19:46
【问题描述】:

我尝试编写一个非常简单的操作系统来更好地理解基本原理。我需要实现用户空间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;
 }

【问题讨论】:

  • 有趣的问题,但您可能应该扩展“不起作用”。
  • 我已经提到了问题的原因。请参见上文。
  • 你为什么要“模拟”像sbrk 这样的系统调用?你应该编辑你的问题来激发它。
  • 我尝试写一个非常简单的os来更好地理解基本原理。我需要实现用户空间malloc。所以一开始我想在我的 linux 机器上实现和测试它。
  • 那么,你不需要定义sbrk(你可以使用系统提供的)。你需要在你的malloc 中使用mmap 并且你的内核应该有mmap 或者一些等效的方法来改变地址空间。你可以在 Qemu 下运行你的操作系统。顺便说一句,请编辑您的问题,不要只在 cmets 中给出重要的动机!

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


【解决方案1】:

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

第二个sbrk 函数不会改变address space(就像sbrk 一样)。您可以使用mmap 来更改它(但使用mmap 而不是sbrk 不会像sbrk 那样更新内核的数据段结束视图)。您可以使用cat /proc/1234/maps 查询pid 1234 的进程的地址空间)。甚至从您的程序中读取(例如使用fopen&fgets/proc/self/maps

顺便说一句,sbrk 已过时(大多数 malloc 实现使用mmap),并且根据定义,每个system call(列在syscalls(2) 中)都由内核执行(对于sbrkkernel 维护“数据段”限制!)。所以你不能避免内核,我什至不明白你为什么要模拟任何系统调用。几乎按照定义,您不能模拟系统调用,因为它们是从用户应用程序与内核交互的唯一方式。从用户应用程序来看,每个系统调用都是一个原子基本操作(由单个 SYSENTER 机器指令完成,机器寄存器中有适当的内容)。

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

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

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

【讨论】:

    猜你喜欢
    • 2017-09-12
    • 2012-05-06
    • 2015-09-24
    • 1970-01-01
    • 2017-06-24
    • 1970-01-01
    • 2019-12-14
    • 2020-07-10
    • 1970-01-01
    相关资源
    最近更新 更多