为什么此代码退出并返回代码 16?

为什么此代码退出并返回代码 16?

我正在尝试使用clone()系统调用来创建一个线程。然而,程序在从 t2_thread() 函数返回时自行终止。为什么会出现这种行为?我缺少什么?

#define _GNU_SOURCE
#include<sys/syscall.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<sched.h>

int t2_thread(void *arg)
{
        printf("Thread 2 (%ld)\n",syscall(SYS_gettid));
        return;
}
int main(int argc, char **argv)
{
        const size_t STCK_SZ = 65536;
        char *stck;
        int flags;
        stck = malloc(STCK_SZ);
        if(stck == NULL)
        {
                perror("malloc");
                exit(EXIT_FAILURE);
        }
        flags = CLONE_SIGHAND |CLONE_FS |CLONE_VM |CLONE_FILES | CLONE_THREAD;
        if(clone(t2_thread, stck + STCK_SZ, flags, NULL)==-1)
        {
                perror("clone");
                exit(EXIT_FAILURE);
        }

        printf("Thread 1 (%ld)\n",syscall(SYS_gettid));

        for(;;)
        {
                printf("T1\n");
                sleep(1);
        }
        exit(EXIT_SUCCESS);
}

顺便说一句,这个程序的输出是:

Thread 1 (8963)
T1
Thread 2 (8964)

$echo $?
16

我应该怎么做才能无限地执行for循环?

答案1

在 2.26 之前的 GNU libc 版本以及包括 x86_64 在内的某些架构上,从传递给 的函数返回后clone(),libc 最终将调用exit_group()(返回值作为参数,您不传递因此随机 16),这将导致所有线程(整个进程)终止。

它被固定在这次提交(看相应的错误报告)。

commit 3f823e87ccbf3723eb4eeb63b0619f1a0ceb174e
Author: Adhemerval Zanella <[email protected]>
Date:   Thu Jun 22 08:49:34 2017 -0300

   Call exit directly in clone (BZ #21512)

   On aarch64, alpha, arm, hppa, mips, nios2, powerpc, sh, sparc, tile,
   and x86_64 the clone syscall jumps to _exit after the child execution
   and the function ends the process execution by calling exit_group.
   This behavior have a small issue where threads created with
   CLONE_THREAD using clone syscall directly will eventually exit the
   whole group altogether instead of just the thread created.  Also,
   s390, microblaze, ia64, i386, and m68k differs by calling exit
   syscall directly.

   This patch changes all architectures to call the exit syscall
   directly, as for s390, microblaze, ia64, i386, and m68k.  This do not
   have change glibc internal behavior in any sort, since the only
   usage of clone implementation in posix_spawn calls _exit directly
   in the created child (fork uses a direct call to clone).

   Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
   powerpc-linux-gnu, powerpc64le-linux-gnu, sparc64-linux-gnu,
   and sparcv9-linux-gnu.

对于旧版本,您可以通过exit直接调用系统调用 ( syscall(SYS_exit, 0)) 而不是使用 来解决这个return问题,或者如果您不想修改函数,请将包装函数传递给clone()定义为:

int wrapper(void *arg)
{
  syscall(SYS_exit, t2_thread(arg));
  return 0; /* never reached */
}

相关内容