You have to understand what a shell is in relation to the kernel to understand what really happens when you run a command.
The Kernel is the most fundamental part of a computer operating system. It’s a program itself and it controls all other programs on the computer. It talks to hardware and software and is highly involved in resource management.
The Shell is an application that executes programs called commands through an interactive user interface with an operating system. It is the layer of programming that understands and executes the commands a user enters. In some systems, the shell is called a command interpreter.
Use the syntax below to write a command with an argument.
command [arg1] [arg2] .. [argn]
ls is a shell command that lists files and directories within a directory. With the -l option, ls *.c will list out files and directories with a .c extension in a long list format which includes their respective permissions, owners, and created date and time as seen in the image below.
After inputting the command into the shell prompt, the shell reads the command, ls -l.c* using thegetLine() function’s STDIN data stream. From here, It stores the input into a buffer as a string.
The buffer then reads from STDIN to the given block size and writes each block to the standard output.
Now the string is broken into tokens by removing whitespaces and then stored in an array of strings.
The shell checks if any of the tokens have an alias defined. If there is, it replaces the token with it’s value.
If there isn’t, it checks if any of the tokens is a built-in function and if it’s not there either.
The shell looks for a program file called ls in the shell’s environment where all the executable files are in the system, specifically in the $PATH variable. The $PATH variable is a list of directories the shell searches every time a command is entered. One of the environment variables, $PATH is parsed using the ‘=’ as a delimiter. Once the $PATH is identified, all the directories in $PATH are tokenized, each location specified using the ‘:’ delimiter, recursively searched by appending the command at the end of the path.
Since it searches recursively, it will first search in the
pwd and then its parent and so on and so forth with all other commands.
To execute ls, the following system calls are made: fork(), execve() and wait().
System calls are interactions we make with the kernel.
To run a command in a shell, the fork() system call is made, this duplicates the shell by creating a child shell of the parent shell.
Then the execve() system call is made, which stops the old duplicate process (parent shell), loads up the new process(child shell which is ls in this case) and start the new program which gives a brand new address with the program by replacing the current process's memory stack with the new data loaded from the ls executable file.
Meanwhile, using the wait() system call, the parent process continues to keep track of it’s children in the background.
Once the ls -l.c* command is executed, the shell frees up memory, exits and prompts the user for input again.
All these happen under a few milliseconds in most cases, depending your machine power.
Did I misplace anything? let me know in the comments.