返回信息流根据man 2 clone的说法,clone syscall的C wrapper需要明确给出child的stack top地址,但是裸syscall却不用,这是为什么呢?我在Google上搜了一下,没有找到能把这个问题讲明白的讨论。根据我自己的实验,这个stack的大小可以非常小,而且我发现calling function可以直接释放掉它而child process不会出现segment fault。
调用完clone之后直接释放stack也不会有问题:
```C++
auto stack = std::make_unique<char[]>(stackSize);
if (!stack) { return 0; }
void* stackTop = static_cast<void*>(stack.get() + stackSize);
return clone(BootstrapChildProcess, stackTop, CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWNS |
CLONE_NEWPID | CLONE_NEWUTS, static_cast<void*>(this));
```
我看Golang的os/exec的源码里调用裸syscall,根本就没传stack这个参数:
```Golang
switch {
case sys.Cloneflags&CLONE_NEWUSER == 0 && sys.Unshareflags&CLONE_NEWUSER == 0:
r1, err1 = rawVforkSyscall(SYS_CLONE, uintptr(SIGCHLD|CLONE_VFORK|CLONE_VM)|sys.Cloneflags)
case runtime.GOARCH == "s390x":
r1, _, err1 = RawSyscall6(SYS_CLONE, 0, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0)
default:
r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
}
```
这是一条镜像帖。来源:北邮人论坛 / linux / #160434同步于 2021/12/22
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Linux机器人发帖
关于clone系统调用的问题
Zelda
2021/12/22镜像同步14 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
如果是 share memory,那么parent和child就会使用同样的stack可能会导致混乱,glibc提供这个就是在这种情况下(大致这个意思吧)提供一种选择给coder
就是这句话才是最让人费解的。如果child是个thread,那它的解释是合理的,也就是说只有在设置了CLONE_THREAD的时候才是必须的。可事实上这个wrapper无论如何都需要这个stack。
所以本质问题是,在clone的是process的场景下,那个child进程的stack到底是不是我在parent的heap上申请的内存?如果是,parent从heap上释放了它之后操作系统认为那段内存是已分配还是未分配呢?如果不是,那这段内存是干嘛用的?我现在能确定它是有用的,因为如果分配的特别少(小于16bytes),parent本身就会crash。
【 在 baymaxhwy 的大作中提到: 】
: 如果是 share memory,那么parent和child就会使用同样的stack可能会导致混乱,glibc提供这个就是在这种情况下(大致这个意思吧)提供一种选择给coder
你这个就是的clone设计问题(glibc也提供了 fork 和 clone3 这样一些其他的供选择)至于 process 在 heap 上是已分配还是未分配这就要看 kernel 代码了不是特别清楚,不过不管 thread 还是 process 在 kernel 都是 task_struct,内存分配规则都是一致的,再详细的就不清楚了,可以去 debug 一下 kernel 测试一下
我认识不管是 heap 还是 stack 都只是一段内存,只要还有使用就不会回收(毕竟 os 没有 gc)
【 在 Zelda 的大作中提到: 】
: 就是这句话才是最让人费解的。如果child是个thread,那它的解释是合理的,也就是说只有在设置了CLONE_THREAD的时候才是必须的。可事实上这个wrapper无论如何都需要这个stack。
: ............
你是不是看错文档了,只有当指定 `CLONE_VM` 的时候才需要指定 `stack` 和 `stack_size`,其它时候指定 `NULL` 和 `0` 就可以。
```
The stack for the child process is specified via cl_args.stack,
which points to the lowest byte of the stack area, and
cl_args.stack_size, which specifies the size of the stack in
bytes. In the case where the CLONE_VM flag (see below) is
specified, a stack must be explicitly allocated and specified.
Otherwise, these two fields can be specified as NULL and 0, which
causes the child to use the same stack area as the parent (in the
child's own virtual address space).
```
原因也很清楚,指定 `CLONE_VM` 的时候父子进程/线程共享地址空间,如果不指定新的栈的话会导致意想不到的行为。
“在clone的是process的场景下,那个child进程的stack到底是不是我在parent的heap上申请的内存?” 如果不指定 `CLONE_VM`,父子进程的地址空间是独立的,内核使用 COW 机制,父子进程对于内存的修改肯定不会相互影响的啊,你在 parent 里 free 从 heap 上分配的内存丝毫不影响 child 中的 stack。
这一段是针对 clone3 的描述。
【 在 nitroethane 的大作中提到: 】
: [md]
: 你是不是看错文档了,只有当指定 `CLONE_VM` 的时候才需要指定 `stack` 和 `stack_size`,其它时候指定 `NULL` 和 `0` 就可以。
: ```
: ...................
我没看错,这段是clone3的说明。clone就是强制需要stack。
【 在 nitroethane 的大作中提到: 】
: [md]
: 你是不是看错文档了,只有当指定 `CLONE_VM` 的时候才需要指定 `stack` 和 `stack_size`,其它时候指定 `NULL` 和 `0` 就可以。
: ```
: ...................
clone3 和 clone 在内核里都是走的同一个函数 kernel_clone。这段 manual 确实是我看走眼了,不过你不应该思考下我后面的分析么?在父子进程不 clone_vm 的情况下就不需要给 child 设置从堆上分配的栈么?而且这个描述在 manual 的 notes 里有。
In contrast to the glibc wrapper, the raw clone() system call
accepts NULL as a stack argument (and clone3() likewise allows
cl_args.stack to be NULL). In this case, the child uses a
duplicate of the parent's stack. (Copy-on-write semantics ensure
that the child gets separate copies of stack pages when either
process modifies the stack.) In this case, for correct
operation, the CLONE_VM option should not be specified. (If the
child shares the parent's memory because of the use of the
CLONE_VM flag, then no copy-on-write duplication occurs and chaos
is likely to result.)
【 在 Zelda 的大作中提到: 】
:我没看错,这段是clone3的说明。clone就是强制需要stack。
我贴的那个go的例子已经能说明系统调用本身是不需要指定stack的了,关于这点我认为没有争议。所以您的这个回答并没有解答我的问题:即这段内存对于clone wrapper到底有什么用。事实上这段stack特别小calling function会crash,所以它肯定是有用的。
【 在 nitroethane 的大作中提到: 】
: clone3 和 clone 在内核里都是走的同一个函数 kernel_clone。这段 manual 确实是我看走眼了,不过你不应该思考下我后面的分析么?在父子进程不 clone_vm 的情况下就不需要给 child 设置从堆上分配的栈么?而且这个描述在 manual 的 notes 里有。
: In contrast to the glibc wrapper, the raw clone() system call
: accepts NULL as a stack argument (and clone3() likewise allows
: ...................
https://stackoverflow.com/questions/38939650/minimal-stack-size-for-linux-clone-call
【 在 Zelda 的大作中提到: 】
: 根据man 2 clone的说法,clone syscall的C wrapper需要明确给出child的stack top地址,但是裸syscall却不用,这是为什么呢?我在Google上搜了一下,没有找到能把这个问题讲明白的讨论。根据我自己的实验,这个stack的大小可以非常小,而且我发现calling function可以直接释放掉它而child process不会出现segment fault。
: [md]
: 调用完clone之后直接释放stack也不会有问题:
: ............