Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
U
uva
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Taddeüs Kroes
uva
Commits
9d90ec9a
Commit
9d90ec9a
authored
Nov 21, 2010
by
Sander Mathijs van Veen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Almost finished report of assignment 4 of Operating Systems.
parent
3c6dc6ce
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
241 additions
and
35 deletions
+241
-35
os/ass4/report/report.tex
os/ass4/report/report.tex
+241
-35
No files found.
os/ass4/report/report.tex
View file @
9d90ec9a
...
...
@@ -91,7 +91,7 @@ elkaar functioneren. Merk op dat het mogelijk is om threads niet meer
onafhankelijk van elkaar te maken door
\texttt
{
join()
}
als host
\footnote
{
In dit
geval is een
\emph
{
host
}
de thread die andere threads aanmaakt.
}
op een of meer
threads uit te voeren. Dan voegen de threads samen en wordt de code na de
\texttt
{
join()
}
weer sequen
c
eel uitgevoerd. Soms kan geen
\texttt
{
join()
}
worden
\texttt
{
join()
}
weer sequen
ti
eel uitgevoerd. Soms kan geen
\texttt
{
join()
}
worden
uitgevoerd op een thread, want een thread kan als
\texttt
{
detached
}
worden
ingesteld.
...
...
@@ -126,7 +126,7 @@ komt doordat threads te maken hebben met \emph{pre-emptive scheduling}, meerdere
Debuggers zijn in veel gevallen een stuk minder geschikt om te gebruiken bij een
multithreaded applicatie. Doordat code tegelijkertijd kan worden uitgevoerd in
plaats van sequen
c
eel, is het debuggen van een stuk code lastiger. Het geheugen
plaats van sequen
ti
eel, is het debuggen van een stuk code lastiger. Het geheugen
kan namelijk in de tussentijd zijn aangepast door een andere thread, terwijl dat
bij een sequenceel programma niet kan.
...
...
@@ -178,7 +178,7 @@ een ontwikkelaar:
\item
Geen synchronisatieprobleem doordat de events sequenceel worden
afgehandeld in plaats van parallel.
\item
Er vinden geen deadlock- en/of starvationsituaties meer plaats, doordat
er geen locks worden gebruikt die de threads in goede banen leid
t
.
er geen locks worden gebruikt die de threads in goede banen leid
en
.
\item
De ontwikkelaar kan gemakkelijker met een debugger werken, want
synchronisatie en parallelisme vormen geen problemen meer.
\end{enumerate}
...
...
@@ -191,37 +191,13 @@ Dit komt doordat er geen \emph{locking overhead} en \emph{context switches}
noodzakelijk om alles parallel te laten lopen. Daar moet wel een kanttekening
bij worden geplaatst: als er meerdere CPU's aanwezig zijn en de taak in
onafhankelijke subtaken is op te delen, dan geeft een multithreaded applicatie
wel degelijk een voordeel. Dit gaat echt wel te kostte van veel ontwikkeltijd.
wel degelijk een voordeel. Dit gaat echt
er
wel te kostte van veel ontwikkeltijd.
% Events avoid concurrency as much as possible, threads embrace:
% - Easy to get started with events: no concurrency, no preemption, no
% synchronization, no deadlock.
% - Use complicated techniques only for unusual cases.
% - With threads, even the simplest application faces the full complexity.
%
% Debugging easier with events:
% - Timing dependencies only related to events, not to internal scheduling.
% - Problems easier to track down: slow response to button vs. corrupted memory.
%
% Events faster than threads on single CPU:
% - No locking overheads.
% - No context switching.
%
% Events more portable than threads.
%
% Threads provide true concurrency:
% - Can have long-running stateful handlers without freezes.
% - Scalable performance on multiple CPUs.
%
% Should You Abandon Threads?
%
% No: important for high-end servers (e.g. databases).
%
% But, avoid threads wherever possible:
% - Use events, not threads, for GUIs, distributed systems, low-end servers.
% - Only use threads where true CPU concurrency is needed.
% - Where threads needed, isolate usage in threaded application kernel: keep
% most of code single-threaded.
Het blijft dus de vraag of de kosten voor de ontwikkeling (loon) opwegen tegen
de kosten voor meer hardware. Als er geen duidelijke voorkeur is, zou ik
persoonlijk kiezen voor het bouwen een prototype dat is gebaseerd op events.
Als blijkt dat het systeem der mate slecht presteert, kan er altijd nog een deel
van de applicatie herschreven worden tot een multithreaded applicatie.
\pagebreak
...
...
@@ -232,9 +208,239 @@ Deze opgave gaat over het ``Dinerende Filosofen Probleem''\footnote{
probleemstelling is simpel: Er is een tafel, met een
$
n
$
filosofen, en
$
n
$
aantal eetstokjes. De filosofen willen afwisselend eten en nadenken. Elke
filosoof heeft twee eetstokjes nodig om te eten. Voor deze opgave dient men een
simulator te maken die dit probleem nabootst.
simulator te maken die dit probleem nabootst, gebruik makend van POSIX threads
(
\texttt
{
pthreads
}
). De eetstokjes dienen te worden gemodelleerd als mutexen
(
\texttt
{
pthread
\_
mutex
}
).
\subsection
{
De host van het diner
}
De oorspronkelijke thread (die wordt gestart vanuit
\texttt
{
main()
}
), wordt de
``host'' genoemd. De host voert de volgende taken uit:
\begin{enumerate}
\item
De host bepaalt hoeveel filosofen er gesimuleerd moeten worden (minimaal
2, maar er mag geen ingeprogrammeerde bovengrens zijn). Vanaf de
commandline kan het aantal te simuleren filosofen worden aangepast:
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
]
host
_
start( argc > 1 ? atoi(argv[1]) : 2 );
\end{lstlisting}
In
\texttt
{
host
\_
start()
}
wordt dan nog gecontroleerd of de gegeven
integer ook daadwerkelijk groter of gelijk aan 2 is. Mocht iemand anders
de functie
\texttt
{
host
\_
start()
}
aanroepen, dan is het goed dat alle
voorwaardes worden gecontroleerd (defensief programmeren).
\item
De host initialiseert de diverse benodigde variabelen, zoals de mutexen
voor de eetstokjes. Dit wordt opgelost in het volgende codefragment:
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
,xrightmargin=-1.2in]
pthread
_
attr
_
t attr;
pthread
_
t *threads = malloc(philos * sizeof(pthread
_
t));
forks = malloc(philos * sizeof(pthread
_
mutex
_
t));
// POSIX standard specifies that threads are joinable by default,
// but unfortunately, not all pthread implementations set threads
// as joinable by default. Therefore, set the thread's detach state.
pthread
_
attr
_
init(
&
attr);
pthread
_
attr
_
setdetachstate(
&
attr, PTHREAD
_
CREATE
_
JOINABLE);
// Let all philosophers wait before they can eat diner.
pthread
_
barrier
_
init(
&
wait
_
barrier, NULL, philos);
// Create the cutlery (mutexes) and initialise the statistics.
for(int i = 0; i < philos; i++)
{
pthread
_
mutex
_
init(
&
forks[i], NULL);
stats[i].meals = 0;
stats[i].forks = 0;
stats[i].id = i;
stats[i].locked = 0;
}
\end{lstlisting}
Merk op dat er een
\emph
{
barrier
}
wordt aangemaakt, want die zal er voor
zorgen dat elke filosoof netjes wacht totdat alle filosofen aan tafel
zitten.
\footnote
{
The
\emph
{
count
}
argument specifies the number of
threads that must call
\texttt
{
pthread
\_
barrier
\_
wait()
}
before any of
them successfully return from the call. From:
\url
{
http://manpages.
ubuntu.com/manpages/maverick/man3/pthread
_
barrier
_
init.3posix.html
}}
De
derde parameter is een getal die het aantal ``wachtende'' threads telt en
als dat in dit geval gelijk is aan alle filosofen, dan wordt deze
barri
\`
ere opgeheven.
In de struct
\texttt
{
diner
\_
stats
}
staat ook
\texttt
{
id
}
, wat het
stoelnummer voorstelt van de filosoof. Dit stoelnummer bepaalt de te
gebruiken vorken van de filosoof. Er is bewust gekozen om deze taak aan de
host van het diner te geven. Een nummertje trek je bij de slager, niet als
je uitgenodigd wordt voor een diner. De host bepaalt zo (zonder
voorbedachte raden) hoe de tafelschikking is.
\item
De host maakt de threads waarmee de filosofen zullen worden gesimuleerd,
en kiest het gedrag van elke individuele filosoof (daarover later meer).
De threads worden als volgt aangemaakt:
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
,xrightmargin=-.9in]
for(int i = 0; i < philos; i++)
{
printf("Host: inviting philo #
%d\n", i);
rc = pthread
_
create(threads+i,
&
attr, philo
_
start,
(void*)
&
stats[i]);
if(rc)
{
fprintf(stderr, "pthread
_
create() returned:
%d\n", rc);
exit(-1);
}
}
\end{lstlisting}
Merk op dat hier bewust geen gedrag aan een filosoof wordt toegewezen. Dit
is een eigenschap van de filosoof en is daarom verplaatst naar de functie
van een filosoof, die aangeroepen wordt als de filesoof ``binnenkomt''
(in deze opdracht is dat
\texttt
{
philo
\_
start()
}
).
\item
De host controleert minimaal 100 maal per seconde of er misschien deadlock
is ontstaan. Als dat mocht zijn gebeurd, zorgt hij ervoor dat de simulatie
netjes wordt beëindigd. Tussen de controles door mag de host geen CPU-tijd
gebruiken.
Deze eis was lastig te implementeren omdat een timer ook tijd gebruikt en
daardoor niet precies over
$
x
$
tijdseenheden kan stoppen. Omdat de context
switches een rol kunnen spelen bij een hoge load-factor van het systeem,
is er voor gekozen om een dynamische timer te maken. Dit houdt in dat elke
keer dat de deadlock-detectie wordt uitgevoerd de timer wordt bijgewerkt.
Ondanks dat het updaten van de timer enkele CPU cycles kost, levert dit
wel een betrouwbaarder resultaat op.
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
,xrightmargin=-.9in]
struct timeval wct;
gettimeofday(
&
wct, NULL);
double start
_
time = wct.tv
_
sec + wct.tv
_
usec / 1e6;
do
{
usleep(10000);
// Deadlock detection
int p;
for(p = 0; p < philos
&&
(stats[p].locked == 1
|| stats[p].locked == 2); p++);
if( p == philos )
{
puts("Host: deadlock occured.");
diner
_
finished = 1;
break;
}
// Update timer
gettimeofday(
&
wct, NULL);
}
while(wct.tv
_
sec+wct.tv
_
usec / 1e6 - start
_
time < TIME
_
LIMIT);
\end{lstlisting}
Gedurende de ontwikkeling van deze simulator is veelvoudig gebruikt
gemaakt van het commando
\texttt
{
time
}
om de runtime van het programma te
meten, buiten het programma om. De simulator benadert de 10 seconden (voor
zo ver dat mogelijk was om met
\texttt
{
time
}
te meten) zeer goed. Zo kan
het zijn dat er
$
10
.
002
$
wordt genoteerd, waarbij de 2 millisecondes te
verklaren zijn door het opruimen/aanmaken van de gebruikte threads en
mutexes. Het aanmaken/opruimen van deze benodigdheden wordt namelijk niet
meegerekend met de ge
\"
eiste 10 seconden grens van het diner.
Op het moment dat een deadlock (alle filosofen wachten op een linker- dan
wel rechtervork) wordt waargenomen door de host, is het over met de pret.
De host stopt het diner en laat elke filosoof betalen voor het aantal
hapjes dat gegeten is of het aantal keren dat een vork is gepakt. Uit de
opdracht is niet op te maken of de host na een deadlock het diner direct
laat be
\"
eindigen of dat hij er voor kiest om iedereen eerst te laten
betalen en dan weg te sturen. Er is voor het tweede gekozen, omdat het
niet vanzelfsprekend is dat er na een ruzie tussen de klanten iedereen
gratis naar huis kan.
\item
De host signaleert alle filosofen na 10 seconden dat het feest over is en
dat ze moeten gaan afrekenen. Hij voert een reeks
\texttt
{
pthread
\_
join()
}
s uit voor de simulatie-threads.
De oplossing om alle filosofen na 10 seconden niet meer te laten eten, is
in de vorige eis besproken. Ook het betalen van de filosofen is in
voorgaande eis besproken. De reeks
\texttt
{
pthread
\_
join()
}
s worden als
volgt uitgevoerd:
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
,xrightmargin=-.9in]
for(int i = 0; i < philos; i++)
{
// Reclaim all cutlery (mutexes) in reverse order.
for(int p = philos; p >= i; p--)
pthread
_
mutex
_
unlock(
&
forks[p]);
// Wait on the other philosophers.
if( (rc = pthread
_
join(threads[i],
&
status)) )
{
fprintf(stderr,
"return code from pthread
_
join() is
%d\n", rc);
exit(EXIT
_
FAILURE);
}
printf("Philo #
%d ate %d meal(s) and grabbed %d fork(s).\n",
i, stats[i].meals, stats[i].forks);
pthread
_
mutex
_
destroy(
&
forks[i]);
}
\end{lstlisting}
De vorken worden afgepakt van de filosofen, de filosofen verlaten het
feest en de vorken worden netjes ``opgeruimd''.
\end{enumerate}
\subsection
{
Code example
}
\subsection
{
De filosofen van het diner
}
Elke filosoof voert de volgende acties uit:
\begin{enumerate}
\item
Zij trekt een nummertje, waarmee de plaats aan tafel wordt bepaald en dus
ook de nummers van de te gebruiken eetstokjes (nb een apart af te schermen
kritieke sectie).
In de eisen van de host van het diner is genoemd waarom er geen nummertje
wordt getrokken. Als dit wel een belangrijk onderdeel was van de opdracht,
dan zou het als volgt zijn opgelost:
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
]
pthread
_
mutex
_
lock(
&
seat
_
lock);
stats->seat = get
_
seat();
pthread
_
mutex
_
unlock(
&
seat
_
lock);
\end{lstlisting}
In dit geval is de
\texttt
{
seat
}
hetzelfde als
\texttt
{
id
}
van de
\texttt
{
diner
\_
stats
}
(
\texttt
{
seat
}
is dit voorbeeld enkel een betere
benaming). De mutex voorkomt dat er tegelijkertijd een nummer kan worden
getrokken. Door het trekken van een nummer door de host te laten doen,
verdwijnt een kritieke sectie (omdat het zonder race conditions kan worden
gedaan door de host). Merk op dat het aantal locks zo laag mogelijk dient
te zijn, om het programma goed te kunnen onderhouden (meerdere locks maken
het programma immers onnodig complex).
\item
Zij meldt de volgende informatie (naar stdout, via printf; dit is weer
een aparte kritieke sectie): stoelnummer, soort gedrag.
Het is onduidelijk waarom dit een kritieke sectie is. Data die dient te
worden geprint, wordt niet zowel gelezen, dan wel geschreven door de andere
threads en heeft dus geen mutex nodig. Mocht het wel een eis van de
opdracht zijn dan zou het op de volgende manier zijn opgelost:
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
]
pthread
_
mutex
_
lock(
&
introduction
_
lock);
printf("Hey, my seat is
%d and i'm %s\n",
stats->id, stats->behaviour);
pthread
_
mutex
_
unlock(
&
introduction
_
lock);
\end{lstlisting}
\item
Zij probeert vervolgens twee eetstokjes te pakken (afhankelijk van
het gekozen gedrag kan dat mislukken, zie onder punt 4).
\item
Als het gelukt is die twee stokjes te pakken gaat ze eten en legt
vervolgens de twee stokjes weer netjes terug. Als er geen signaal is dat
er nu moet worden afgerekend, probeert ze weer opnieuw twee eetstokjes te
pakken (terug naar stap 3).
\item
Afrekenen bestaat er uit te melden hoe vaak deze filosoof heeft geprobeerd
stokjes en pakken, en hoe vaak ze feitelijk heeft gegeten. Deze eis is bij
de eisen van de host besproken.
\item
Na het afrekenen verlaat ze de zaak middels een
\texttt
{
pthread
\_
exit
}
.
\end{enumerate}
\begin{lstlisting}
[numbers=none,backgroundcolor=
\color
{
darkgray
}
]
code example
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment