Skip to content

Instantly share code, notes, and snippets.

@Chubek
Created January 17, 2024 19:53
Show Gist options
  • Save Chubek/c84edd840af22f28c43787f23705949f to your computer and use it in GitHub Desktop.
Save Chubek/c84edd840af22f28c43787f23705949f to your computer and use it in GitHub Desktop.
Demonstration of Fork-Exec-Wait loop in C

Demonstration of For-Exec-Wait Loop in C

So this is basically how a POSIX-XCU shell exectues a program. In reality, a shell with job control especially, has a much more involed procedure. But in brunt of the things, this is how it's done.

A Fork-Exec-Wait loop, hereby refered to as FEWL, has three stages:

1- Fork the parent process 2- Execute the program in the child process 3- Wait until the program terminates in the parent process.

On Linux, and most POSIX systems, programs are created by forking in this exact same manner. Sometimes via a shell using an 'initrc' script, sometime using an explciit FEWL. What this mean is, on POSIX, there's a process tree that begins with the 'mother' process, often called the 'init' proces, and everything is forked from there.

Every process has with itself a PCB --- a 'Process Control Block'. POSIX does not specify what PCB should contain, or how the programmer should access PCB. On Linux, for example, if you include 'linux/sched.h' you will gain access to a data strcucture which contains the PCB for the current process.

When you shut down the system, again, it is not 'fully' specified in POSIX specs, but usually, the process tree is walked back and they are all SIGTERM'd. Notice that SIGTERM is different from SIGINT. SIGINT means 'interrupt', and SIGINT can be issued by any program. But SIGTERM can only be issued by the kernel, or something with elavated access (beyond that of sudo, maybe an LKM? Not sure!)

How to Use this Program

Save this into a file called fewl.c. Then run gcc -o fewl fewl.c. After that, you can run it like:

./fewl echo "This echoes"

Then echo is invoked.

Notice that, in the C runtime library, there are 6 exec* functions whereas there's only one execve system call. The runtime function that I have used, execvp, threats the program (the first argument) like shell treats them, it looks for it the the usual places specified by confstr library routine. Some exec-family functions do this, some don't. Consult man 3 exec for more info.

#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char** argv) {
char* program = argv[1];
char** commands = &argv[1];
pid_t child_pid = 0;
int exit_status = 0;
if (argc < 2) {
fprintf(stderr, "Error: Not enough arguments passed to commandline");
exit(EXIT_FAILURE);
}
if (sysconf(_SC_ARG_MAX) < argc - 2) {
fprintf(stderr, "Error: excess arguments passed to command");
exit(EXIT_FAILURE);
}
if (!(child_pid = fork())) {
execvp(program, commands);
perror("execvp");
exit(EXIT_FAILURE);
} else if (child_pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (waitpid(child_pid, &exit_status, 0) < 0) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0)
fprintf(stderr, "Program %s exited gracefully", program);
else {
fprintf(stderr, "Program %s existed with error status %d", program, exit_status);
}
return exit_status;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment