0% found this document useful (0 votes)
8 views17 pages

OS2-lab1

The document provides an overview of signals in operating systems, describing them as software interrupts used for communication between processes and the operating system. It explains how signals can alter program flow, their various types, default actions, and how they can be sent and handled in Unix programming. Additionally, it discusses the significance of asynchronous events and inter-process communication through signals, as well as the limitations on certain signals that cannot be ignored or handled.

Uploaded by

shahed1022drdr
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views17 pages

OS2-lab1

The document provides an overview of signals in operating systems, describing them as software interrupts used for communication between processes and the operating system. It explains how signals can alter program flow, their various types, default actions, and how they can be sent and handled in Unix programming. Additionally, it discusses the significance of asynchronous events and inter-process communication through signals, as well as the limitations on certain signals that cannot be ignored or handled.

Uploaded by

shahed1022drdr
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 17

Syrian Arab republic

Homs University
4th Year

Operating Systems -2-

Lab -1-

Signals

Eng. Gemma Wakim


1. What are signals and how are they used ?

A signal is a software interrupt, a way to communicate information to a


process about the state of other processes, the operating system, and the
hardware. A signal is an interrupt in the sense that it can change the flow
of the program —when a signal is delivered to a process, the process will
stop what its doing, either handle or ignore the signal, or in some cases
terminate, depending on the signal.

Signals also are delivered in an unpredictable way out of sequence with


the program because signals usually originate outside of the current
executing process. Another way to view signals is a mechanism for
handling asynchronous events. As opposed to synchronous events,
which is when a standard program executing iteratively, one line of code
following another, asynchronous events is when portions of the program
may execute out of order, or not immediately in a iterative style.
Asynchronous events are typically due to external events at the interaction
layer between the hardware and the operating system; the signal, itself, is
the way for the operating system to communicate these events to the
processes.

1.1 How we use signals ?

Signals are used for a wide variety of purposes in Unix programming, and
we've already used them in smaller contexts. For example, when we are
working in the shell and wish to "kill all cat programs" we type the
command:

The killall command will send a signal to all processes named cat that
says "terminate." The actually signal being sent is SIGTERM, whose
purposes is to communicate a termination request to a given proces, but
the process does not actually have to terminate … more on that later.
We've also used and looked at signals in the context of terminal
signaling which is how programs stop, start and terminate. When we type
Ctrl-c that is the same as sending a SIGINT signal, and when we type Ctrl-
z that is the same as sending a SIGTSTP signal, and when we type fg or
bg that is the same as sending a SIGCONT signal. Each of these signals
describe an action that the process should take in response. This action
is outside the normal control flow of the program, the events arrive
asynchronously requiring the process to interrupt its current operations
to responde to the event. For above signals, the response is clear —
SIGTERM terminate, SIGSTOP stop, SIGCONT continue — but for other
signals, the programmer can choose the correct response, which may be
to simply ignore the signal all together.

2. The Wide World of Signals

Every signal has a name, it starts with SIG and ends with a description.
We can view all the signals in section 7 of the man pages, below are the
standard Linux signals you're likely to interact with:
2.1 Signal Names and Values

Notice that each signal has a name, value, and default action. The signal
name should start to become a bit more familiar, the value of a signal is
actually the same as the signal itself. In fact, the signal name is just a
#defined value and we can see this by looking at the sys/signal.h header
file:
In code we use both the #defined value and the number. In general, it is
easier to remeber the name of the signal, but some signals are often
refered to by value, in particular, SIGKILL, whose value 9 is affectionately
used in the phrase: "Kill 9 that process."

2.2 Default Actions of Signals

Also, each signal has a default action. There are four described in the
table:

 Term : The process will terminate


 Core : The process will terminate and produce a core dump file that
traces the process state at the time of termination.
 Ign : The process will ignore the signal
 Stop : The process will stop, like with a Ctrl-Z
 Cont : The process will continue from being stopped

As we can see later, for some signals, we can change the default actions.
A few signals, which are control signals, cannot have their default action
changed, these include SIGKILL and SIGSTOP, which is why "kill 9" is
the ultimate kill statement.

3. Signals from the Command Line

Terminology for delivering signals is to "kill" a process with the kill


command. The kill command is actually poorly named — originally, it was
only used to kill or terminate a process, but it is currently used to send any
kind of signal to a process. The difference between kill and killall is that
kill only sends signals to process identified by their pid, killall sends the
signal to all process of a given name.
3.1 Preparing for the kill

A good exercise to explore the variety of signals and how to use them is
to actually use them from the command line. To start, we can open two
terminals, in one, we execute the loop program:

Which will just loop forever, and in other terminal, we will kill this process
with various sginals to see how it responds. Let's start with a signal we all
love to hate, the signal that indicates a Segmentation Fault occurs. You
might not have realized this, but a Segmentation Fault is a signal
generated from the O.S. that something bad has happened. Let's simulate
that effect:

And in the in the terminal where =loop is running, the result is eerily
familiar.

The 11 following the message is the signal number: 11 is the signal


number for SIGSEGV. Note that the default response to a SIGSEGV is to
terminate with a core dump. We can explore some of the more esoteric
signals and see similar results occur when the program terminates:
3.2 Sending Terminal Signals with Kill

Let's restart the loop program and use kill to simulate terminal signaling.
We've been discussing how the terminal control will deliver signals to stop,
continue, and terminate a process; there's no mystery here. Those signals
are signals that you can send yourself with kill.

Let's look at starting the loop program again, but this time

And again, the result in the other terminal is quite familiar:

If we were to run jobs, we can see that loop is stopped in the background.
This is the same as typing Ctrl-z in the terminal.
Before, we'd continue the loop program with a call to bg , but we can use
kill to do that too. From the other terminal:

And, after we run jobs, the loop program is running in the background:

Finally, let's terminate the loop program. The Ctrl-c from the terminal
actually generates the SIGINT signal, which stands for "interrupt" because
a Ctrl-c initiates an interrupt of the foreground process, which by default
terminates the process.

And the expected result:

4. Handling and Generating Signals

Now that we have a decent understanding of signals and how they


communicate information to a process, let's move on to investigate how
we can write program that take some action based on a signal. This is
described as signal handling, a program that handles a signal, either by
ignoring it or taking some action when the signal is delivered. We will also
explore how signals can be sent from one program to another, again, we'll
use a kill for that.
4.1 Hello world of Signal Handling

The primary system call for signal handling is signal(), which given a
signal and function, will execute the function whenever the signal is
delivered. This function is called the signal handler because it handles
the signal. The signal() function has a strange declaration:

That is, signal takes two arguments: the first argument is the signal
number, such as SIGSTOP or SIGINT, and the second is a reference to
a handler function whose first argument is an int and returns void. It's
probably best to explore signal() through an example, and hello world
program is where we always start.
The above program first establishes a signal handler for the user signal
SIGUSR1. The signal handling function hello() does as expected: prints
"Hello World!" to stdout. The program then sends itself the SIGUSR1
signal, which is accomplished via raise(), and the result of executing the
program is the beautiful phrase:

4.2 Asynchronous Execution

Some key points to take away from the hello program is that the second
argument to signal() is a function pointer, a reference to a function to call.
This tells the operating system that whenever this signal is sent to this
process, run this function as the signal handler.

Also, the execution of the signal handler is asynchronous, which means


the current state of the program will be paused while the signal handler
executes, and then execution will resume from the pause point, much like
context switching.

Let's look at another example hello world program:


The above program will set a signal handler for SIGINT the signal that is
generated when you type Ctrl-C. The question is, when we execute this
program, what will happen when we type Ctrl-C?

To start, let's consider the execution of the program. It will register the
signal handler and then will enter the infinite loop. When we hit Ctrl-C, we
can all agree that the signal handler hello() should execute and "Hello
World!" prints to the screen, but the program was in an infinite loop. In
order to print "Hello World!" it must have been the case that it broke the
loop to execute the signal handler, right? So it should exit the loop as well
as the program. Let's see:

As the output indicates, every time we issued Ctrl-C "Hello World!" prints,
but the program returns to the infiinite loop. It is only after issue a SIGQUIT
signal with Ctrl- \ did the program actually exit.

While the interoperation that the loop would exit is reasonable, it doesn't
consider the primary reason for signal handling, that is, asynchronous
event handling. That means the signal handler acts out of the standard
flow of the control of the program; in fact, the whole program is saved
within a context, and a new context is created just for the signal handler
to execute in. If you think about it some more, you realize that this is pretty
cool, and also a totally new way to view programming.
4.3 Inter Process Communication

Signals are also a key means for inter-process communication. One


process can send a signal to another indicating that an action should be
taken. To send a signal to a particular process, we use the kill() system
call. The function declaration is below.

Much like the command line version, kill() takes a process identifier and
a signal, in this case the signal value as an int, but the value is #defined
so you can use the name. Let's see it in use.

Main():
In this program, first a signal handler is established for SIGUSR1, the
hello() function. After the fork, the parent calls wait() and the child will
communicate to the parent by "killing" it with the SIGUSR1 signal. The
result is that the handler is invoked in the parent and "Hello World!" is
printed to stdout from the parent.

While this is a small example, signals are integral to inter process


communication. In previous lessons, we've discussed how to
communicate data between process with pipe(), signals is the way
process communicate state changes and other asynchronous events.
Perhaps most relevant is state change in child processes. The SIGCHLD
signal is the signal that gets delivered to the parent when a child
terminates. So far, we've been handling this signal implicitly through
wait(), but you can choose instead to handle SIGCHLD and take different
actions when a child terminates. We'll look at that in more detail in a future
lesson.
4.4 Ignoring Signals

While we've so far looked at changing the actions for a set of signals using
a handler. So far, our handlers have been doing things — mostly, printing
"Hello World!" — but we might just want our handler to do nothing,
essentially, ignoring the signal. That is easy enough to write in code, for
example, here is a program that will ignore SIGINT by handling the signal
and do nothing:

And if we run this program, we see that, yes, it Ctrl-c is ineffective and we
have to use Ctrl-\ to quit the program:

But, it would seem like a pain to always have to write the silly little ignore
function that does nothing, and so, when there is a need, there's a way.
The signal.h header defines a set of actions that can be used in place of
the handler:

 SIG_IGN : Ignore the signal

 SIG_DFL : Replace the current signal handler with the default

handler With these keywords, we can rewrite the program simply as:

4.5 Some signals are more equal than others


The last note on signal handling is that not all signals are created equal,
and some signals are more equal than others. That means, that you
cannot handle all signals because it could potentially place the system in
an unrecoverable state. The two signals that can never be ignored or
handled are: SIGKILL and SIGTSTOP. Let's look at an example:
The above program tries to set the ignore signal handler for SIGSTOP,
and then goes into an infinite loop. If we execute the program, we find that
oure efforts were fruitless:

The program did stop. And we can see the same for a program that
ignores SIGKILL.

The reasons for this are clearer when you consider that all programs must
have a way to stop and terminate. These processes cannot be interfered
with otherwise operating system would loose control of execution traces.
4.6 Checking Errors of signal()
The signal() function returns a pointer to the previous signal handler,
which means that here, again, is a system call that we cannot error check
in the typical way, by checking if the return value is less than 0. This is
because a pointer type is unsigned, there is no such thing as negative
pointers.

Instead, a special value is used SIG_ERR which we can compare the


return value of signal(). Here, again, is the program where we try and
ignore SIGKILL, but this time with proper error checking:

And the output from the perror() is clear:

The invalid argument is SIGKILL which cannot be handled or ignored.

You might also like