45
Shells, System Calls, and Signals

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

Embed Size (px)

Citation preview

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 CWDchar *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 numbergid_t getgid( void );

• Getting the real user ID of a processuid_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> // This is called a• #include <iostream> // fork bomb!!!!!• using namespace std; // 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 <unistd.h> // exec, fork, getpid• #include <iostream> // cout• #include <sys/types.h> // needed for wait• #include <sys/wait.h> // wait()

• using namespace std;

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.outMAIN: Starting fork system callCHILD: Child process PID is 32557CHILD: Child process now ready to exec lsPARENT: Parent process PID is 32556PARENT: Parent waiting for childa.out lowcost_DB_NOW.pdf Project 1.pdfasc mail public_htmlBackup MASC Softwarebin MPISpawn2 ZephyrDemohybrid_parallel_system.pdf Parallaxis icp_fall2004_prj4.txt Pictures PARENT: Child is done. Parent returningMAIN: Doneneptune.cs.kent.edu] {59}%

A More Concise Exampleint 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

CALLCALL DescriptionDescription

fd = open( name, how )fd = open( name, how ) Open a file for reading and/or writingOpen a file for reading and/or writing

s = close( fd ) s = close( fd ) Close and open fileClose and open file

n = read( fd, buffer, size )n = read( fd, buffer, size ) Read data from a file into a bufferRead data from a file into a buffer

n = write( fd, buffer, size )n = write( fd, buffer, size ) Write data from a buffer into a fileWrite data from a buffer into a file

s = lseek( fd, offset, whence )s = lseek( fd, offset, whence ) Move the “current” pointer for a fileMove the “current” pointer for a file

s = stat( name, &buffer )s = stat( name, &buffer ) Get a file’s status information( in buffer )Get a file’s status information( in buffer )

s = mkdir( name, mode )s = mkdir( name, mode ) Create a new directoryCreate a new directory

s = rmdir( name )s = rmdir( name ) Remove a directory (must be empty)Remove a directory (must be empty)

s = link( name1, name2 )s = link( name1, name2 ) Create a new entry (name2) that points to Create a new entry (name2) that points to the same object as name1the same object as name1

s = unlink( name )s = unlink( name ) Remove the name as a link to an object Remove the name as a link to an object (deletes the object if name was the only link (deletes the object if name was the only link to it)to it)

More System CallsCallCall DescriptionDescription

pid = fork()pid = fork() Create a child process identical to the Create a child process identical to the parentparent

pid = waitpid( pid, &statloc, options )pid = waitpid( pid, &statloc, options ) Wait for a child to terminateWait for a child to terminate

s = execve( name, argv, environp )s = execve( name, argv, environp ) Replace a process’ core imageReplace a process’ core image

exit( status )exit( status ) Terminate process execution and return Terminate process execution and return statusstatus

s = chdir( dirname )s = chdir( dirname ) Change the working directoryChange the working directory

s = chmod( name, mode )s = chmod( name, mode ) Change a file’s protection bitsChange a file’s protection bits

s = kill( pid, signal )s = kill( pid, signal ) Send a signal to a processSend a signal to a process

seconds = time( &seconds )seconds = time( &seconds ) Get the elapsed time since January 1, 1970Get the elapsed time since January 1, 1970

A Simple Shellwhile( true ) // repeat forever{

type_prompt(); // display promptread_command( command, parameters ); // input from terminal

if( fork() != 0 ) // fork off child process{ // parent code

waitpid( -1, &status, 0 ); // wait for child to exit}else // child code{

execve( command, parameters, 0 ); // 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 SIGINT terminate execution– ^\ SIGQUIT terminate and core dump– ^Z 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