mirror of https://github.com/tLDP/LDP
646 lines
21 KiB
Plaintext
646 lines
21 KiB
Plaintext
<!doctype linuxdoc system>
|
|
<article>
|
|
<title>RTLinux HOWTO</title>
|
|
<author>
|
|
<url url="mailto:dinildivakaran@rediffmail.com" name="Dinil Divakaran">
|
|
</author>
|
|
<!-- always have a version number and a date (YYYY-MM-DD format) -->
|
|
<date>1.1, 2002-08-29</date>
|
|
<abstract>
|
|
RTLinux Installation and writing realtime programs in Linux
|
|
</abstract>
|
|
<toc>
|
|
<sect>
|
|
Introduction
|
|
<sect1>
|
|
Purpose
|
|
<p>
|
|
This document aims at getting the novice user up and running with RTLinux in as painless a
|
|
manner as possible.
|
|
|
|
<sect1>
|
|
Who should read this HOWTO
|
|
<p>
|
|
This document is meant for all those who wish to know the working of a
|
|
realtime kernel. For those of you already familiar with module programming,
|
|
the document wouldn't appear as a difficult one. And for others, you needn't
|
|
worry; since only the basic concepts of module programming are required,
|
|
which we would indeed discuss, as and when required.
|
|
|
|
<sect1>
|
|
Acknowledgement
|
|
<p>
|
|
First of all I would like to thank my advisor, Pramode C. E, for his
|
|
encouragement and help. Also I express my sincere appreciation to Victor Yodaiken.
|
|
This document would not have been possible without all the information
|
|
gathered from different papers contributed by Victor Yodaiken. I am
|
|
also grateful to Michael Barabanov for his thesis on "A Linux-base
|
|
Real-Time Operating System".
|
|
|
|
<sect1>
|
|
Feedback
|
|
<p>
|
|
Any doubt or comment about this document, is always welcome. Please
|
|
feel free to email <url url="mailto:dinildivakaran@rediffmail.com" name="me">. If
|
|
there is any mistake in this document, please let me know so that I can correct it in the
|
|
next revision. Thanks.
|
|
<sect1>
|
|
Distribution Policy
|
|
<p>
|
|
Copyright (C)2002 Dinil Divakaran.
|
|
<p>
|
|
This document is free; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version.
|
|
<p>
|
|
This document is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
<sect>
|
|
Installing RTLINUX
|
|
<p>
|
|
The first step in the compilation of RTLinux kernel, is to download a
|
|
pre-patched kernel 2.2.18 (x86 only)
|
|
<url url="http://ftp.kernel.org/pub/linux/kernel/v2.2/linux-2.2.18.tar.gz" name="2.2.18"> (x86 only)
|
|
or <url url="http://ftp.kernel.org/pub/linux/kernel/v2.4/linux-2.4.0-test1.tar.gz" name="2.4.0-test1"> (x86, PowerPC, Alpha) into /usr/src/ and untar it. Also put a fresh copy of
|
|
RTLinux kernel (version 3.0) from
|
|
<url url="http://www.rtlinux.org" name="www.rtlinux.org"> in /usr/src/rtlinux/.
|
|
(We will use $ to represent the prompt).
|
|
|
|
<enum>
|
|
<item>
|
|
Now, configure the Linux kernel :
|
|
|
|
<verb>
|
|
$ cd /usr/src/linux
|
|
$ make config
|
|
or
|
|
$ make menuconfig
|
|
or
|
|
$ make xconfig
|
|
</verb>
|
|
|
|
<item>
|
|
For building the kernel image, type :
|
|
|
|
<verb>
|
|
$ make dep
|
|
$ make bzImage
|
|
$ make modules
|
|
$ make modules_install
|
|
$ cp arch/i386/boot/bzImage /boot/rtzImage
|
|
</verb>
|
|
|
|
<item>
|
|
Next step is to configure LILO. Type in the following lines in the file
|
|
/etc/lilo.conf
|
|
|
|
<verb>
|
|
image=/boot/rtzImage
|
|
label=rtl
|
|
read-only
|
|
root=/dev/hda1
|
|
</verb>
|
|
|
|
WARNING: replace /dev/hda1 in the above with your root filesystem. The
|
|
easiest way to find out which filesystem it should be, take a look
|
|
at the existing entry in your /etc/lilo.conf for "root=".
|
|
|
|
<item>
|
|
Now, restart your computer and load the RTLinux kernel by typing 'rtl'
|
|
at the LILO prompt. Then 'cd' to /usr/src/rtlinux/ and configure RTLinux.
|
|
|
|
<verb>
|
|
$ make config
|
|
or
|
|
$ make menuconfig
|
|
or
|
|
$ make xconfig
|
|
</verb>
|
|
|
|
<item>Finally, for compiling RTLinux
|
|
|
|
<verb>
|
|
$ make
|
|
$ make devices
|
|
$ make install
|
|
</verb>
|
|
|
|
</enum>
|
|
|
|
The last step will create the directory:
|
|
|
|
/usr/rtlinux-xx (xx denotes the version)
|
|
|
|
which contains the default installation directory for RTLinux which is
|
|
needed to create and compile user programs (that is, it contains the
|
|
include files, utilities, and documentation). It will also create a
|
|
symbolic link:
|
|
|
|
/usr/rtlinux
|
|
|
|
which points to /usr/rtlinux-xx. In order to maintain future
|
|
compatibility, please make sure that all of your own RTLinux programs
|
|
use /usr/rtlinux as its default path.
|
|
|
|
NOTE : If you change any Linux kernel options, please don't forget to do:
|
|
|
|
<verb>
|
|
$ cd /usr/src/rtlinux
|
|
$ make clean
|
|
$ make
|
|
$ make install
|
|
</verb>
|
|
|
|
<sect>
|
|
Why RTLinux
|
|
<p>
|
|
The reasons for the design of RTLinux can be understood by examining the
|
|
working of the standard Linux kernel. The Linux kernel separates the hardware
|
|
from the user-level tasks. The kernel uses scheduling algorithms and assigns
|
|
priority to each task for providing good average performances or throughput.
|
|
Thus the kernel has the ability to suspend any
|
|
user-level task, once that task has outrun the time-slice allotted to it
|
|
by the CPU. This scheduling algorithms along with device drivers,
|
|
uninterruptible system calls, the use of interrupt disabling and virtual
|
|
memory operations are sources of unpredictability. That is to say,
|
|
these sources cause hindrance to the realtime performance of a task.
|
|
|
|
<p>
|
|
You might already be familiar with the non-realtime performance, say,
|
|
when you are listening to the music played using 'mpg123' or any other player.
|
|
After executing this process for
|
|
a pre-determined time-slice, the standard Linux kernel could preempt the
|
|
task and give the CPU to another one
|
|
(e.g. one that boots up the X server or Netscape). Consequently,
|
|
the continuity of the music is lost. Thus, in trying to ensure fair distribution of
|
|
CPU time among all processes, the kernel can prevent other events from
|
|
occurring.
|
|
|
|
<p>
|
|
A realtime kernel should be able to guarantee the timing requirements of
|
|
the processes under it.
|
|
The RTLinux kernel accomplishes realtime performances by removing such
|
|
sources of unpredictability as discussed above. We can consider the RTLinux
|
|
kernel as sitting between the standard Linux kernel and the hardware. The
|
|
Linux kernel sees the realtime layer as the actual hardware. Now, the
|
|
user can both introduce and set priorities to each and every task. The user
|
|
can achieve correct timing for the processes by deciding on the
|
|
scheduling algorithms, priorities, frequency of execution etc. The
|
|
RTLinux kernel assigns lowest priority to the standard Linux kernel. Thus
|
|
the user-task will be executed in realtime.
|
|
|
|
<p>
|
|
The actual realtime performance is obtained by intercepting all
|
|
hardware interrupts. Only for those interrupts that are related to
|
|
the RTLinux, the appropriate interrupt service routine is run. All other
|
|
interrupts are held and passed to the Linux kernel as software interrupts when
|
|
the RTLinux kernel is idle and then the standard Linux kernel runs. The RTLinux
|
|
executive is itself nonpreemptible.
|
|
|
|
<p>
|
|
Realtime tasks are privileged (that is, they have direct access to hardware), and they
|
|
do not use virtual memory. Realtime tasks are written as special Linux
|
|
modules that can be dynamically loaded into memory. The
|
|
initialization code for a realtime tasks initializes the realtime task
|
|
structure and informs RTLinux kernel of its deadline,
|
|
period, and release-time constraints.
|
|
|
|
<p>
|
|
RTLinux co-exists along with the Linux kernel since it leaves the Linux
|
|
kernel untouched. Via a set of relatively simple modifications,
|
|
it manages to convert the existing Linux kernel into a hard realtime environment
|
|
without hindering future Linux development.
|
|
|
|
<sect>
|
|
Writing RTLinux Programs
|
|
<sect1>
|
|
Introduction to writing modules
|
|
<p>
|
|
So what are modules? A Linux module is nothing but an object file, usually
|
|
created with the -c flag argument to gcc. The module itself is created by
|
|
compiling an ordinary C language file without the main() function. Instead
|
|
there will be a pair of init_module/cleanup_module functions:
|
|
|
|
<itemize>
|
|
<item>
|
|
The init_module() which is called when the module is inserted into the kernel.
|
|
It should return 0 on success and a negative value on failure.
|
|
<item>
|
|
The cleanup_module() which is called just before the module is removed.
|
|
</itemize>
|
|
|
|
Typically, init_module() either registers a handler for something with the
|
|
kernel, or it replaces one of the kernel function with its own code
|
|
(usually code to do something and then call the original function).
|
|
The cleanup_module() function is supposed to undo whatever init_module() did,
|
|
so the module can be unloaded safely.
|
|
|
|
For example, if you have written a C file called module.c
|
|
(with init_module() and cleanup_module() replacing the main() function),
|
|
the code can be converted into a module by typing :
|
|
|
|
<verb>
|
|
$ gcc -c {SOME-FLAGS} my_module.c
|
|
</verb>
|
|
|
|
This command creates a module file named module.o, which can now be inserted
|
|
into the kernel by using the 'insmod' command :
|
|
|
|
<verb>
|
|
$ insmod module.o
|
|
</verb>
|
|
|
|
Similarly, for removing the module, you can use the 'rmmod' command :
|
|
|
|
<verb>
|
|
$ rmmod module
|
|
</verb>
|
|
|
|
<sect1>
|
|
Creating RTLinux Threads
|
|
<p>
|
|
A realtime application is usually composed of several ``threads'' of execution.
|
|
Threads are light-weight processes which share a common address space.
|
|
In RTLinux, all threads share the Linux kernel address space.
|
|
The advantage of using threads is that switching between threads
|
|
is quite inexpensive when compared with context switch. We can have
|
|
complete control over the execution of a thread by using different functions
|
|
as will be shown in the examples following.
|
|
|
|
<sect1>
|
|
An example program
|
|
<p>
|
|
The best way to understand the working of a thread is to trace a realtime
|
|
program. For example, the program shown below will execute once every second,
|
|
and during each iteration it will print 'Hello World'.
|
|
|
|
|
|
The Program code (file - hello.c) :
|
|
|
|
<verb>
|
|
#include <rtl.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
|
|
pthread_t thread;
|
|
|
|
void * thread_code(void)
|
|
{
|
|
pthread_make_periodic_np(pthread_self(), gethrtime(), 1000000000);
|
|
|
|
while (1)
|
|
{
|
|
pthread_wait_np ();
|
|
rtl_printf("Hello World\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int init_module(void)
|
|
{
|
|
return pthread_create(&thread, NULL, thread_code, NULL);
|
|
}
|
|
|
|
void cleanup_module(void)
|
|
{
|
|
pthread_delete_np(thread);
|
|
}
|
|
</verb>
|
|
|
|
So, let us start with the init_module(). The init_module() invokes
|
|
pthread_create(). This is for creating a new thread that executes
|
|
concurrently with the calling thread. <em>This function must only be
|
|
called from the Linux kernel thread (i.e., using init_module()).</em>
|
|
|
|
<verb>
|
|
int pthread_create(pthread_t * thread,
|
|
pthread_attr_t * attr,
|
|
void * (*thread_code)(void *),
|
|
void * arg);
|
|
</verb>
|
|
|
|
The new thread created is of type pthread_t, defined in
|
|
the header pthread.h. This thread executes the function thread_code(),
|
|
passing it <em>arg</em> as its argument. The <em>attr</em>
|
|
argument specifies thread attributes to be applied to the new thread.
|
|
If <em>attr</em> is NULL, default attributes are used.
|
|
|
|
So here, thread_code() is invoked with no argument. thread_code
|
|
has three components - initialization, run-time and termination.
|
|
|
|
In the initialization phase, is the call to pthread_make_periodic_np().
|
|
|
|
<verb>
|
|
int pthread_make_periodic_np(pthread_t thread,
|
|
hrtime_t start_time,
|
|
hrtime_t period);
|
|
</verb>
|
|
|
|
pthread_make_periodic_np marks the <em>thread</em> as ready for execution.
|
|
The thread will start its execution at <em>start_time</em> and will run at
|
|
intervals specified by <em>period</em> given in nanoseconds.
|
|
|
|
gethrtime returns the time in nanoseconds since the system bootup.
|
|
|
|
<verb>
|
|
hrtime_t gethrtime(void);
|
|
</verb>
|
|
|
|
This time is never reset or adjusted. gethrtime always gives monotonically
|
|
increasing values. hrtime_t is a 64-bit signed integer.
|
|
|
|
By calling the function pthread_make_periodic_np(), the thread tells the
|
|
scheduler to periodically execute this thread at a frequency of 1 Hz. This marks
|
|
the end of the initialization section for the thread.
|
|
|
|
The while() loop begins with a call to the function pthread_wait_np(), which
|
|
suspends execution of the currently running realtime thread until the start of
|
|
the next period. The thread was previously marked for execution using
|
|
pthread_make_periodic_np. Once the thread is called again, it executes the rest
|
|
of the contents inside the while loop, until it encounters another call to
|
|
pthread_wait_np().
|
|
|
|
Because we haven't included any way to exit the loop, this thread will continue
|
|
to execute forever at a rate of 1Hz. The only way to stop the program is by removing
|
|
it from the kernel with the rmmod command. This invokes the cleanup_module(),
|
|
which calls pthread_delete_np() to cancel the thread and deallocate its resources.
|
|
|
|
<sect>
|
|
Compiling and Executing
|
|
<p>
|
|
In order to execute the program, hello.c, (after booting rtlinux, ofcourse)
|
|
you must do the following :
|
|
|
|
<enum>
|
|
<item>
|
|
Compile the source code and create a module using the GCC compiler.
|
|
To simplify things, however, it is better to create a Makefile. Then you only need
|
|
to type 'make' to compile the code.
|
|
|
|
Makefile can be created by typing in the following lines in a file named 'Makefile'.
|
|
|
|
<verb>
|
|
include rtl.mk
|
|
all: hello.o
|
|
clean:
|
|
rm -f *.o
|
|
hello.o: hello.c
|
|
$(CC) ${INCLUDE} ${CFLAGS} -c hello.c
|
|
</verb>
|
|
|
|
<item>
|
|
Locate and copy the rtl.mk file into the same directory as your hello.c and Makefile.
|
|
The rtl.mk file is an include file which contains
|
|
all the flags needed to compile the code. You can copy it from the RTLinux source
|
|
tree and place it alongside of the hello.c file.
|
|
<item>
|
|
For compiling the code, use the command 'make'.
|
|
|
|
<verb>
|
|
$ make
|
|
</verb>
|
|
|
|
<item>
|
|
The resulting object binary must be inserted into the kernel, where it will be executed by
|
|
RTLinux. Use the command 'rtlinux' (you need to be the 'root' to do so).
|
|
|
|
<verb>
|
|
$ rtlinux start hello
|
|
</verb>
|
|
|
|
</enum>
|
|
|
|
|
|
|
|
|
|
You should now be able to see your hello.o program printing its message
|
|
every second. Depending on the configuration of your machine, you should
|
|
either be able to see it directly in your console, or by typing:
|
|
|
|
<verb>
|
|
$ dmesg
|
|
</verb>
|
|
|
|
To stop the program, you need to remove it from the kernel. To do so, type:
|
|
|
|
<verb>
|
|
$ rtlinux stop hello
|
|
</verb>
|
|
|
|
Alternate ways for inserting and removing the module is to use <em>insmod</em>
|
|
and <em>rmmod</em> respectively.
|
|
|
|
<p>
|
|
Here, we have made our example program too simple. Contrary to what we have
|
|
seen, there may be multiple threads in a program. Priority can be set at thread
|
|
creation or modified later. Also, we can select the scheduling algorithm that
|
|
should be used. In fact, we can write our own scheduling algorithms!
|
|
|
|
<p>
|
|
In our example, we can set priority of the thread as 1, and select FIFO scheduling
|
|
by inserting the following lines in the beginning of the function, thread_code() :
|
|
|
|
<verb>
|
|
struct sched_param p;
|
|
p . sched_priority = 1;
|
|
pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);
|
|
|
|
</verb>
|
|
<sect>
|
|
Inter-Process Communication
|
|
<p>
|
|
The example program that we have seen above is what is known as a realtime process.
|
|
Not every part of a application program need be written in realtime. It is found
|
|
that only that part of a program which requires precise time restrictions should
|
|
be written as a realtime process. Others can be written and executed in user space.
|
|
User spaces processes are often easier to write, execute and debug than realtime
|
|
threads. But then, there should be a way to communicate between user space Linux
|
|
processes and realtime thread.
|
|
|
|
<p>
|
|
There are several ways for inter-process communication. We will discuss the
|
|
most important and common way of communication - the realtime FIFO.
|
|
|
|
<sect1>
|
|
Realtime FIFO
|
|
|
|
<p>
|
|
Realtime FIFOs are unidirectional queues (First In First Out). So at one end a
|
|
process writes data into the FIFO, and from the other end of the FIFO,
|
|
information is read by another process. Usually, one of these processes is
|
|
the realtime thread and the other is a user space process.
|
|
|
|
<p>
|
|
The Realtime FIFOs are actually character devices (/dev/rtf*) with a major
|
|
number of 150. Realtime threads uses integers to refer to each FIFO (for
|
|
example - 2 for /dev/rtf2). There is a limit to the number of FIFOs. There
|
|
are functions such as rtf_create(), rtf_destroy(), rtf_get(), rtf_put()
|
|
etc for handling the FIFOs.
|
|
|
|
On the other hand, the Linux user process sees the realtime FIFOs as normal
|
|
character devices. Therefore the functions such as open(), close(), read() and
|
|
write() can be used on these devices.
|
|
|
|
<sect1>
|
|
Application Using FIFO
|
|
|
|
<p>
|
|
First, let us consider a simple C program (filename pcaudio.c) to play music
|
|
(of just two tones) through the PC speaker. For the time being, let us assume
|
|
that for playing the note, we need only write to the character device /dev/rtf3.
|
|
(Later, we will see a realtime time process that reads from this FIFO (/dev/rtf3)
|
|
and sends to the PC speaker)
|
|
|
|
|
|
<verb>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#define DELAY 30000
|
|
|
|
void make_tone1(int fd)
|
|
{
|
|
static char buf = 0;
|
|
write (fd, &buf, 1);
|
|
}
|
|
|
|
void make_tone2(int fd)
|
|
{
|
|
static char buf = 0xff;
|
|
write (fd, &buf, 1);
|
|
}
|
|
|
|
main()
|
|
{
|
|
int i, fd = open ("/dev/rtf3", O_WRONLY);
|
|
while (1)
|
|
{
|
|
for (i=0;i<DELAY;i++);
|
|
make_tone1(fd);
|
|
for (i=0;i<DELAY;i++);
|
|
make_tone2(fd);
|
|
}
|
|
}
|
|
|
|
</verb>
|
|
|
|
Now, if the above shown program (pcaudio.c) is compiled and run, it should create
|
|
regular sound patters corresponding to a square wave.
|
|
But before that we need a module for reading from '/dev/rtf3' and sending the
|
|
corresponding data to the PC speaker. This realtime program can be found
|
|
at the rtlinux source tree (/usr/src/rtlinux/examples/sound/) . Insert the module
|
|
sound.o using the command 'insmod'.
|
|
|
|
Since we have inserted a module for reading from the device, we can now execute
|
|
our program (compile using 'gcc' and then execute the corresponding 'a.out'.
|
|
So the process produces somewhat regular
|
|
tones, when there is no other (time consuming) process in the system. But,
|
|
when the X server is started in another console, there comes more prolonged
|
|
silence in the tone. Finally, when a 'find' command (for a file in /usr
|
|
directory) is executed, the sound pattern is completely distorted. The
|
|
reason behind this is that, we are writing the data onto the FIFO in
|
|
non-realtime.
|
|
|
|
<p>
|
|
We will, now, see how to run this process in realtime, so that the sound
|
|
is produced without any kind of
|
|
disturbance. First, we will convert the above program into a realtime program.
|
|
(Filename rtaudio.c)
|
|
<verb>
|
|
#include <rtl.h>
|
|
#include <pthread.h>
|
|
#include <rtl_fifo.h>
|
|
#include <time.h>
|
|
|
|
#define FIFO_NO 3
|
|
#define DELAY 30000
|
|
pthread_t thread;
|
|
|
|
void * sound_thread(int fd)
|
|
{
|
|
int i;
|
|
static char buf = 0;
|
|
while (1)
|
|
{
|
|
for(i=0; i<DELAY; i++);
|
|
buf = 0xff;
|
|
rtf_put(FIFO_NO, &buf, 1);
|
|
|
|
for(i=0;i<DELAY;i++);
|
|
buf = 0x0;
|
|
rtf_put(FIFO_NO, &buf, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int init_module(void)
|
|
{
|
|
return pthread_create(&thread, NULL, sound_thread, NULL);
|
|
}
|
|
|
|
void cleanup_module(void)
|
|
{
|
|
pthread_delete_np(thread);
|
|
}
|
|
|
|
</verb>
|
|
|
|
|
|
<p>
|
|
If not already done, 'plug in' the module sound.o into the kernel. Compile
|
|
the above program by writing a Makefile for it (as said earlier),
|
|
thus producing the module 'rtaudio.o'.
|
|
Before inserting this module, one more thing. Note that the
|
|
above program runs into infinite loop. Since, we have not included code
|
|
for the thread to sleep or stop, the thread will not cease its execution. In short,
|
|
your PC speaker will go on producing the tone, and you will have to restart
|
|
your computer for doing anything else.
|
|
|
|
<p>
|
|
So, let us change the code a little bit (only in the function sound_thread())
|
|
by asking the thread itself to make the delay between tones.
|
|
|
|
<verb>
|
|
void * sound_thread(int fd)
|
|
{
|
|
static char buf = 0;
|
|
pthread_make_periodic_np (pthread_self(), gethrtime(), 500000000);
|
|
|
|
while (1)
|
|
{
|
|
pthread_wait_np();
|
|
buf = (int)buf^0xff;
|
|
rtf_put(FIFO_NO, &buf, 1);
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
</verb>
|
|
|
|
This time we can stop the process by just removing the module by using
|
|
the 'rmmod' command.
|
|
|
|
<p>
|
|
Here, we have seen how realtime FIFOs can be used for inter-process
|
|
communication. Also the real need for RTLinux can be understood from the
|
|
above example.
|
|
|
|
<sect>
|
|
What next
|
|
<p>
|
|
This document has gone through the basics of programming in RTLinux. Once you have
|
|
understood the basic concept it is not difficult to make steps by your
|
|
own. So you can go through all other examples available along with the
|
|
rtlinux source. Then you should be
|
|
able to write modules and test them. For more information regarding
|
|
module programming, you can refer to 'Linux Kernel Module Programming Guide'
|
|
by <em>Ori Pomerantz</em>.
|
|
</article>
|