Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在C中使用clone函数需要这个指针算法?_C_Linux_Clone - Fatal编程技术网

为什么在C中使用clone函数需要这个指针算法?

为什么在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

我试图在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 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*
似乎是最好的选择。