UNIX System Calls, Signals, and Shells

Download Report

Transcript UNIX System Calls, Signals, and Shells

Shells, System Calls, and Signals
What is a Shell?
• A shell is a command line interface to the
operating system
– Fetch a command from the user and execute the
command
• Sometimes the commands are built-in to the shell
• Other times the commands are external system
programs or user programs
• There are lots of different shells available in
UNIX
Bourne Shell
• Historically the sh language was the first to be
created and goes under the name of The
Bourne Shell
• It has a very compact syntax which makes it
obtuse for novice users but very efficient
when used by experts
• It also contains some powerful constructs built
in
Bourne Shell
• On UNIX systems, most of the scripts used to
start and configure the operating system are
written in the Bourne shell
• It has been around for so long that is virtually
bug free
C Shell
• The C Shell (csh)
– Similar syntactical structures to the C language
• The UNIX man pages contain almost twice as
much information for the C Shell as the pages
for the Bourne Shell, leading most users to
believe that it is twice as good
C Shell
• Actually, there are several compromises within
the C Shell which makes using the language
for serious work difficult
• (Check the list of bugs at the end of the man
pages!).
C Shell
• The real reason why the C Shell is so popular is
that it is usually selected as the default login
shell for most users
• The features that guarantee its continued use
in this arena are aliases and history lists
tcsh – An Enhanced C Shell
• An enhanced but completely compatible
version of the Berkeley UNIX C Shell, csh
• It is a command language interpreter usable
both as an interactive login shell and a shell
script command processor
• Uses a C-like syntax
tcsh – An Enhanced C Shell
• It includes:
– Command-line editor
– Programmable word completion
– Spelling correction
– History mechanism
– Job control
BASH
• GNU Bourne Again Shell
• A complete implementation of the IEEE
POSIX.2 and Open Group Shell specificaiton
with…
– Interactive command line editing
– Job control on architectures that support it
– Csh-like features such as history substitution and
brace expansion
– …and a slew of other features
Korne Shell
• The ksh was made famous by IBM’s AIX
version of UNIX
• The Korne Shell can be thought of as a
superset of the Borne Shell as it contains the
whole of the Borne Shell world within its own
syntax rules
Processes and the CWD
• Every process runs in a directory
– The attribute is called the “current working directory”
(cwd)
• Finding the CWD
char *getcwd( char *buf, size_t size );
– Returns a string that contains the absolute pathname of
the current working directory
• There are functions that can be used to change the
current working directory (chdir)
Other Process Attributes
• Getting the process id number
#include <unistd.h>
pid_t getpid( void );
• Getting the group id number
gid_t getgid( void );
• Getting the real user ID of a process
uid_t getuid( void );
Creating a Process
• The only way to create a new process is to
issue the fork() system call
• Fork() splits the current process into 2
processes, one is called the parent and the
other is called the child
Parent and Child Processes
• The child process is a copy of the parent
process
• Same program
• Same place in the program
– Almost….
• The child process get a new process ID
Process Inheritance
• The child process inherits many attributes
from the parent including…
– Current working directory
– User id
– Group id
The fork() system call
#include <unistd.h>
Pid_t fork( void );
• fork() returns a process id (small unsigned
integer)
• fork() returns twice!!!!!!!
– In the parent process, fork returns the id of the
child process
– In the child, fork returns a 0
Example
• #include <unistd.h>
• #include <iostream>
• using namespace std;
• int main( int argc, char *argv[] )
• {
•
if( fork() )
•
cout << "I am the parent" << endl;
•
else
•
cout << "I am the child" << endl;
•
return( 0 );
• }
Bad Example (don’t do this)
• #include <unistd.h>
• #include <iostream>
• using namespace std;
// This is called a
// fork bomb!!!!!
// please don’t do this
• int main( int argc, char *argv[] )
• {
•
while( fork() )
•
cout << "I am the parent" << endl;
•
•
cout << "I am the child" << endl;
•
return( 0 );
• }
Switching Programs
• fork() is the only way to create a new process
• This would be almost useless if there was not
a way to switch what program is associated
with a process
• The exec() system call is used to start a new
program
exec()
• There are actually a number of exec functions
– execlp, execl, execle, execvp, execv, execve
• The difference between these functions is the
parameters
– How the new program is identified and some
attributes that should be set
The exec Family
• When you call a member of the exec family,
you give it the pathname of the executable file
that you want to run
• If all goes well, exec will never return!!!
• The process becomes the new program!!!
Execl()
• int execl( char *path,
char *arg0,
char *arg1,
…,
char *argN,
(char *) 0);
execl( “/home/bin/foobar”, “alpha”,
“beta”, NULL );
A Complete execl Example
•
•
•
#include <unistd.h>
#include <iostream>
using namespace std;
•
•
int main( int argc, char *argv[] )
{
•
•
char buf[ 1000 ];
cout << "Here are the files in " << getcwd( buf, 1000 ) <<
endl;
•
•
•
•
execl( "/bin/ls", "ls", "-al", NULL );
cout << "If all goes well, this line will not be printed!!!"
<< endl;
return( 0 );
}
fork() and exec() Together
• The following program does the following:
– fork() – results in 2 processes
– Parent prints out it’s PID and waits for child
process to finish (to exit)
– Child process prints out it’s PID and then exec()
“ls” and then exits
execandfork.cpp (1)
•
•
•
•
#include
#include
#include
#include
<unistd.h>
<iostream>
<sys/types.h>
<sys/wait.h>
• using namespace std;
//
//
//
//
exec, fork, getpid
cout
needed for wait
wait()
execandfork.cpp (2)
•
•
•
void child( void )
{
int pid = getpid();
•
•
cout << "CHILD: Child process PID is " << pid << endl;
cout << "CHILD: Child process now ready to exec ls" << endl;
•
•
execl( "/bin/ls", "ls", NULL );
}
execandfork.cpp (3)
•
•
•
•
•
•
•
•
•
void parent( void )
{
int pid = getpid();
int stat;
cout << "PARENT: Parent process PID is " << pid << endl;
cout << "PARENT: Parent waiting for child" << endl;
wait( &stat );
cout << "PARENT: Child is done. Parent returning" << endl;
}
execandfork.cpp (4)
•
•
int main( int argc, char *argv[] )
{
•
cout << "MAIN: Starting fork system call" << endl;
•
•
•
•
if( fork() )
parent();
else
child();
•
cout << "MAIN: Done" << endl;
•
•
return( 0 );
}
execandfork.cpp (output)
neptune.cs.kent.edu] {58}% a.out
MAIN: Starting fork system call
CHILD: Child process PID is 32557
CHILD: Child process now ready to exec ls
PARENT: Parent process PID is 32556
PARENT: Parent waiting for child
a.out
lowcost_DB_NOW.pdf
asc
mail
Backup
MASC
bin
MPISpawn2
hybrid_parallel_system.pdf Parallaxis
icp_fall2004_prj4.txt
Pictures
PARENT: Child is done. Parent returning
MAIN: Done
neptune.cs.kent.edu] {59}%
Project 1.pdf
public_html
Software
ZephyrDemo
A More Concise Example
int main()
{
pid_t pid;
/* fork another process */
pid = fork();
if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0) { /* child process */
execlp("/bin/ls", "ls", NULL);
}
else { /* parent process */
/* parent will wait for the child to complete */
wait (NULL);
printf ("Child Complete");
exit(0);
}
}
System Calls for Files and Directories
CALL
Description
fd = open( name, how )
Open a file for reading and/or writing
s = close( fd )
Close and open file
n = read( fd, buffer, size )
Read data from a file into a buffer
n = write( fd, buffer, size )
Write data from a buffer into a file
s = lseek( fd, offset, whence )
Move the “current” pointer for a file
s = stat( name, &buffer )
Get a file’s status information( in buffer )
s = mkdir( name, mode )
Create a new directory
s = rmdir( name )
Remove a directory (must be empty)
s = link( name1, name2 )
Create a new entry (name2) that points to
the same object as name1
s = unlink( name )
Remove the name as a link to an object
(deletes the object if name was the only link
to it)
More System Calls
Call
Description
pid = fork()
Create a child process identical to the
parent
pid = waitpid( pid, &statloc, options )
Wait for a child to terminate
s = execve( name, argv, environp )
Replace a process’ core image
exit( status )
Terminate process execution and return
status
s = chdir( dirname )
Change the working directory
s = chmod( name, mode )
Change a file’s protection bits
s = kill( pid, signal )
Send a signal to a process
seconds = time( &seconds )
Get the elapsed time since January 1, 1970
A Simple Shell
while( true )
{
type_prompt();
read_command( command, parameters );
if( fork() != 0 )
{
waitpid( -1, &status, 0 );
}
else
{
execve( command, parameters, 0 );
}
}
// repeat forever
// display prompt
// input from terminal
// fork off child process
// parent code
// wait for child to exit
// child code
// execute command
Signaling Processes
• Signal
– A signal is a notification to a process that an event has occurred.
Signals are sometimes called “software interrupts”.
• Features of Signal
– Signal usually occur asynchronously.
– The process does not know ahead of time exactly when a
signal will occur.
– Signal can be sent by one process to another process (or to
itself) or by the kernel to a process.
Sources for Generating Signals
• Hardware
– A process attempts to access addresses outside its own
address space.
– Divides by zero.
• Kernel
– Notifying the process that an I/O device for which it has
been waiting is available.
• Other Processes
– A child process notifying its parent process that it has
terminated.
• User
– Pressing keyboard sequences that generate a quit,
interrupt or stop signal.
Three Courses of Action
• Process that receives a signal can take one of three
action:
– Perform the system-specified default for the signal
• notify the parent process that it is terminating;
• generate a core file;
– (a file containing the current memory image of the process)
• terminate.
– Ignore the signal
• A process can do ignoring with all signal but two special signals:
SIGSTOP and SIGKILL.
– Catch the Signal (Trapping)
• When a process catches a signal, except SIGSTOP and SIGKILL, it
invokes a special signal handing routine.
POSIX-Defined Signals (1)
•
•
•
•
•
•
SIGALRM: Alarm timer time-out. Generated by alarm( ) API.
SIGABRT: Abort process execution. Generated by abort( ) API.
SIGFPE: Illegal mathematical operation.
SIGHUP: Controlling terminal hang-up.
SIGILL: Execution of an illegal machine instruction.
SIGINT: Process interruption.
– Can be generated by <Delete> or <ctrl_C> keys.
• SIGKILL: Sure kill a process. Can be generated by
– “kill -9 <process_id>“ command.
• SIGPIPE: Illegal write to a pipe.
• SIGQUIT: Process quit. Generated by <crtl_\> keys.
• SIGSEGV: Segmentation fault. generated by de-referencing a NULL pointer.
POSIX-Defined Signals (2)
• SIGTERM: process termination. Can be generated by
– “kill <process_id>” command.
• SIGUSR1: Reserved to be defined by user.
• SIGUSR2: Reserved to be defined by user.
• SIGCHLD: Sent to a parent process when its child process has
terminated.
• SIGCONT: Resume execution of a stopped process.
• SIGSTOP: Stop a process execution.
• SIGTTIN: Stop a background process when it tries to read
from its controlling terminal.
• SIGTSTP: Stop a process execution by the control_Z keys.
• SIGTTOUT: Stop a background process when it tries to write
to its controlling terminal.
Sending Signals
• You may send signals to a process connected to your
terminal by typing
– ^C
– ^\
– ^Z
SIGINT terminate execution
SIGQUIT
terminate and core dump
SIGSTOP
suspend for later
• The terminal driver is a program that processes I/O
to the terminal can detect these special character
sequences and send the appropriate signal to your
interactive shell.
• The shell in turn generates an appropriate signal to
the foreground process.
Kill
• The user can use the csh built-in kill command or
use regular UNIX kill command to send a specific
signal to a named process.
– % kill [-sig] process
• If no signal is specified, then SIGTERM (15)(terminate) is
assumed
• In C/C++ the system call is
– #include <signal.h>
– int kill( int pid, int sig_id );
– Return values: Success = 0, Failure = -1, Sets errno…YES
Signal Delivery and Processing
• When an interrupt or event causes a signal to
occur, the signal is added to a set of signals
that are waiting for delivery to a process.
• Signals are delivered to a process in a manner
similar to hardware interrupts.
Signal Delivery
• If the signal is not currently blocked by the process, it is
delivered to the process following these steps:
– The same signal is blocked from further occurrence until delivery and
processing are finished
– The current process context is saved and a new one built
– A handler function associated with the signal is called
– If the handler function returns, then the process resumes execution
from the point of interrupt, with its saved context restored. Among
other things, the signal mask is restored.
• Signals have the same priority
– But processes can block listening to specific signals via a signal mask
Signal Trapping
• The system call signal() is used to trap signals
– #include <signal.h>
– signal( int sig_id, void * handler() );
• Example: Write a C++ program to count the
number of times CTRL-C is pressed at the
terminal
– cc_counter.cpp
Alarms
• Function
– The alarm API requests the kernel to send the SIGALRM signal after a
certain number of real clock seconds.
– #include <signal.h>
– int alarm( unsigned int time_interval);
– Return:
• Success: the number of CPU seconds left in the process timer; Failure: 1; Sets errno: Yes
– Argument
• time_interval: the number of CPU seconds elapse time. After which the
kernel will send the SIGALRM signal to the calling process.
• Example: Write a C++ program to set an alarm for signal 5
seconds after process startup and trap the alarm signal.
– alarmer.cpp