Using the Clone() System Call
Low-level C programming has always been seen as the arena of the "guru". In my upper level operating systems course, the professor thought this was an idea that should be wiped from our minds. To this end, we had quite a few programming assignments that pushed us into totally new territories. One of these assignments was to research and use the clone() system call provided by Linux. Finding information on the low-level system calls provided by Linux was no easy task. Hopefully, this article will save any other budding guru-wannabees some time and trouble.
C programmers in the UNIX world have long used the fork() system call. This call is the basis of multiprocessing, as this is how new processes are created. Actually, for most systems, this is the only way to create a new process. Linux has introduced a new call, clone(), which allows a new level of process context creation.
Processes come in two basic flavours, heavy-weight and light-weight. Heavy-weight processes are those that you normally think of when you run a program. They contain their own address space and program execution context (see Figure 1). Light-weight processes are normally thought of as threads. These light-weight processes share the address space of the parent, and only contain some subset of the context elements (see Figure 2).
Before looking at the clone() system call, I'll begin with a quick review of fork. This system call, along with the class of exec system calls (i.e., execl(), execlp(), execv(), execvp()) is actually the way your shell executes programs given on the command line. A simple example of using fork() is given in Listing 1. The parent opens a file, assigns a value to a variable and forks a child. The child then tries to change the variable and close the file. After exiting, the parent wakes up and checks to see what the variable is and whether the file is open.
As you can see, the child is in a totally new address space. Within this address space, the child process receives a copy of everything the parent had at the point of forking. (This isn't exactly true in Linux. The child only receives a new copy of a memory page when it tries to make a change to something in that page. Until then, it simply uses the parent's copy. This is called copy-on-write memory management.) Changes to this copy do not affect the parent. When the child tries to change the value of the variable, the parent doesn't see this change.
How would you write a similar program using the clone() system call? Listing 2 gives a very basic program that looks like it copies the behaviour of the program using fork(). The most obvious difference at this stage is the fact that the clone() system call leaves all memory management up to the programmer. The first thing to be done is allocating space for the stack of the new child thread with malloc(). Once this is done, the clone() call is issued to begin a new context of execution, starting at the beginning of the given function. The general signature of this system call is
int __clone(int (*fn) (void *arg), void *child_stack, int flags, void *args)
As you can see, there are several parameters to prepare. The first parameter is a pointer to a function which returns an integer. In C, function names are pointers to functions, so this is easily taken care of. The second parameter is a pointer to the stack space that was set up for the child. You will have to decide how much space is required for the operations the child process will perform. The last parameter is a pointer to the arguments that will pass to the function that the child process will execute. In this case, that is a null pointer, since we aren't passing any arguments to the function.
The third parameter deserves special attention. This is where you can designate the flags which will define how much of the process context will be shared between the child and the parent. As in most C functions, multiple flags are simply read together. The flags available to the clone call are:
CLONE_VM - share memoryCLONE_FILES - share file descriptorsCLONE_SIGHAND - share signal handlersCLONE_VFORK - allow child to signal parent on exitCLONE_PID - share PID (not implemented yet)CLONE_FS - share filesystem
You can see that we've set up our sample program with CLONE_VM and CLONE_FILES. Looking at the output of the program, you can see that the child process will share the address space and file descriptor table with its parent. Any changes made by one process are now visible to the other. If the exact behaviour of fork() was required, all that needs to be changed is which flags are set. This way, you get to choose the exact level of sharing your program will use.
There are a few caveats I should mention in using this system call. Several functions, such as printf(), which are not thread-safe when used with clone(). These functions assume the kind of wrapping that occurs when using thread libraries, like linuxthreads. If you are experimenting with a program and run into segfaults or coredumps, this would be a likely place to look to start your troubleshooting. A possible solution would be to use lower level system calls, such as write() instead of printf(). Also, you should compile programs using the -static switch.
I am in debt to the good folks at Red Hat for pointing out some pitfalls while I stumbled my way through this article. Any errors in this article are entirely the fault of the author, and I would appreciate any corrections that you think should be made.
Joey Bernard has a background in both physics and computer science. This serves him well in his day job as a computational research consultant at the University of New Brunswick. He also teaches computational physics and parallel programming.
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?
|Dynamic DNS—an Object Lesson in Problem Solving||May 21, 2013|
|Using Salt Stack and Vagrant for Drupal Development||May 20, 2013|
|Making Linux and Android Get Along (It's Not as Hard as It Sounds)||May 16, 2013|
|Drupal Is a Framework: Why Everyone Needs to Understand This||May 15, 2013|
|Home, My Backup Data Center||May 13, 2013|
|Non-Linux FOSS: Seashore||May 10, 2013|
- Dynamic DNS—an Object Lesson in Problem Solving
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- The Secret Password Is...
- RSS Feeds
- New Products
4 hours 17 min ago
- Keeping track of IP address
6 hours 8 min ago
- Roll your own dynamic dns
11 hours 21 min ago
- Please correct the URL for Salt Stack's web site
14 hours 32 min ago
- Android is Linux -- why no better inter-operation
16 hours 48 min ago
- Connecting Android device to desktop Linux via USB
17 hours 16 min ago
- Find new cell phone and tablet pc
18 hours 14 min ago
19 hours 43 min ago
- Automatically updating Guest Additions
20 hours 52 min ago
- I like your topic on android
21 hours 38 min ago