Subversion Repositories shark

Rev

Rev 1676 | Blame | Compare with Previous | Last modification | View Log | RSS feed

%----------------------------------------------------------------------------
\chapter{Utility functions}
%----------------------------------------------------------------------------

S.Ha.R.K. provides a set of utility functions aimed at getting information
about the kernel state. Mainly, they allow a user to get the actual
system time and some information concerning the tasks' state. Moreover,
it allows to set exception handlers and to manage interrupts.

%----------------------------------------------------------------------------
\section{Reading time}
%----------------------------------------------------------------------------

The S.Ha.R.K. Kernel does not have the concept of tick. Every time
interval and every absolute time in the system is measured usin the
Real-Time Clock available on the PC. To read the current time you
can use the following function:

%----------------------------------------------------------------------------
\begin{intest}
SYS\_GETTIME\index{sys\_gettime()}
\end{intest}

\begin{description}
\item [\texttt{TIME sys\_gettime(struct timespec *t);}]
\item [Description:]It returns the number of microseconds elapsed from
system's initialization, that is from the end of the \texttt{\_\_kernel\_register\_levels\_\_}
function. If the \texttt{t} value is not equal \texttt{NULL}, the
function fills also the timespec structure passed as parameter.
\end{description
}

%----------------------------------------------------------------------------
\section{Getting information on tasks}
%----------------------------------------------------------------------------

Since all the tasks are handled by a Module, it is a responsibility of each
Module to hide or not hide informations about the tasks handled by the system.
However, at the moment S.Ha.R.K. provides a function that simply prints the
tasks state on the console \footnote{Old versions of the kernel supported a
\texttt{void sys\_status(DWORD cw);} \index{sys\_status()}primitive. That
primitive is currently unsupported.
}.

\pagebreak

%----------------------------------------------------------------------------
\begin{intest}
PERROR\index{perror()}
\end{intest}

\begin{description}
\item [\texttt{void perror (const char *s);}]
\item [Description:]This is the POSIX \texttt{perror()} funcion, that prints
on the console (using \texttt{kern\_printf}) a message that explain
the meaning of the \texttt{errno} \index{errno} variable. Note that
each task has its own \texttt{errno} variable, as specified by the
\texttt{POSIX} standard.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
exec\_shadow\index{exec\_shadow}
\end{intest}
\begin{description}
\item [\texttt{PID exec\_shadow;}]
\item [Description:]This is the internal variable used by the Kernel to
track the running task. You can read its value to know the PID of
the current task. You CAN NOT modify this variable.
\end{description
}

%----------------------------------------------------------------------------
\section{Printing messages on the console}
%----------------------------------------------------------------------------

To print a simple message on the console, please use the c* functions
(cprintf\index{cprintf}, cputs\index{cputs}, \ldots{}) described
in Volume II. If tou are debugging \emph{the kernel}, you can use
kern\_printf \index{kern\_printf
} to print very simple messages without
floating point arithmetic.

%----------------------------------------------------------------------------
\chapter{Signals and Exception Handling}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\section{Signals}
%----------------------------------------------------------------------------

S.Ha.R.K. implements the specification of the signals and of the real-time
signald provided by the standard IEEE 1003.13 POSIX PSE51/PSE52. In
particular, you can use all the functions described into the IEEE
1003.1\{a,b\} standards, except that:

\begin{itemize}
\item all the \texttt{pid\_t} parameters and in general all parameters related
with processes should be ignored;
\item when in POSIX a signal cause the termination of the process, it causes
in S.Ha.R.K. the termination of the whole system (you can think S.Ha.R.K.
as a single process multithread kernel);
\item The \texttt{siginfo\_t} structure contains an additional parameter
called \texttt{si\_task} of type \texttt{PID}. It contains the \texttt{PID}
of the task that queued a particular real-time signal.
\end{itemize}

In particular, you can use these functions for signal handling: \texttt{kill},
\texttt{sigemptyset}, \texttt{sigfillset}, \texttt{sigaddset},
\texttt{sigdelset}, \texttt{sigismember}, \texttt{sigaction},
\texttt{pthread\_sigmask} \footnote{If you are not using the POSIX scheduling
modules please use \texttt{task\_sigmask}}, \texttt{sigprocmask},
\texttt{sigpending}, \texttt{sigsuspend}, \texttt{sigwait},
\texttt{sigwaitinfo}, \texttt{sigtimedwait}, \texttt{sigqueue},
\texttt{pthread\_kill} \footnote{If you are not using the POSIX scheduling
modules please use \texttt{task\_signal} (Note that \texttt{task\_kill} does not
send any signal, but issue a cancellation request on a task!)}, \texttt{alarm},
\texttt{pause}, \texttt{sleep} (note the difference between \texttt{sleep},
\texttt{task\_sleep} and \texttt{nanosleep}!), \texttt{raise}, \texttt{signal
}.

%----------------------------------------------------------------------------
\section{Exception handling\label{ch:except}}
%----------------------------------------------------------------------------

S.Ha.R.K. provides a flexible mechanism to handle the exceptions of the Kernel.
The mechanism is based on the POSIX signals. In fact, S.Ha.R.K. exceptions are
remapped on the real-time signal \texttt{SIGHEXC} \footnote{see
\texttt{include/signal.h}.} (9). Every time something goes wrong, the system
calls the primitive \texttt{kern\_raise}, that simply queue a real-time signal
of number SIGHEXC.

The user can define its own exception handler simply remapping the SIGHEXC
signal using the POSIX primitive \texttt{sigaction}. To fullfil the typical
usage of an exception handler (exit the system after printing a message), the
default behavior of the signal handler has been redefined to print a text
message on system shutdown. \footnote{Older versions of the Kernel supported two
functions to be used for standard redefinition of the kernel signal handler.
These functions, called SET\_EXCHANDLER\_TXT\index{set\_exchandler\_txt()} and
SET\_EXCHANDLER\_GRX\index{set\_exchandler\_grx()}, are no more supported, and
can be removed from your code without problems.}

Here is a sample code that explain how to redefine a signal handler:

\begin{verbatim}
#include <kernel/kern.h>

void thehandler(int signo, siginfo_t *info, void *extra) {

    /* the signal handler:
    info.sivalue.sival_int contains the exception number
    (see include/bits/errno.h)
   
    info.si_task is the task that raised the exception
    extra is not used
    */
    ...
}

...

int myfunc(...) {
    struct sigaction action;
    ...

    action.sa_flags = SA_SIGINFO;
    action.sa_sigaction = thehandler;
    action.sa_handler = 0;
    sigfillset(&action.sa_mask);
    sigaction(SIGHEXC, &action, NULL);

    ...
}
\end{verbatim
}

%----------------------------------------------------------------------------
\begin{intest}
KERN\_RAISE\index{kern\_raise()}
\end{intest}

\begin{description}
\item [\texttt{void kern\_raise(int n, PID p);}]
\item [Description:]This function uses the POSIX function sigqueue to put
a signal SIGHEXC into the signal queue. The parameter n is used as
the exception number, and it is passed into the siginfo\_t parameter
(into the sivalue.sival\_int field). The signal appears to be queued
by the task p (the p value is stored into the si\_task field of the
siginfo\_t structure passed as parameter).
\end{description
}

%----------------------------------------------------------------------------
\chapter{Interrupt and HW Ports handling}
\label{ch:interr
}
%----------------------------------------------------------------------------

Generally speaking, I/O to and from an external peripheral device
can be handled in three different ways depending on the peripheral
type and on the application:

\begin{itemize}
\item \textbf{Polling}: the program cyclically checks the status of the
I/O port, waiting for a input data to be ready or an output data to
be transmittable;
\item \textbf{Interrupt}: the program enables the I/O interface to send
a hardware interrupt every time an input data is available or an output
data is transmittable;
\item \textbf{DMA}: the program enables the interface to use DMA mechaninsm
for directly transferring data to/from memory.
\end{itemize}
\noindent In this chapter we will analyse the support that the S.Ha.R.K.
kernel provides for using the second method (interrupt).

When an interrupt arrives, a code for the hand-shake with the interface
and for transferring data has to be executed. This code can run in
two different modes:

\begin{itemize}
\item it can be entirely encapsulated in a function to be executed immediately
on the interrupt arrival, in the context of the executing task (\textit{fast
handler});
\item it can be entirely encapsulated in a task (\textit{safe handler})
which is activated on the interrupt arrival and scheduled with its
own priority together with the other tasks.
\end{itemize}
\noindent The first method is appropriate when the interrupt needs
a fast response time. Its potential drawback is that if its computation
time is not low, the overall schedulabuility can be severly affected.
This is because the guarantee algorithm does not take into account
the execution time of the interrupt handlers. The second method, on
the contrary, is perfectly integrated with the kernel's scheduling
mechanism, but can cause considerable delays in transferring data.

S.Ha.R.K. provides great flexibility in interrupt handling, since
it allows each interrupt to be associated with a \textit{fast handler},
a \texttt{safe handler}, or both.

On an interrupt's arrival the following operations are performed by
the kernel:

\begin{itemize}
\item The system checks whether a fast handler is associated with the interrupt.
If so, the interrupts are enabled and the handler is invoked. This
method allows a handler to be interrupted by a higher priority handler.
As an example, the keyboard handler (interrupt 1) can be interrupted
be the timer handler (interrupt 0).
\item The system checks whether a sporadic task (\emph{safe handler}) is
associated with the interrupt. If so, the task is activated and is
eligible to run with enabled interrupts.
\end{itemize
}
\noindent The system provides a set of functions for accessing the
hardaware interfaces' ports. In the drivers directory you can find
examples of a S.Ha.R.K. device driver.

%----------------------------------------------------------------------------
\section{Setting an interrupt handler}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\begin{intest}
HANDLER\_SET\index{handler\_set()}
\end{intest}

\begin{description}
\item [\texttt{int handler\_set(int no, void (*fast)(int), PID
pi, BYTE lock);}
]
\item [Description:]It installs function \texttt{fast} (fast handler) and
the sporadic task \texttt{p} (safe handler) on the interrupt identified
by \texttt{no}. The \texttt{no} parameter must belong to the range
1\ldots{}15 (interrupt 0 is associated to the timer and cannot be
intercepted). On the interrupt's arrival, function \texttt{fast} is
invoked and runs. Depending on the \texttt{lock} flag, the interrupts
are disabled (\texttt{lock} = TRUE) or enabled (\texttt{lock} = FALSE)
during handler execution. Furthermore, on the interrupt's arrival,
task \texttt{p} is activated.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
HANDLER\_REMOVE\index{handler\_remove()}
\end{intest}

\begin{description}
\item [\texttt{void handler\_remove(int no);}]
\item [Description:]It removes the handler of the interrupt number \texttt{intno};
the interrupt is masked.
\end{description
}

%----------------------------------------------------------------------------
\section{Reading and writing from I/O ports}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\begin{intest}
INP, INPW, INPD \index{inp()} \index{inpw()} \index{inpd()}
\end{intest}

\begin{description}
\item [\texttt{unsigned char inp(unsigned short \_port);}]
\item [\texttt{unsigned short inpw (unsigned short \_port);}]
\item [\texttt{unsigned long inpd(unsigned short \_port);}]
\item [Description:]They return the data read on port \texttt{\_port}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
OUTP, OUTPW, OUTPD \index{outp()} \index{outpw()} \index{outpd()}
\end{intest}

\begin{description}
\item [\texttt{void outp(unsigned short \_port, unsigned char \_data);}]
\item [\texttt{void outpw(unsigned short \_port, unsigned short \_data);}]
\item [\texttt{void outpd(unsigned short \_port, unsigned long \_data)}]
\item [Description:]It writes the data \texttt{\_data} into the port \texttt{\_port}.
\end{description
}

%----------------------------------------------------------------------------
\section{Disabling/Enabling interrupts}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\begin{intest}
KERN\_CLI\index{kern\_cli()}
\end{intest}

\begin{description}
\item [\texttt{void kern\_cli(void);}]
\item [Description:]It disables interrupts (as the x86 \texttt{cli} instruction).
\end{description
}
%----------------------------------------------------------------------------

\begin{intest}
KERN\_STI\index{kern\_sti()}
\end{intest}

\begin{description}
\item [\texttt{void kern\_sti(void);}]
\item [Description:]It enables interrupts (as the x86 \texttt{sti} instruction).
\end{description
}

%----------------------------------------------------------------------------
\section{Saving/Restoring interrupts}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\begin{intest}
KERN\_FSAVE\index{kern\_fsave()}\index{SYS\_FLAGS}
\end{intest}

\begin{description}
\item [\texttt{SYS\_FLAGS kern\_fsave(void);}]
\item [Description:]It disables interrupts (as the x86 \texttt{cli} instruction).
The CPU flags are returned by the function; in that way they can be
restored using kern\_frestore
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
KERN\_FRESTORE\index{kern\_frestore()}
\end{intest}

\begin{description}
\item [\texttt{void kern\_frestore(SYS\_FLAGS f);}]
\item [Description:]It restores the interrupt state as it was when the
correspondent \texttt{kern\_fsave} was called.
\end{description
}

%----------------------------------------------------------------------------
\section{Masking/Unmasking PIC interrupts}
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
\begin{intest}
IRQ\_MASK\index{irq\_mask()}
\end{intest}

\begin{description}
\item [\texttt{void irq\_mask(WORD irqno);}]
\item [Description:]It mask the interrupt number \texttt{irqno} on the
PC PIC. \texttt{irqno} must be in the interval {[}1..15{]}.
\end{description
}

%----------------------------------------------------------------------------
\begin{intest}
IRQ\_UNMASK\index{irq\_unmask()}
\end{intest}

\begin{description}
\item [\texttt{void irq\_unmask(WORD irqno);}]
\item [Description:]It unmask the interrupt number \texttt{irqno} on the
PC PIC. \texttt{irqno} must be in the interval {[}1..15{]}.
\end{description
}

%----------------------------------------------------------------------------
\chapter{Memory Management Functions}
%----------------------------------------------------------------------------

The S.Ha.R.K. Kernel provides the standard set of memory allocations
functions provided by the Standard C libraries. In particular, the
functions listed in figure \ref{fig:malloc} can be used.

\begin{figure}
\begin{center} \fbox{\tt{ \begin{minipage}{6cm} \begin{tabbing}
123\=123\=123\=\kill
\#include <stdlib.h>\\
void *calloc(size\_t nmemb, size\_t size);\\
void *malloc(size\_t size);\\
void free(void *ptr);\\
void *realloc(void *ptr, size\_t size);\\
\end{tabbing} \end{minipage} }} \end{center}
\caption{\label{fig:malloc}Memory allocation functions.}
\end{figure}
In particular \footnote{These descriptions came directly from the Linux man pages...}:

\begin{itemize}
\item calloc() allocates memory for an array of nmemb elements of size bytes
each and returns a pointer to the allocated memory. The memory is
set to zero. The value returned is a pointer to the allocated memory,
which is suitably aligned for any kind of variable, or NULL if the
request fails.
\item malloc() allocates size bytes and returns a pointer to the allocated
memory. The memory is not cleared. The value returned is a pointer
to the allocated memory, which is suitably aligned for any kind of
variable, or NULL if the request fails.
\item free() frees the memory space pointed to by ptr, which must have been
returned by a previous call to malloc(), calloc() or realloc(). Otherwise,
or if free(ptr) has already been called before, undefined behaviour
occurs. If ptr is NULL, no operation is performed.
\item realloc() changes the size of the memory block pointed to by ptr to
size bytes. The contents will be unchanged to the minimum of the old
and new sizes; newly allocated memory will be uninitialized. If ptr
is NULL, the call is equivalent to malloc(size); if size is equal
to zero, the call is equivalent to free(ptr). Unless ptr is NULL,
it must have been returned by an earlier call to malloc(), calloc()
or realloc(). It returns a pointer to the newly allocated memory,
which is suitably aligned for any kind of variable and may be different
from ptr, or NULL if the request fails or if size was equal to 0.
If realloc() fails the original block is left untouched - it is not
freed or moved.
\end{itemize
}

The S.Ha.R.K. Kernel also provides a set of low-level memory management
functions that can be used to allocate memory with particular requirements
(for example, they are useful for getting memory blocks aligned to
a page (4 Kb) boundary or with addresses under 1/16 Mb). Description
of these functions is given in Chapter 3 of the S.Ha.R.K. Architecture
Manual.