为什么在 C 中使用克隆函数需要这个指针运算?
我正在尝试在 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.
遵循本文<的评论中的建议之后< /a>,我已经能够使用这个 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;
}
但我很困惑为什么 clone
行中的特定指针算术是必要的。鉴于根据 clone
文档,堆栈应该向下增长,我明白为什么您应该在传入 malloc
返回的指针之前添加一个值。但是我预计您会添加 malloc 的字节数,而不是该值除以 8(在 64 位系统上),这似乎是实际有效的。无论我将 SIZE
定义为什么,上面的代码似乎都能正常工作,但是如果我使用注释版本(这正是我期望的工作方式),我会收到所有 SIZE 值的分段错误高于某个阈值。
那么,任何人都明白为什么给定的克隆行有效,但注释行却不起作用?
至于为什么我首先使用clone
,而不是fork
或pthreads,我试图使用它的一些高级沙箱功能来防止不受信任的进程突破 chroot 监狱,如此处所述。
I'm trying to use the clone()
function in C, and am uncertain of how the second argument works. Per the clone()
man page:
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.
After following suggestions in the comments on this article, I've been able to get a simple example working using this C program:
#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;
}
But I'm confused as to why the particular pointer arithmetic in the clone
line is necessary. Given that according to the clone
docs the stack is supposed to grow downward, I see why you should add a value to the pointer returned by malloc
before passing it in. But I'd expect that you'd add the number of bytes malloc'd, instead of that value divided by 8 (on a 64-bit system), which is what seems to actually work. The code above seems to work fine regardless of what I define SIZE
as, but if I use the commented version instead, which is what I'd expect to work, I get a segmentation fault for all SIZE values above a certain threshold.
So, anyone understand why the given clone
line works, but the commented one doesn't?
As for why I'm using clone
to begin with, instead of fork
or pthreads, I'm trying to use some of its advanced sandboxing features to prevent an untrusted process from breaking out of a chroot jail, as described here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
问题是您已将
child_stack
声明为void **
(指向 void 指针的指针),而它实际上是指向用于堆栈的原始内存的指针(没有 C 类型)。因此,如果您只是将其声明为char *
或intptr_t
会更有意义,在这种情况下,您可以直接进行指针算术(传递child_stack + SIZE
),而不必纠正错误的类型。请注意,所写的更正是不正确的(应该是
/ sizeof(void *)
而不是/ sizeof(void **)
),但它可以正常工作为sizeof(void **) == sizeof(void *)
The problem is that you've declared
child_stack
as avoid **
(a pointer to void pointers), when its really a pointer to raw memory to be used for a stack (which doesn't have a C type). So it would make more sense if you just declared it as achar *
orintptr_t
instead, in which case you could do the pointer arithmetic directly (passingchild_stack + SIZE
) rather than having to correct for the incorrect type.Note that the correction as written is incorrect (should be
/ sizeof(void *)
rather than/ sizeof(void **)
), but it works out ok assizeof(void **) == sizeof(void *)
on most machines将整数值 V 添加到 T* 类型的指针会将内存地址增加 V*sizeof(T)。由于代码中的
child_stack
类型为void**
,因此child_stack+SIZE
实际上意味着内存地址增加了SIZE* sizeof(void*)
字节。Adding an integer value V to a pointer of type T* increments the memory address by V*sizeof(T). Since
child_stack
in your code has typevoid**
,child_stack+SIZE
in effect means that the memory address is increased bySIZE*sizeof(void*)
bytes.对于指针算术,在确定实际内存偏移量时会合并所指向类型的大小,例如:
在这种情况下,
p
的值不仅仅是p< 的地址/code> + 1,它是
p + 1 * sizeof(int)
的值(指向的类型的大小)。考虑到这一点,当您想要偏移一定数量的字节时,您需要将偏移量除以要修改的指针类型的大小。在您的情况下,您指向的类型是void*
,因此更准确的说法是:您可以使用以下方式可视化此行为:
With pointer arithmetic, the size of the type pointed to is incorporated when determining the actual memory offset, take for example:
In this case, the value of
p
isn't just address ofp
+ 1, it's the value ofp + 1 * sizeof(int)
(the size of the type pointed to). To account for this, when you want to offset some number of bytes, you need to divide the offset by the size of the pointer type you're modifying. In your case, the type you're pointing to isvoid*
, so it may be more accurate to say:You can visualize this behavior with something like:
child_stack + SIZE
指向您分配的数据末尾的末尾,因此使用该位置作为堆栈开头的分段错误并不奇怪。您是否尝试过child_stack + SIZE - 1
?child_stack + SIZE
points to one past the end of the end of the data you allocated, so a segmentation fault for using that location as the start of the stack is not that surprising. Have you triedchild_stack + SIZE - 1
?