"Creating Your Own Command Interpreter: Main Concepts Guide: Simple Shell - Part 3"
Deeper into UNIX: Exploring the Inner Workings of UNIX with a Custom Interpreter
In this blog series, we have explored the basics of creating and managing processes in C programming language. We have discussed the concept of pid and ppid, how to manipulate the environment of the current process, and the three prototypes of the main function. We have also looked at how the shell uses the PATH environment variable to find programs when commands are entered. In the upcoming parts of the series, we will delve deeper into process execution and management, input/output functions, and handling end-of-file conditions.
In the upcoming part of this blog series, we will explore advanced topics related to process creation and management in C programming language. Specifically, we will delve deeper into the execve system call and how to suspend the execution of a process until one of its children terminates. Additionally, we will discuss the concept of EOF or "end-of-file" in C programming. By understanding these advanced topics, you will be better equipped to create and manage complex processes in your C programs. Stay tuned for more information in the upcoming parts of this blog series.
Let’s get started :
How to execute another program with the execve system call ?
In C programming language, you can execute another program from within a program using the execve system call. The execve system call replaces the current process image with a new process image, as specified by the path to the executable file.
The execve system call takes three arguments: the path to the executable file, an array of command-line arguments, and an array of environment variables. The path to the executable file can be an absolute or relative path, and the command-line arguments and environment variables are optional.
Here's an example of how to use the execve system call to execute the ls command:

execve system call with the path to the ls executable file (/bin/ls), the argv array, and the envp array. In this example, we define an array argv that contains the command-line arguments to pass to the ls command. We also define an array envp that contains the environment variables to pass to the new process. In this case, we are not passing any environment variables, so envp is set to NULL.
We then call the execve system call with the path to the ls executable file (/bin/ls), the argv array, and the envp array. The execve call replaces the current process image with the ls process image, which executes the ls command with the specified command-line arguments.
remember that
unistd.hwhich is a header file needed for interacting with the operating system on Unix-like systems.
Let’s compile : gcc myprog.c -o output -D_GNU_SOURCE
Output =>
Now Let’s see another example of using the execve system call with a non-null envp argument:
#include <unistd.h>
int main(int argc, char *argv[], char *envp[])
{
argv[0] = "echo";
argv[1] = "Hello Shaza";
argv[2] = NULL;
envp[0] = "MY_VAR=123";
envp[1] = NULL;
execve("/bin/echo", argv, envp);
return 0;
}
Let’s gcc => gcc execv2.c -o ex2 -D_GNU_SOURCE
output => Hello Shaza MY_VAR=123
How to suspend the execution of a process until one of its children terminates ?
In C programming language, you can suspend the execution of a process until one of its child processes terminates by using the wait system call.
The wait system call suspends the execution of the calling process until one of its child processes terminates. When a child process terminates, the wait call returns the process ID (PID) of the terminated child process.
Here's an example of how to use the wait system call to suspend the execution of a process until one of its child processes terminates:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid;
int status;
pid = fork(); // create a child process
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// child process
printf("Child process executing...\n");
sleep(2); // sleep for 2 seconds
printf("Child process exiting...\n");
exit(EXIT_SUCCESS);
} else {
// parent process
printf("Parent process waiting for child to terminate...\n");
wait(&status); // wait for child to terminate
printf("Child process terminated with status %d\n", status);
}
return 0;
}In this example,
forksystem call to create a child process. In the child process, we print a messagesleep for 2 seconds, and then exit with a success status.
In the parent process, we print a message indicating that we are waiting for the child process to terminate
call the
waitsystem call to suspend the execution of the parent process until the child process terminates.
When the child process terminates, the wait call returns the status of the child process, which we print to the console.
compile :
gcc wait.c -o wait -D_GNU_SOURCE
./wait
Parent process waiting for child to terminate...
Child process executing...
Child process exiting...
Child process terminated with status 0
What is EOF / “end-of-file” ?
In C programming language, EOF or "end-of-file" is a special value that indicates the end of a file or input stream.
EOF is a constant defined in the standard I/O library header file stdio.h. It is usually defined as -1.
EOF is returned by input/output functions such as getchar, fgetc, and fgets to indicate that there is no more data to read from a file or input stream. When EOF is encountered, the input/output function stops reading and returns EOF.
Here's an example of how to use EOF to read input until the end of a file:
#include <stdio.h>
int main() {
int c;
while ((c = getchar()) != EOF) {
putchar(c); // print the character
}
return 0;
}
In this example, we use a while loop to read input from the standard input stream (stdin) until EOF is encountered. The getchar function reads a single character from the input stream and returns it as an int. If EOF is encountered, getchar returns EOF and the loop terminates.
Inside the loop, we use the putchar function to print the character to the standard output stream (stdout).
In conclusion, we have covered in 3 parts series a range of important topics related to process creation, management, and execution in C programming language. We started by discussing the basics of how a shell works, the concept of pid and ppid, and how to manipulate the environment of the current process. We then explored the difference between a function and a system call, and how to create and manage processes using the fork and execve system calls.
We also covered the three prototypes of the main function and how the shell uses the PATH environment variable to find programs. Finally, we discussed how to suspend the execution of a process until one of its children terminates and the concept of EOF or "end-of-file" in C programming.
By understanding these topics, you should now have a solid foundation in process management and execution in C programming language. In the next parts of this blog series, we will delve deeper into these topics and explore more advanced techniques for creating and managing processes.
Go to Part 1 : PART 1 Simple Shell
Go to Part 2 : PART 2 Simple Shell


