为什么在C中使用克隆函数需要这个指针算术?

时间:2011-12-25 15:35:35

标签: c linux clone

我正在尝试在C中使用clone()函数,并且不确定第二个参数是如何工作的。根据{{​​1}}手册页:

clone()

在对this article的评论中提出以下建议之后,我已经能够得到一个使用此C程序的简单示例:

   The child_stack argument specifies the location of the stack used by  the
   child  process.  Since the child and calling process may share memory, it
   is not possible for the child process to execute in the same stack as the
   calling  process.  The calling process must therefore set up memory space
   for the child stack and pass a pointer to this space to clone().   Stacks
   grow downwards on all processors that run Linux (except the HP PA proces‐
   sors), so child_stack usually points to the topmost address of the memory
   space set up for the child stack.

但我很困惑为什么#include <stdio.h> #include <sched.h> #include <stdlib.h> #include <assert.h> #define SIZE 65536 int v1; int run(void *arg) { v1 = 42; return 0; } int main(int argc, char **argv) { void **child_stack; int pid, rc, status; v1 = 10; child_stack = (void **) malloc(SIZE); assert(child_stack != NULL); printf("v1 before: %d\n", v1); pid = clone(run, child_stack + SIZE/sizeof(void **), CLONE_VM, NULL); //pid = clone(run, child_stack + SIZE, CLONE_VM, NULL); assert(pid != -1); status = 0; rc = waitpid(pid, &status, __WALL); assert(rc != -1); assert(WEXITSTATUS(status) == 0); printf("v1 after: %d\n", v1); return 0; } 行中的特定指针算法是必要的。鉴于根据clone文档堆栈应该向下增长,我明白为什么你应该在传递它之前为clone返回的指针添加一个值。但我希望你' d添加malloc'd的字节数,而不是该值除以8(在64位系统上),这似乎实际上工作。上面的代码似乎工作正常,无论我将malloc定义为什么,但如果我使用注释版本,这是我期望工作的,我会得到一个高于某个SIZE值的分段错误阈值。

所以,任何人都明白为什么给定的SIZE行有效,但被注释的行不行?

至于为什么我开始使用clone,而不是clone或pthreads,我正在尝试使用它的一些高级沙盒功能来防止不受信任的进程被打破chroot监狱,如here所述。

4 个答案:

答案 0 :(得分:3)

问题是你已经将child_stack声明为void **(指向void指针的指针),当它真正指向要用于堆栈的原始内存时(它不是有C型)。因此,如果您只是将其声明为char *intptr_t,则更有意义,在这种情况下,您可以直接执行指针运算(传递child_stack + SIZE)而不必更正不正确的类型。

请注意,所写的更正不正确(应为/ sizeof(void *)而不是/ sizeof(void **)),但在大多数计算机上都可以正确sizeof(void **) == sizeof(void *)

答案 1 :(得分:2)

向类型为T *的指针添加整数值V会使存储器地址增加V * sizeof(T)。由于代码中的child_stack类型为void**,因此child_stack+SIZE实际上意味着内存地址增加SIZE*sizeof(void*)个字节。

答案 2 :(得分:1)

使用指针运算时,在确定实际内存偏移量时会包含指向的类型的大小,例如:

int a[2] = {1, 2};
int* p = a;

printf("%x: %x\n", &a[0], p);
printf("%x: %x\n", &a[1], p + 1);

在这种情况下,p的值不仅仅是p + 1的地址,而是p + 1 * sizeof(int)的值(指向的类型的大小)。为了解决这个问题,当你想要偏移一些字节数时,你需要将偏移量除以你正在修改的指针类型的大小。在您的情况下,您指向的类型为void*,因此可能更准确地说:

pid = clone(run, child_stack + SIZE/sizeof(void *), CLONE_VM, NULL);

您可以使用以下内容可视化此行为:

int SIZE = 65536;
void** child_stack = (void **) malloc(SIZE);

void** child_stack_end = child_stack + SIZE;
void** child_stack_end2 = child_stack + SIZE / sizeof(*child_stack);

printf("%d\n", (intptr_t)child_stack_end - (intptr_t)child_stack); // "262144"
printf("%d\n", (intptr_t)child_stack_end2 - (intptr_t)child_stack); // "65536"

答案 3 :(得分:-1)

child_stack + SIZE指向您分配的数据结尾的一个,因此使用该位置作为堆栈开头的分段错误并不令人惊讶。你试过child_stack + SIZE - 1吗?