为什么在C中使用clone函数需要这个指针算法?
我试图在C中使用为什么在C中使用clone函数需要这个指针算法?,c,linux,clone,C,Linux,Clone,我试图在C中使用clone()函数,但不确定第二个参数是如何工作的。根据clone()手册页: 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 exec
clone()
函数,但不确定第二个参数是如何工作的。根据clone()
手册页:
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.
在评论中给出了以下建议之后,我得到了一个使用此C程序的简单示例:
#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;
}
#包括
#包括
#包括
#include.child\u stack+SIZE
指向您分配的数据末尾的末尾,因此使用该位置作为堆栈开始的分段错误并不奇怪。您是否尝试过child\u stack+SIZE-1
?child\u stack+SIZE
指向所分配数据末尾的一个,因此使用该位置作为堆栈开始的分段错误并不奇怪。您是否尝试过child\u stack+SIZE-1
?向类型为T*的指针添加一个整数值V会将内存地址增加V*sizeof(T)。由于代码中的child\u stack
具有类型void**
,child\u stack+SIZE
实际上意味着内存地址增加了SIZE*sizeof(void*)
字节。将整数值V添加到类型T*的指针中,会将内存地址增加V*sizeof(T)。由于代码中的child\u stack
具有类型void**
,child\u stack+SIZE
实际上意味着内存地址增加了SIZE*sizeof(void*)
字节。使用指针算法,在确定实际内存偏移量时会合并指向的类型的大小,例如:
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"
使用指针算法,在确定实际内存偏移量时,会合并指向的类型的大小,例如:
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"
问题是您将child\u stack
声明为void**
(指向void指针的指针),而实际上它是指向堆栈(没有C类型)要使用的原始内存的指针。因此,如果您只是将其声明为char*
或intptr\u t
,则更有意义,在这种情况下,您可以直接执行指针算术(传递child\u stack+SIZE
),而不必纠正错误的类型
请注意,所写的更正是不正确的(应该是/sizeof(void*)
,而不是/sizeof(void**)
),但在大多数机器上,它的结果是sizeof(void**)==sizeof(void*)问题是您已将子堆栈声明为void**
(指向void指针的指针),当它实际上是一个指向原始内存的指针,用于堆栈(没有C类型)。因此,如果您只是将其声明为char*
或intptr\u t
,则更有意义,在这种情况下,您可以直接执行指针算术(传递child\u stack+SIZE
),而不必纠正错误的类型
请注意,所写的更正是不正确的(应该是/sizeof(void*)
,而不是/sizeof(void**)
),但在大多数机器上,当sizeof(void**)==sizeof(void*)
时,它可以正常工作,因为子堆栈类型是void**
,而不是void*
?它不需要修改(可以使用任何指针类型)。这是原始作者正在使用的类型,因此在此继续。正如您在另一条注释中指出的,char*
似乎是此测试用例的一种更简单的类型(因为它避免了sizeof()要求)。为什么child\u stack
的类型是void**
而不是void*
?它不需要(可以使用任何指针类型)。这是原始作者使用的类型,因此在这里继续。正如您在另一个注释中指出的,char*
对于这个测试用例来说似乎更简单(因为它避免了sizeof()要求)。这。我想知道为什么堆栈被声明为void**
。我可以理解void*
,但是char*
似乎是最好的选择。这。我想知道为什么堆栈被声明为void**
。我可以理解void*
,但是char*
似乎是最好的选择。