子进程不以 syscall(SYS_clone3, …) + CLONE_VM 启动

问题描述 投票:0回答:1

大家好。

调用进程和子进程必须在同一内存空间。 因此,我使用 CLONE_VM 标志。 但子进程没有启动。 看起来堆栈内存分配有问题。 能解释一下原因吗?

#define _DEFAULT_SOURCE          /* syscall() */
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64     /* getrlimit() */

#include <sched.h>              /* CLONE_* constants */
#include <linux/sched.h>        /* struct clone_args */
#include <sys/syscall.h>        /* SYS_* constants */
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>             /* uintptr_t */
#include <err.h>                /* err() */

#include <sys/resource.h>
#include <sys/mman.h>
#include <signal.h>

void spawn(void)
{
    struct rlimit       rlim;
    struct clone_args   cl_args = {0};
    uint64_t            stack_size;
    void                *stackBot;
    void                *stackTop;
    pid_t               pid;

    if (getrlimit(RLIMIT_STACK, &rlim) == -1)
        err(EXIT_FAILURE, "getrlimit");
    stack_size = rlim.rlim_cur;

    stackBot = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN |
                    MAP_STACK, -1, 0);
    if (stackBot == MAP_FAILED)
        err(EXIT_FAILURE, "mmap");

    long count = stack_size / sizeof(uint64_t);
    stackTop = (uint64_t *)stackBot + count - 1;

    printf("stack_size:      %lu\n", stack_size);
    printf("stack_size/2:    %lu\n", stack_size / 2);
    printf("stack_size/4:    %lu\n", stack_size / 4);
    printf("stack_size/8:    %lu\n", stack_size / 8);
    printf("stack_size/16:   %lu\n", stack_size / 16);
    printf("stack_size/4096: %lu\n", stack_size / 4096);   /* PAGESIZE */

    printf("stackBotAddr: %p\n", (void *)stackBot);
    printf("stackTopAddr: %p\n", (void *)stackTop);

    printf("stackBotVal: %lu\n", *(uint64_t *)(uintptr_t)stackBot);
    printf("stackTopVal: %lu\n", *(uint64_t *)(uintptr_t)stackTop);

    printf("stackTopAddr-stackBotAddr: %lu\n", (void *)stackTop - stackBot);

    printf("stackBotCast: %lu\n", (uint64_t)(uintptr_t)stackBot);
    printf("stackTopCast: %lu\n", (uint64_t)(uintptr_t)stackTop);

    cl_args.flags       = CLONE_FILES | CLONE_IO | CLONE_VM;
    cl_args.exit_signal = SIGCHLD;
    cl_args.stack       = (uint64_t)(uintptr_t)stackBot;
    cl_args.stack_size  = (void *)stackTop - stackBot;

    pid = syscall(SYS_clone3, &cl_args, sizeof(cl_args));
    switch(pid) {
        case -1:
            munmap(stackBot, stack_size);
            err(EXIT_FAILURE, "syscall");
        case 0:     /* Child */
            printf("Hello from child. Child's pid: %d\n", getpid());
            munmap(stackBot, stack_size);
            break;
        default:    /* Parent */
            printf("Hello from parent. Parent's pid: %d\n", getpid());
            break;
    }
}

int
main(int argc, char *argv[])
{
    spawn();

    printf("Before last while\n");
    while(1) {
    }

    return EXIT_SUCCESS;
}

使用

gdb
进行调试会导致

[Detaching after fork from child process 16095]

attach process-id
通过
gdb
通向

Attaching to process 16095
warning: process 16095 is a zombie - the process has already terminated
ptrace: Operation not permitted.

在使用clone3()之前,我尝试通过fork()和clone()函数来解决问题。 我拒绝fork(),因为父进程和子进程必须有相同的内存空间。 clone() 函数不起作用,因为文件描述符应该传递给子进程,但如果存在 CLONE_VM 标志,文件描述符会自发更改。我还发现了关于这个问题的两个有用的链接:

但是子进程永远不会启动。

c linux fork clone system-calls
1个回答
0
投票

大家

通过实验发现,创建线程时,当调用进程和子进程处于同一地址空间(CLONE_VM标志)时,无法从高级语言(C)调用clone和clone3系统调用使用包装函数 syscall(2)。在这种情况下,syscall(2) 启动子进程时没有堆栈帧和返回地址,因此它将无法返回。克隆(2)手册页上的描述,建议使用

创建一个线程(CLONE_VM标志)
long syscall(SYS_clone3, struct clone_args *cl_args, size_t size);

,是一个文档错误。该问题的解决方案在文章Linux 上实用的 libc-free 线程 中有详细描述。也非常感谢本文作者的个人评论。

© www.soinside.com 2019 - 2024. All rights reserved.