3

I am trying my hands on clone() system call to create a Thread. However program is terminating itself as it return from t2_thread() function. Why is this behaviour? What am I missing?

#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);
}

By the way output of this program is:

Thread 1 (8963)
T1
Thread 2 (8964)

$echo $?
16

What should I do to execute for loop infinitely?

  • You have int t2_thread(void *arg) but you don't return anything. Do you want to return the output of syscall()? Also, you don't use t2_thread()'s argument anywhere, nor do you use main()'s argc and **argv . Is there any reason why you don't want to use gettid() instead of a call to syscall() (perhaps I misunderstood something). – schaiba Oct 9 '19 at 11:37
  • I can't reproduce on Debian with kernel 5.2. What's the versions of kernel, libc and compiler on your system? – Stéphane Chazelas Oct 9 '19 at 11:49
  • @schaiba I have edited my previous program where I was using these parameters, so you can ignore arg, argc, and argv. However, despite including <sys/types.h> gettid() is undefined reference for gcc. My linux version is : Ubuntu (3.13.0-117) and gcc is (4.6.3), that why I have used syscall. – MankPan Oct 9 '19 at 11:51
  • @StéphaneChazelas its Ubuntu (3.13.0-117) and gcc version is (4.6.3) – MankPan Oct 9 '19 at 11:52
  • The version of libc would probably be more relevant. – Stéphane Chazelas Oct 9 '19 at 11:55
3

In versions of GNU libc prior to 2.26 and on some architectures including x86_64, upon return from the function passed to clone(), the libc would eventually call exit_group() (with the returned value as argument which you don't pass hence the random 16) which would cause all threads (the whole process) to terminate.

It was fixed in this commit (see corresponding bug report).

commit 3f823e87ccbf3723eb4eeb63b0619f1a0ceb174e
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
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.

With older versions, you could work around it by calling the exit system call directly (syscall(SYS_exit, 0)) instead of using return, or if you don't want to modify your function, pass a wrapper function to clone() defined as:

int wrapper(void *arg)
{
  syscall(SYS_exit, t2_thread(arg));
  return 0; /* never reached */
}
  • Thank you!! My reputation is less than 15 so I can't up vote your answer. – MankPan Oct 9 '19 at 12:41
  • @MankPan, that is fine. You should be able to accept it (by clicking the tick mark) though, unless you want to wait for someone to maybe provide with a potentially better alternative. – Stéphane Chazelas Oct 9 '19 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.