\documentclass[10pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}

\usepackage{enumerate}
\usepackage{listings}
\usepackage{url}
\usepackage{float}
\usepackage[toc,page]{appendix}

\usepackage[dutch]{babel}
\usepackage{listings}

\lstset{
language=C,
numbers=left,
numberstyle=\footnotesize,
stepnumber=1,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=4,
captionpos=t,
breaklines=false
}

% dot graphs
\usepackage{dot2texi}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows}

\title{Operating systems opdracht 3: \\ ICP met fork/pipe}
\author{Sander van Veen \& Tadde\"us Kroes \\
        6167969 \& 6054129 \\
		\url{sandervv@gmail.com} \& \url{taddeuskroes@hotmail.com} }

\begin{document}

\definecolor{darkgray}{rgb}{0.95,0.95,0.95}

\maketitle

\tableofcontents

\pagebreak

\section{Inleiding}

Deze opgave gaat over interprocess communication (IPC). Hieronder vallen het
cre\"eren van processen met behulp van \texttt{fork()}, het overbrengen van data
tussen deze processen met behulp van een \texttt{pipe()}, en het sturen van
signals. 

\subsection{Forken van processen}

Binnen POSIX-compatible besturingssystemen komen alle processen voort uit een
ouderproces, met als uitzondering \texttt{init}, want dat is de wortel van deze
procesboom. De meest gebruikte manier om een nieuwe process te starten is: 

\begin{enumerate}
    \item het huidige proces te forken met de functie \texttt{fork()}. Forken
          maakt een kindproces aan en geeft het ouderproces het procesnummer
          terug van het aangemaakte kindproces. In de taal C zou dat er zo uit
          kunnen zien:

          \begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
if( (cpid = fork()) < 0 ) 
    perror("fork() failure");
else if( cpid == 0 )
    child_process();
else
    parent_process();
          \end{lstlisting}
          Merk op dat \texttt{fork()} in feite een kopie maakt van het huidige
          process en dat na \texttt{fork()} het kind- en het ouderproces elk een
          eigen weg inslaan.
 
    \item de functie \texttt{exec()} gebruiken om het kindproces (tijdelijk) te
          vervangen door het nieuwe proces. De functie \texttt{exec()} is een
          \emph{front-end} voor de functie \texttt{execve()} en daarom kunnen
          ook alle andere functies worden gebruikt, die een {front-end} zijn voor
          \texttt{execve()}.
\end{enumerate}

\noindent          
In Linux maakt \texttt{fork()} gebruik van \emph{copy-on-write} voor het
geheugen. Daardoor zal \texttt{fork()} alleen CPU-tijd en geheugen nodig hebben
om de \emph{page tables} te kopie\"eren en de nieuwe \emph{task structure} aan
te maken voor het kindproces. Het gevolg is dat het forken van een proces
relatief gezien een goedkope operatie is.

\subsection{Data oversturen tussen processen}

Op vele verschillende manieren kan data tussen twee processen worden
uitgewisseld. Als de processen op dezelfde machine aanwezig zijn, kan er
bijvoorbeeld \emph{shared memory} worden gebruikt (zeer snel, maar de locks zijn
moeilijk te implementeren) en een pipe (makkelijke implementatie, minder snel
dan shared memory door de buffer). Als er data tussen twee processen moet worden
uitgewisseld, waarbij de processen niet op dezelfde machine draait, is het meest
voor de hand liggend om sockets te gebruiken. 

Sockets kunnen ook nuttig zijn voor processen op dezelfde machine, omdat het een
bi-directionele verbinding opzet. Een pipe heeft immers maar \'e\'en richting
(zie het figuur hieronder). Merk op dat het onder Solaris wel mogelijk is om
een bi-directionele pipe op te zetten. 
\\
\begin{dot2tex}[dot,options=--cache --autosize]
digraph pipe {
    rankdir=LR
    node[shape="box"]; pipe;
    node[shape="box",label="proces A"]; a;
    node[shape="box",label="proces B"]; b;
    a -> pipe [label="schrijven"];
    pipe -> b [label="lezen"];
}
\end{dot2tex}

Een unidirectionele pipe wordt in POSIX-compatible besturingssystemen met
\texttt{pipe()} aangemaakt. Dat geeft een array van file descriptors terug die
naar de uiteinden van de pipe gaan. In de meeste gevallen zal het ouderproces
schrijven naar het kindprocess en dan sluit het ouderproces \texttt{pipefd[0]}
en het kindproces sluit \texttt{pipefd[1]} met \texttt{close()}. Vervolgens
schrijft het ouderproces data met \texttt{write()} naar \texttt{pipefd[1]} en
het kindproces leest dan met \texttt{read()} de data uit \texttt{pipefd[0]}.
Als het kindproces data naar het ouderproces wil sturen, draaien \texttt{0} en
\texttt{1} van de file descriptors om.

Ook is er een functie, genaamd \texttt{mkfifo()}, die het mogelijk maakt om
een pipe aan het bestandsysteem te koppelen. Hiermee kunnen twee processen
communiceren door de output/input te schrijven naar een ``bestand''. Dit bestand
wordt niet op de hardeschijf opgeslagen, maar enkel aan het bestandssysteem
gekoppeld. Voorbeeld van een named pipe:

\begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
mkfifo --mode=0666 /tmp/sql_dump
gzip --stdout -d file.gz > /tmp/sql_dump
\end{lstlisting}

\noindent
en dan kan er in bijvoorbeeld MySQL gelezen worden door:

\begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
LOAD DATA INFILE '/tmp/sql_dump' INTO TABLE table_name;
\end{lstlisting}

\noindent
De uncompressed data wordt in dit geval niet op de hardeschijf opgeslagen, maar
direct doorgestuurd naar een buffer waaruit de MySQL server leest.

\subsection{Zombie- en wees-processen}

Dit zijn twee belangrijke begrippen die door de practicumleiding als volgt
worden beschreven: \\
De parent kan wachten op zijn child of ondertussen doorgaan (multitasking). In
het laatste geval kan het voorkomen dat het child klaar is, maar nog niet dood,
een zogenaamd `zombie', in de lijst met processen aangegeven met $<$node$>$.
Soms komt men onder Unix ook wel $<$defunct$>$ en $<$zombie$>$ als status
tegen. De omgekeerde toestand, een child waarvan de parent al klaar is, wordt
een `orphan' (Engels voor wees) proces genoemd.

\subsection{Signals sturen en ontvangen}

In de taal C is het mogelijk om een actie uit te voeren, als een signaal wordt
verstuurd naar een applicatie. Door middel van de functie \texttt{sigaction()}
kan de oude signal handler worden opgevraagd en een nieuwe signal handler worden
ingesteld. Voor de meeste signals is de handler standaard op \texttt{SIGIGN}
gezet (\emph{ignore}; geen actie uitvoeren).

Het instellen van signal handlers gebeurt over een geheel proces. Een
multithreaded proces deelt zijn handlers over de threads. Kindprocessen nemen
de signal handlers over van het ouderproces. Echter nemen kindprocessen die
voortkomen uit \texttt{exec()} niet de signal handlers van het ouderprocess
over, maar krijgen de standaard signal handlers toegewezen.

\begin{table}[H]
\begin{tabular}{|l|l|}
\hline
SIGINT  & Interrupt van toetsenbord (CTRL + C wordt ingedrukt) \\
SIGKILL & Kill signal (kill -9 \emph{proces\_id}) \\
SIGSEGV & Invalid memory reference (resulteert in een segfault) \\
SIGPIPE & Broken pipe: write to pipe, no readers (verbroken ssh connectie) \\
SIGTERM & Termination signal (kill proces\_id) \\
SIGCHLD & Kindproces is getermineerd \\
\hline
\end{tabular}
\caption{Veel gebruikte signals in POSIX-compatible besturingssystemen.}
\end{table}

Het is mogelijk om in de taal C met de functie \texttt{kill()} een signal te
sturen naar een proces. Dit kan ook vanuit de shell met het gelijknamige
commando \texttt{kill}, eventueel gevolgd door \texttt{-s} en het nummer van de
signal die dient te worden verstuurd. Het sturen van signals in de taal C komt
niet verder aan bod in dit practicum en zal daarom niet verder worden
behandeld.

\pagebreak

\section{De opdracht}

Door middel van de C-functie \texttt{getchar()} binnen een \emph{while}-loop
wordt de invoer van de gebruiker uitgelezen. Witregels en spaties (ascii: 32)
worden genegeerd.

\subsection{Fork en pipe implementatie}

Als er een \texttt{f} wordt gelezen, weet de bigfish dat er een nieuwe gup
dient te worden aangemaakt: \texttt{fork()} gebruiken en het kindproces
\texttt{gup()} laten aanroepen.

\begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
switch( (child_id = fork()) )
{
    case -1:
        perror("Child could not be created\n");
        exit(1);
    case 0:
        /* Child process continues.. */
        gup(log1, log2, pipe_id, kiddoCount);
        break;
    default:
        /* Parent proces continues.. */
        printf("Started child process %d with id %d\n",
               kiddoCount, child_id);
}
\end{lstlisting}

\noindent Door middel van \texttt{pipe()} wordt een array van file descriptors
aangemaakt, die wijzen naar het uiteinden van de pipe. Het aanmaken van de pipe
gebeurt voor \texttt{fork()}, anders kan er geen data worden verstuurd naar het
kindproces. Dan schrijft het ouderproces het karakter naar de ``voederbak'' met
\texttt{write()}.

\begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
if( write(pipe_id[1], &c, 1) <= 0 )
    perror("broken pipe.");
\end{lstlisting}

\noindent en \'e\'en van de kindprocessen leest vervolgens uit de pipe met
\texttt{read()}.

\begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
if( read(pipe_id[0], &c, 1) <= 0 )
    perror("unable to read from pipe.");
\end{lstlisting}

\noindent voordat het kindproces leest en het ouderproces schrijft, worden
\texttt{pipe\_id[1]} en respectievelijk \texttt{pipe\_id[0]} met
\texttt{close()} afgesloten. De aangemaakte pipe is immers eenrichtingsverkeer.

\subsection{Experimenten met logfiles}

Allereerst hebben wij zes tekstbestanden gemaakt en gevuld met de aangegeven
invoer van het practicum. Om eenvoudig te zien wat het resultaat was van de
verschillende invoer, is er een bash-scriptje gemaakt:

\begin{lstlisting}[language=bash,numbers=none,backgroundcolor=\color{darkgray}]
for i in {1..6}; do ./fish < input$i.txt; \
    ./view-logs.bash; done
\end{lstlisting}

\textbf{Waarnemingen}

\begin{enumerate}
\item Als in de logbestanden staat ``Child $n$ read from pipe with error and
      exited'', dan komt dat doordat het ouderproces is getermineerd en een van
      de kindprocessen \texttt{read()} aanroept. Dit geeft een fout omdat de
      pipe is verbroken (ouderproces bestaat immers niet meer).
\item Ook valt op dat de melding ``Program exited normally'' in de logfiles
      wordt geplaatst en er vervolgens nog gelezen wordt door het ouderproces.
      Dit heeft te maken met de buffer van de logfiles.
\end{enumerate}

% In de aangeleverde code worden met opzet meerdere verschillende log files
% worden aangemaakt, op verschillende plaatsen/tijdstippen en met verschillende
% eigenschappen. Bestudeer je log files ook steeds goed. Wat komt er in terecht?
% In welke volgorde? Ontbreekt er ook informatie? Probeer de verschillen te
% verklaren (in je verslag).

\subsection{Opruimen van zombie processen}

Wanneer een kindproces termineert verandert het in een zombie-proces (defunct).
Dit kan leiden tot een overbodig volle defunct-rij, daarom is het nuttig deze
processen op te ruimen. Dit wordt volgens stap 6 ge\"implementeerd m.b.v. een
\texttt{wait(NULL)} statement na het schrijven van het karakter `P' in de
pipe. Het probleem hierbij echter is dat als het kindproces dat de `P' zal
lezen antidote heeft, het niet zal termineren waardoor het ouderproces zal
blijven wachten op een statusverandering die niet zal komen. Dit probleem kan
worden opgelost door het gebruik van signals, dit wordt besproken in de
volgende paragraaf.

\subsection{Signal verwerking}

In stap 7 wordt gevraagd om een signal handler in te bouwen in de bigfish. Met
deze signal handler kunnen signal worden opgevangen en in dit geval schrijft de
handler een bericht naar \texttt{stdout} met de ontvangen signal. Voordat de
bigfish leest van \texttt{stdin}, worden de signal handlers als volgt
toegewezen:

\begin{lstlisting}[numbers=none,backgroundcolor=\color{darkgray}]
/* Bind signal handlers */
if( sigaction(SIGINT, &action, NULL)
    || sigaction(SIGTERM, &action, NULL)
    || sigaction(SIGCHLD, &action, NULL) )
{
    perror("binding the signal handlers failed.\n");
    exit(1);
}
\end{lstlisting}

% wait() beschrijven.

\noindent In de man page van \texttt{sigaction}\footnote{
http://www.kernel.org/doc/man-pages/online/pages/man2/sigaction.2.html}
staat een \emph{flag} met betrekking tot zombieprocessen en \texttt{SIGCHLD}:
\\
\\
\texttt{SA\_NOCLDWAIT (since Linux 2.6) \\ If signum is SIGCHLD, do not
transform children into zombies when they terminate. This flag is only
meaningful when establishing a handler for SIGCHLD.} \\
\\
Met die flag worden kindprocessen niet op defunct gezet, maar direct
vrijgegeven. Daardoor is er geen \texttt{wait()} meer nodig binnen de signal 
handler. \\
Aangezien de opdracht was om w\'el een \texttt{wait}-statement te gebruiken,
hebben we deze flag echter niet gebruikt.

\pagebreak

\begin{appendices}

\section{fishbones.c}

\lstinputlisting[language=c]{../fishbones.c}

\pagebreak

\section{view-logs.bash}

Om meerdere logbestanden tegelijkertijd te kunnen bekijken, is er een bash
scriptje voor GNU screen geschreven. Dit script start \texttt{screen} op en
laadt het \texttt{screenrc}-bestand (configuratie) in. Als GNU screen niet op
het systeem is geinstalleerd, zal het script dit proberen op te lossen. Dit
werkt alleen voor (afgeleiden van) Debian systemen.
\\
\lstinputlisting[language=bash]{../view-logs.bash}

\section{screenrc}

Meerdere logbestanden worden in een vertical split-view weergegeven. Merk op dat
\texttt{watch} er voor zorgt dat om de 2 seconde nieuwe data wordt weergegeven.
Met andere woorden: elke 2 seconden wordt de output van \texttt{cat
child.log$i$} weergegeven in een van de drie subterminals.

\lstinputlisting[language=bash]{../screenrc}

\end{appendices}

\end{document}
