"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.h
which 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,
fork
system 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
wait
system 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