Linux Clone system call
When reading the strace output of a multithreaded process that also forks child processes, it sometimes becomes necessary to distinguish between thread creation and process creation.
In this article, I would like to describe a simple way to do this to help debugging easier.
While looking at the strace output, we see that the clone system call is used for both process creation and thread creation. Hence, it is necessary to briefly understand how the clone system call works in order to identify if it is being used to create a thread or if it is being used to create a process.
A thread is created on Linux OSes using the function — pthread_create provided by the pthread library, whereas a process is created on Linux OSes using the fork() function. Both of these are in turn implemented using the clone system call.
It is interesting to note here that for the Linux kernel, both processes and threads appear as a “task”.
A process is a task that does not share virtual memory with its parent, whereas a thread is a task that shares virtual memory with its parent.
It is necessary to elaborate the above statement to bring better clarity. When a process is forked, the child’s memory pages are initially mapped to the same pages shared by the parent, and only when the child or parent modifies them the copy happens. Once the copy happens, the parent will not be able to read the data written by the child to any of the global data structures. This is different from the behavior seen when a new thread is created in a process. In this case, the parent thread sees the update done by the newly created child thread to any of the global data structures as the virtual memory is shared between the two.
This difference is behavior happens due to the way the CLONE_VM flag is passed to the clone system call when using it to create a new process vs when using the clone system call to create a new thread.
In case when a new process is to be created, the CLONE_VM flag will be absent in the clone system call to indicate that it is not necessary to share the virtual memory. In case a new thread is to be created, the CLONE_VM flag will be present in the clone system call.
Here are a few example snippets from strace of the Apache HTTPD process.
To identify process creation, identify clone system calls without CLONE_VM flag:
brkarana@brkarana-VirtualBox:~/apache/httpd-2.4.53/install-root$ grep clone strace_merge_log | grep -v CLONE_VM
156713 13:56:33.607371 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fb2dede2e50) = 156714
156714 13:56:33.614079 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fb2dede2e50) = 156715
To identify thread creation, look at clone calls with the CLONE_VM flag present:
`156719 13:56:33.629064 clone(child_stack=0x7fb2b07f7fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=, tls=0x7fb2b07f8700, child_tidptr=0x7fb2b07f89d0) = 156823
156720 13:56:33.629250 clone(child_stack=0x7fb2b07f7fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=, tls=0x7fb2b07f8700, child_tidptr=0x7fb2b07f89d0) = 156824`