We have described how to enter a command on the command line, how to redirect input and output, how to enter several commands on a single command line that either act independently or whose input and output are tied together, and how to run multiple commands concurrently.
We have described how the command line is processed before execution. Now, lets examine what actually happens when a command is run.
When you start a command or program running, it becomes a process. But a process is more than the program. A process is a program's code and the resources needed for the program to run. This includes the memory allocated by the operating system (the OS) to hold the program code, any memory needed by the program while it is running for work, the interface to any and all I/O devices, and information needed by the OS to track and control the running process. It also includes information copied from the process that invoked the program.
For example, when you run ls from the bash command prompt, some of the information known by bash is shared with the ls command, whether it needs it or not. This includes the command line arguments and any variables and functions marked as exportable.
When logged in, you are interacting with a copy of the bash shell program running as a process. This copy was started by the operating system as part of the login procedure and assigned a unique process id. This id assignment allows the OS to run many copies of the bash shell and still keep track of each individual copy, who is using it, what terminal is assigned to it, and what commands and files it is interacting with.
When you issue a command, several actions are taken to execute it. The OS forks the command interpreter. The forking process causes the command shell or other program invoking the new command to be duplicated in memory and assigned a new unique process id. This called the child process. Any exportable information is also copied into the new memory location. Any I/O opened with redirection is also copied. If successful, the forking process returns the new processes' id to the parent and a zero to the new process or child.
If the new process is to run in the foreground, the parent process then goes to sleep.
If the new child process is a background process, then the parent process continues to run. In the case of the bash interpreter, you will immediately be issued a new command-line prompt.
The child process fetches the command specified into the newly allocated memory replacing the code copied from the parent program. The other information, such as environmental variables, remains in memory.
The new code executes and is free to create new variables, change existing variables, and open new redirection. These changes exist only while the child process exists.
On completion of activity, the new code returns a status value. This code is passed back to the parent process. Newly created variables or changes in variables passed in to the child will be lost on termination. If the parent process was sleeping, it will begin to execute again.
Keep in mind, built-in shell commands do not invoke the forking process. They often need to access and change local variables. Examples of builtins are echo, set, declare, export, source, etc. See the section "Shell Builtin Commands" around line 3365 in the bash man pages.
It is possible to fetch an external command directly into the current shell environment. This is useful if you want to run a command in the foreground and have it terminate the login on completion of the task. The command to do this exec. This is a shell builtin that performs the same action that happens in a forked child process except the forking never occurs.
exec find / -name "*.h" > found 2> /dev/null
The preceding example loads the find command into the current processes' working memory, replacing the command interpreter, and runs it. Upon completion, since there is no longer a command interpreter to return execution to, the login session terminates. You can see this in a simpler example.
#( Try the following. First start a second copy of the bash interpreter. This
is so when the command terminates the shell process, you don't have to log back
in. ) bash #( First run ps normally, then use exec to invoke ps )
ps > psout1 #( If things ran correctly, you will back in the original shell you invoked bash from. ) #( Now compare the two outputs ) diff psout1 psout2
|
The psout1 should list a copy of bash and the ps command. The psout2 should list only the ps command. Also note that the process id of bash in psout1 matches the process id of ps in psout2.
We have used ps and its options several times to see what is running.
Unix provides another command for listing background (concurrent) processes started from the current shell. A process running in the background of the current shell is called a job. And you may list any jobs running with the jobs command.
#(Try the following ) #(Run the sleep process in the background three times ) sleep 130 & sleep 125 & sleep 150 & sleep 100 & #(Now list the jobs running. The -l includes the process id. )
ps -f #( The jobs started will terminate on their own in 1 to 2 minutes)
|
Notice that jobs only lists the processes started concurrently or in the background. The 1st column lists a number in the brackets, []. These are the jobs id and will be sequentially numbered in the order the jobs were started.
The next column will either be blank or contain a + or -. The most recently manipulated job will have a plus and the one before that the minus. The rest will be blank.
The third column lists the process ids. This will usually be a larger number, most likely in ascending order but also be randomly spaces. Job ids are unique to the current shell. Process ids are unique to the whole system.
The forth column lists the status of the jobs. For now they should all be running.
Finally, the command will be listed.
Invoking jobs without the option will list the same except for the process ids.
To bring a jobs into the foreground, use the fg %job-id command, where job-id is the specific job number..
fg %2
If fg is invoked with no job id, the job with the + in front of it will begin to run as a foreground process.
To put the process back in the background, type in [ctrl]z. This signals the process to become a background job with suspended or stopped status. And this job's id will be displayed. To restart it in the background, use bg. If you perform bg immediately after the [ctrl]z, you will not need to specify the job id. If you need to specifically target a suspended background job, use the %job-id with the command.
bg and fg are builtin shell commands. As such, they will only work on jobs started from the current terminal and they will only take job ids.
Unix provides the signal mechanism to communicate condition changes to a process in memory. You have already sent signals if you have pressed [ctrl]c or [ctrl]z. And bg is a builtin that generates a signal for you.
Signals are sent to the target process and the process may choose to ignore the signal, allow a default action to occur, or trap the signal and handle in a special way. Some signals can not be trapped or ignored. And we will look at trapping signals when we work with shell scripts.
The primary user command for sending signals is the kill command.
kill is normally used to terminate a background job or a process started from another terminal. However, it can be used request other changes in the status of a process. You must be the owner of the process to successfully send a signal.
There are a large number of signals known by a system but only some of them are used by the user. To see the list, see the man page section 7 on signals and also info on kill. You can also see a list of signals and short descriptions with /bin/kill -t
The most useful user signals are :
Signal name | SignalCommand equivalent | Description | |
SIGHUP | 1 | kill -HUP | Hang up |
SIGINT | 2 | [ctrl]c | Interrupt |
SIGQUIT | 3 | [ctrl]\ | Interrupt (core dump) |
SIGKILL | 9 | kill -KILL | Untrappable, kill (terminate) |
SIGTERM | 15 | kill | Standard termination |
SIGCONT | 18 | bg | Restart a paused process. |
SIGSTOP | 19 | kill -STOP | Untrappable, suspend execution |
SIGTSTP | 15 | [ctrl]z | Suspend execution. |
The syntax of kill is :
kill [-signal] process_id_list
kill 11735
The option for kill specifies the signal sent to the process. The default is 15 or SIGTERM. You may specify an alternative signal. It is a numeric value, but most versions on kill support a symbolic form of the numeric option.
For example, the signal option to request a hang up is -1 or -SIGHUP or -HUP.
kill -HUP 12354
The numeric value for some signals may vary from system to system depending on the cpu of the system. So it is better to use the symbolic options.
Also, most shells have a builtin version of kill which may behave differently from the external kill program. The bash version of kill may take either a process id or a job id. But the external version, /bin/kill, does not.
Lets look at the signals listed above in more detail.
SIGHUP - (-HUP) requests a hangup. It was initially designed to signal a modem to hang up after properly terminating the communication. Many Unix services or daemons respond to a HUP by re-reading their configuration files rather than shutting down. SIGHUP can be trapped and either ignored or handled special.
SIGHUP allows the process to close files and do other housekeeping before terminating.
If you have a process that you have lost interaction with such as a vi session that is in limbo because you lost your network connection while working on it, try using kill -HUP on the vi session once connection is reestablished.
vi keeps a backup of the file being edited. If you terminate it with -HUP, it may be able to write changes in memory before terminating. You can then attempt to recover it with vi -r.
SIGINT - (-INT) is the signal generated by [ctrl]c. The [ctrl]c must be issued from the keyboard and only to a foreground process. The signal can be sent to a background process or even a process running from another terminal. SIGINT can be trapped.
SIGQUIT - (-QUIT) is the signal generated by [ctrl]\. The [ctrl]\ must be issued from the keyboard and only to a foreground process. SIGQUIT is the same as SIGINT, except that if the command being interrupted is a binary (compiled) program, you will get a core dump that can be used for debugging.
SIGKILL - ( -kill or -KILL) is the terminate with prejudice signal. It cannot be trapped and will always succeed (as long as you own the process). Use this on processes that have gotten out of control. But keep in mind, that unlike SIGHUP, SIGKILL immediately terminates the process with no chance for it to perform any type of housekeeping.
SIGTERM - is the default of the standard kill with no options.
SIGCONT - ( -CONT ) restarts a suspended process in the background. It is the similar to bg, except that you can issue it from a different terminal. If the process requires foreground input, it may immediately go back into suspension.
SIGSTOP - ( -STOP ) stops or suspends the specified process. This signal cannot be trapped.
SIGTSTP - ( -TSTP ) stops or suspends the specified process. This is the signal generated by [ctrl]z. This signal can be trapped.
#( Create the following function get ) get () { read ans; } #( Run the following commands )
get & #( Run sleep in the foreground and use [ctrl]z to suspend it.)
sleep 3000 #( Now run another sleep in the background. Then suspend it with kill -STOP. If no other background processes were running, it should be job %4. The job and process ids should be displayed and you can confirm the job id. )
sleep 2500 & #( Now run jobs -l to see the different suspended states.)
|
What you see.
The read in the get function requires input from standard input, but because it is run in the background, the OS suspends it until it is brought into the foreground.
( Note that you can run a function as a background process. If you do, it gets its own process id and is launched in its own shell space. )
The vi bkg invokes the vi editor. However, the editor uses the curses library to communicate with the display terminal. Since it is in the background, it cannot do so and gets suspended.
The [ctrl]z suspends the currently running foreground process. The command interpreter restarts, becoming the foreground process.
And finally, we started sleep as a background process and used the STOP signal to suspend its execution.
To terminate these, try :
kill -HUP %1 %2 %3 %4
Give it a moment and run
jobs -l
This will probably terminate everything but the vi. To terminate any remaining jobs, invoke kill with the -KILL option and a list of the job ids that did not terminate.
kill -KILL %job-id
The c-shell and the bash shell support job control. The original version of the Bourne shell does not. And the bash emulation of the Bourne shell does not unless specifically started with job control. The Bourne shell does support background processes but all signal communication is done with process ids.
Everything we have tried with the kill command and the %job-ids could have been done with the process ids.