Operating Systems

Department of Informatícs Engíneeríng
FCTUC
Uníversíty of Coímbra








Signals and Pipes
2
Signals

A sígnaí represents an asynchronous event whích an appíícatíon must
(shouíd? can?) process
 The programmer can regíster a routíne to handíe such events
 Exampíes:
 The user híts Ctrí+C  SIGINT

The system requests the appíícatíon to termínate  SIGTERM

The program tríed to wríte to a cíosed channeí  SIGPIPE
Process
normaí fíow
of executíon
ínt sígínt_handíer() {
// process sígnaí
}
SIGINT


3
Signals (2)

Sígnaís can be ín one of four states:
 Bíocked:
Upon arrívaí, they are stored ín a queue untíí the process unbíocks them.
Then, they are deíívered.
 Ignored:
Upon arrívaí, they are díscarded. It ís as íf they had never exísted.

Beíng Handíed:
They are redírected to a sígnaí handíer whích ís caííed.

None of the above:
Non-handíed, non-bíocked or ígnored sígnaís. Upon arrívaí, they cause
program termínatíon.

Some sígnaís cannot be ígnored or handíed (e.g. SIGKILL)
 When a process starts, sígnaís are on theír "defauít behavíor".
 Some are ígnored, most are ín the "non-handíed, non-bíocked nor
ígnored state". If a sígnaí occurs, the process wííí díe.
4
Basic Signal Routines
typedef voíd (*síghandíer_t)(ínt);

síghandíer_t sígnaí(ínt sígnum, síghandíer_t handíer);
Redírects a certaín sígnaí () to a handíer routíne.

ínt kííí(píd_t píd, ínt síg);
Sends a sígnaí to a certaín process ídentífíed by a PID. (Note: íf píd ís 0,
sends to aíí processes ín the current process group.)
 ínt pause();
Bíocks the process untíí a sígnaí ís receíved.
prototype of the
handíer routíne
1h1 s1gac11oh{),
1h1 s1gpJocmask{),
1h1 s1gpehd1hg{),
1h1 s1gsuspehd{),
Note: These are the
recommended POSIX routínes.
We are not goíng to cover them
here. The probíem wíth sígnaí()
ís that ín certaín cases íts
behavíor ís undefíned across
systems.


5
Handling a signal
vo1d s1g1h1{1h1 s1ghum) {
chaJ op11oh|2],
pJ1h11{"\h ´C pJessed. 0o you wah1 1o aboJ1? "),
scah1{"º1s", op11oh),
11 {op11oh|0] == `y`) {
pJ1h11{"0k, bye bye!\h"),
ex11{0),
}
}
1h1 ma1h{)
{
// Red1Jec1s STCThT 1o s1g1h1{)
s1ghaì{STCThT, s1g1h1),
// 0o some woJk!
wh1ìe {1) {
pJ1h11{"0o1hg some woJk...\h"),
sìeep{1),
}
Je1uJh 0,
}
6
Special constants in signal()
sígnaí(SIGINT, SlG_lGN)
Ignores SIGINT
sígnaí(SIGINT, SlG_DFL)
Restores SIGINT to íts
"defauít" handííng


7
Blocking a signal
vo1d s1g1h1{1h1 s1ghum) {
...
}
1h1 ma1h{)
{
s1ghaì{STCThT, s1g1h1),
s1gse1_1 bìock_c1Jìc,
s1gemp1yse1 {&bìock_c1Jìc),
s1gaddse1 {&bìock_c1Jìc, STCThT),
// 0o some woJk!
wh1ìe {1) {

s1gpJocmask {STC_BL0CK, &bìock_c1Jìc, hbLL),
pJ1h11{"0o1hg some woJk...\h"),
sìeep{5),
pJ1h11{"Ehd o1 ¸ob.\h"),
s1gpJocmask {STC_bhBL0CK, &bìock_c1Jìc, hbLL),
}
Je1uJh 0,
}
8
The problem with signals

They make programmíng extremely hard

It´s compíeteíy asynchronous: you never know when you
are goíng to get a sígnaí

Thís means that you have to protect aíí caíís!

After caíííng a standard functíon, ít may return -1
índícatíng an error

errno==EINTR means that a certaín routíne was
ínterrupted and has to be tríed agaín.

Other routínes return other thíngs.

It you are usíng sígnaís, you have to protect them agaínst
aíí that!


9
The problem with signals (2)

For ínstance, símpíy to try to read a "struct
person" from dísk...
s1Juc1 peJsoh p,
...
1h1 h, 1o1aì = 0,
wh1ìe {1o1aì < s1zeo1{p))
{
h = Jead{1d, {chaJ*)p + 1o1aì, s1zeo1{p)-1o1aì),
11 {h == -1)
{
11 {eJJho == EThTR)
coh11hue,
eìse
{
// TJue eJJoJ!
}
}
1o1aì+= h,
}
And you have to do somethíng ííke thís
for aíí caíís beíng done ín your program!
10
Sending a signal

It´s |ust a questíon of caíííng kííí() wíth the PID of
the target process...
vo1d mas1eJ{p1d_1 p1d_soh)
{
pJ1h11{"has1eJ sìeep1hg 1oJ a wh1ìe...\h"),
sìeep{3),
pJ1h11{"has1eJ says. heììo soh!\h"),
kÍ11{pÍd_son, 5ICu5ß1)¦
}
1h1 ma1h{) {
p1d_1 soh,
// CJea1es a woJkeJ pJocess
11 {{soh=1oJk{)) == 0) {
woJkeJ{),
ex11{0),
}

// The mas1eJ
mas1eJ{soh),
wa11{hbLL),

Je1uJh 0,
}


11
The code of the child process...
vo1d dady_caìì{1h1 s1ghum)
{
pJ1h11{"0ady has ¸us1 caììed 1h!\h"),
}
vo1d woJkeJ{)
{
// Red1Jec1 "useJ s1ghaì 1" 1o a hahdìeJ Jou11he
s1ghaì{STCbSR1, dady_caìì),
// 0o some woJk
pJ1h11{"Ch1ìd pJocess, ì11e 1s good...\h"),
1oJ {1h1 1=0, 1<10, 1++)
{
pJ1h11{"Ch1ìd do1hg some woJk\h"),
sìeep{1),
}

pJ1h11{"Ch1ìd say1hg bye bye!\h"),
}
12
Danger!!!

What do you thínk ít wííí happen íf you receíve a sígnaí ínsíde a
sígnaí handíer??

In most systems, upon enteríng a sígnaí handííng routíne, aíí
sígnaís of that type become bíocked (í.e. they are queued).
|Weíí, for "normaí" sígnaís, a fíníte set of them are queued
(typícaííy 1); for "reaí tíme sígnaís", aíí are...|

The other sígnaís are stííí processed asynchronousíy íf they
arríve.

Thís behavíor ís not consístent across systems. In fact, ín some
systems, that sígnaí type resets to íts defauít behavíor. Thís
means that íf, meanwhííe, the program receíves a sígnaí of the
same type ít may díe! On that type of system, the fírst thíng that
you must do ís to once agaín set the sígnaí handíer.

Weíí... doesn´t reaííy soíve the probíem, ít |ust makes ít íess
ííkeíy.

The new POSIX routínes address thís - use them. Aíso, most
system nowadays don´t reset the sígnaí handíer.
vo1d dady_caìì{1h1 s1ghum)
{
s1ghaì{STCbSR1, dady_caìì),
pJ1h11{"0ady has ¸us1 caììed 1h!\h"),
}


14
Some Signals


15
Stream mode of communication

Pípes and Named Pípes aííow processes to communícate usíng
"streams of data"

A "pípe" ís a connectíon between two processes. You can send
thíngs through the pípe, you can try to receíve thíngs from the
pípe.

A pípe acts ííke a synchronous fíníte buffer.

If a process tríes to wríte to a pípe that ís fuíí, ít bíocks

If a process tríes to read from a pípe that ís empty, ít bíocks
wríte() read()

16
Pipes

Provídes for communícatíon amount processes that are
híerarchícaííy reíated (í.e. father-chííd)

Pípes must be created príor to creatíng chííd processes

Whenever a pípe ís created, usíng pípe(), two fííe descríptors are
opened: one for readíng (fd|0|), one for wrítíng (fd|1|)

Unused fííe descríptors shouíd be cíosed!

Pípes are unídírectíonaí
fd|1| fd|0|
1h1 1d|2],
p1pe{1d),
readíng
wrítíng


17
Example
1ypede1 s1Juc1 {
1h1 a,
1h1 b,
} humbeJs,
// F1ìe descJ1p1oJs 1oJ 1he p1pe chahheì
1h1 chahheì|2],
{...)
1h1 ma1h{) {
// CJea1e a p1pe
p1pe{chahheì),
// CJea1e 1he pJocesses
11 {1oJk{) == 0) {
woJkeJ{),
ex11{0),
}
mas1eJ{),
wa11{hbLL),

Je1uJh 0,
}
18
Example (cont.)
vo1d woJkeJ{) {
humbeJs h,
cìose{chahheì|1]),
wh1ìe {1) {
read{channe1|6), &n, sÍzeo1{numbers))¦
pJ1h11{"|W0RKER] Rece1ved {ºd,ºd) 1Jom mas1eJ 1o add. Resuì1=ºd\h",
h.a, h.b, h.a+h.b),
}
}
vo1d mas1eJ{)
{
humbeJs h,

cìose{chahheì|0]),

wh1ìe {1) {
h.a = Jahd{) º 100,
h.b = Jahd{) º 100,

pJ1h11{"|hASTER] Sehd1hg {ºd,ºd) 1oJ W0RKER 1o add\h", h.a, h.b),
WrÍ1e{channe1|1), &n, sÍzeo1{numbers))¦
sìeep{2),
}
}


19
Be careful!

A pípe ís a fíníte buffer. If you try to wríte too much too quíckíy ínto
ít, the process wííí bíock untíí some space cíears up.

Atomícíty ís somethíng to be deaít wíth

If you try to wríte íess that PIPE_BUF bytes ínto a pípe, you are
guaranteed that ít wííí be wrítten atomícaííy

It you try to wríte more, you have no guarantíes! If severaí
processes are wrítíng at the same tíme, the wrítes can be
ínteríeaved

Aíso, when a process tríes to read from a pípe, you are not
guaranteed that ít wííí be abíe to read everythíng

Meaníng...

You must synchroníze your wrítes when you´re wrítíng a íot of
data!

You must ensure that you read compíete messages!
s1Juc1 peJsoh p,
1h1 h, 1o1aì = 0,
wh1ìe {1o1aì < s1zeo1{p)) {
h = Jead{1d|0], {chaJ*)p + 1o1aì, s1zeo1{p)-1o1aì),
1o1aì+= h,
}
20
Controlling File Descriptors

Each process has a fííe descríptor tabíe. By defauít, entríes 0,
1 and 2 are: stdín, stdout, stderr.

Each tíme a fííe ís open, an entry ís added to thís tabíe. Each
tíme a fííe ís cíosed, the correspondíng entry becomes
avaííabíe.

The process descríptor tabíe, ín fact, contaíns oníy references
to the OS gíobaí fííe descríptor tabíe.
<free>
<free>
0
1
2
3
4
5
6
stdín
stdout
stderr
f2
f3
Fííe Descríptor Tabíe after:
open("f1")
open("f2")
open("f3")
cíose("f1")


21
Controlling File Descriptors (2)

Two routínes are usefuí for controíííng fííe
descríptors:

ínt dup(ínt fd)
Dupíícates fííe descríptor "fd" on the fírst avaííabíe
posítíon of the fííe descríptor tabíe.

ínt dup2(ínt fd, ínt newfd)
Dupíícates fííe descríptor "fd" on the "newfd" posítíon,
cíosíng ít íf necessary.

Note that after a fííe descríptor ís dupíícated, the
orígínaí and the dupíícate can be used
ínterchangeabíy. They share the fííe poínters, the
buffers, íocks, etc.

Carefuí: Cíosíng one fííe descríptor doesn´t cíose aíí other
that have been dupíícated!
22
Ìmplementing a pipe between two processes

Impíementíng a pípe
between two processes
ís quíte easy. It´s oníy
necessary to assocíate
the standard output of
one process wíth the
standard ínput of
another.

Símpíe exampíe: "ís |
sort".

Note: cíosíng one fííe
descríptor doesn´t cíose
aíí other that have been
dupíícated!


23
Resulting in...

Sign up to vote on this title
UsefulNot useful