Spawning New Linux Processes in C

There are many good reasons to spawn other programs in Linux to do your bidding, but most programming languages don’t give you nearly as much control of the process as in C. In this post I will cover some of the most common ways to create new processes and manage them in C on a Linux system.

Grab a Fork

The classic fork() function is the most popular way to create a new process. It works by duplicating the process that calls it. It may sound complicated but it’s a fairly simple system.

#include <unistd.h>

pid_t fork(void);
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  pid_t fork_pid = fork();

  if (fork_pid == 0) {
    printf("Hello from the child!\n");
  } else {
    printf("Hello from the parent!\n");
  }

  return 0;
}
$ ./forktest 
Hello from the parent!
Hello from the child!

When a process calls fork(), Linux will duplicate that current process. The value returned by fork() will be 0 in the child process. In the parent process fork() will return the PID (Process ID) of the new child process or -1 if some error occurs.

Replacing a Running Process

After creating a new process, it’s common to replace that child process with an entirely different program. The exec() family of functions can handle this for us. The execl() is the simplest method.

#include <unistd.h>
int execl(const char *filename, const char *arg, ...);

All you need to provide is the location of the file to load, and the arguments you’d like to provide to it. Just like for any normal process, the first process is the process name.

#include <unistd.h>

int main(int argc, char *argv[]) {
  execl("/bin/bash", "/yes/its/bash", "-c", "echo $0 && uptime", NULL);
  
  return 0;
}
$ ./execltest
/yes/its/bash
10:11:12 up 1:07, 2 users, load average: 0.07, 0.10, 0.18

The other functions in the exec() family have various options to control arguments and environment variables for the new process that takes over.

Playing with the Kids

After you have wee ‘lil child processes, you’ll probably want to make sure they are doing as they are told. After you’ve sent the child process off to do its chores, you can use the wait() function to see what it returns with after it’s done.

#include <sys/wait.h>

pid_t wait(int *status);

When you call wait(), it will block the parent process until any of it’s children processes change state. The status pointer can be used if you’re interested in knowing what kind of state change has occurred, to determine if the program exited normally, or if it was terminated by a control signal. The return value of wait() is the child PID that has changed states.

#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
  pid_t child = fork();
  
  if (child) {
    wait(NULL);
    printf("child process terminated\n");
  } else {
    execl("/bin/bash", "/THECHILD", NULL);
  }
  
  return 0;
}
$ ./waitkids
$ echo $0
/THECHILD
$ exit
exit
child process terminated

Check the man page for wait for more details on the various options available when waiting.

Attack of the Clones

The fork(), exec() and wait() families of functions are portable across POSIX compliant systems. If more fine grained control over the process creation is desired, we’ll need to use the Linux specific clone() function.

I won’t cover the clone() in this post, as that’s probably more suited for a dedicated post. Regardless, I suggest perusing the man page for it to get an idea of what capabilities it offers.

That covers the basics of process creation using C on Linux. In the next post I plan on covering some of the methods available to communicate between running processes. If you found this helpful or informative, or have any feedback, please leave a note in the comments!

Leave a Reply

Your email address will not be published. Required fields are marked *