为什么我执行的 sbrk 系统调用不起作用?
Why my implementation of sbrk system call does not work?
我尝试写一个非常简单的os来更好地理解基本原理。我需要实现 user-space 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
应该有一个 long increment
。你忘了处理错误(并设置 errno
)
第二个 sbrk
函数不会更改 address space(与 sbrk
一样)。您可以使用 mmap
来更改它(但是使用 mmap
而不是 sbrk
不会像 sbrk
那样更新内核的数据段结束视图)。您可以使用 cat /proc/1234/maps
来查询 pid 1234 进程的地址 space。甚至从程序内部读取(例如使用 fopen
&fgets
)/proc/self/maps
。
顺便说一句,sbrk
已过时(大多数 malloc 实现使用 mmap
),并且根据定义每个 system call (listed in syscalls(2))由内核执行(对于 sbrk
kernel 保持 "data segment" 限制!)。所以你无法避免内核,我什至不明白你为什么要模拟任何系统调用。几乎根据定义,您不能模拟系统调用,因为它们是从用户应用程序与内核交互的唯一方式。从用户应用程序来看,每个系统调用都是一个原子基本操作(由单个 SYSENTER
机器指令完成,机器寄存器中有适当的内容)。
您可以使用 strace(1) 了解您的 运行 程序完成的实际系统调用。
顺便说一句,GNU libc
is a free software. You could look into its source code. musl-libc is a simpler libc 及其代码更具可读性。
最后用 gcc -Wall -Wextra -g
编译并使用 gdb
调试器(如果你愿意,你甚至可以查询寄存器)。也许阅读 x86/64-ABI specification and the Linux Assembly HowTo.
我尝试写一个非常简单的os来更好地理解基本原理。我需要实现 user-space 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
应该有一个 long increment
。你忘了处理错误(并设置 errno
)
第二个 sbrk
函数不会更改 address space(与 sbrk
一样)。您可以使用 mmap
来更改它(但是使用 mmap
而不是 sbrk
不会像 sbrk
那样更新内核的数据段结束视图)。您可以使用 cat /proc/1234/maps
来查询 pid 1234 进程的地址 space。甚至从程序内部读取(例如使用 fopen
&fgets
)/proc/self/maps
。
顺便说一句,sbrk
已过时(大多数 malloc 实现使用 mmap
),并且根据定义每个 system call (listed in syscalls(2))由内核执行(对于 sbrk
kernel 保持 "data segment" 限制!)。所以你无法避免内核,我什至不明白你为什么要模拟任何系统调用。几乎根据定义,您不能模拟系统调用,因为它们是从用户应用程序与内核交互的唯一方式。从用户应用程序来看,每个系统调用都是一个原子基本操作(由单个 SYSENTER
机器指令完成,机器寄存器中有适当的内容)。
您可以使用 strace(1) 了解您的 运行 程序完成的实际系统调用。
顺便说一句,GNU libc
is a free software. You could look into its source code. musl-libc is a simpler libc 及其代码更具可读性。
最后用 gcc -Wall -Wextra -g
编译并使用 gdb
调试器(如果你愿意,你甚至可以查询寄存器)。也许阅读 x86/64-ABI specification and the Linux Assembly HowTo.