699 lines
22 KiB
Plaintext
699 lines
22 KiB
Plaintext
RTLinux HOWTO
|
||
Dinil Divakaran <mailto:dinildivakaran@rediffmail.com>
|
||
1.1, 2002-08-29
|
||
|
||
RTLinux Installation and writing realtime programs in Linux
|
||
______________________________________________________________________
|
||
|
||
Table of Contents
|
||
|
||
|
||
1. Introduction
|
||
|
||
1.1 Purpose
|
||
1.2 Who should read this HOWTO
|
||
1.3 Acknowledgement
|
||
1.4 Feedback
|
||
1.5 Distribution Policy
|
||
|
||
2. Installing RTLINUX
|
||
|
||
3. Why RTLinux
|
||
|
||
4. Writing RTLinux Programs
|
||
|
||
4.1 Introduction to writing modules
|
||
4.2 Creating RTLinux Threads
|
||
4.3 An example program
|
||
|
||
5. Compiling and Executing
|
||
|
||
6. Inter-Process Communication
|
||
|
||
6.1 Realtime FIFO
|
||
6.2 Application Using FIFO
|
||
|
||
7. What next
|
||
|
||
|
||
|
||
______________________________________________________________________
|
||
|
||
1. Introduction
|
||
|
||
1.1. Purpose
|
||
|
||
This document aims at getting the novice user up and running with
|
||
RTLinux in as painless a manner as possible.
|
||
|
||
|
||
1.2. Who should read this HOWTO
|
||
|
||
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.
|
||
|
||
|
||
1.3. Acknowledgement
|
||
|
||
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".
|
||
|
||
|
||
1.4. Feedback
|
||
|
||
Any doubt or comment about this document, is always welcome. Please
|
||
feel free to email me <mailto:dinildivakaran@rediffmail.com>. If there
|
||
is any mistake in this document, please let me know so that I can
|
||
correct it in the next revision. Thanks.
|
||
|
||
1.5. Distribution Policy
|
||
|
||
Copyright (C)2002 Dinil Divakaran.
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
|
||
2. Installing RTLINUX
|
||
|
||
The first step in the compilation of RTLinux kernel, is to download a
|
||
pre-patched kernel 2.2.18 (x86 only) 2.2.18
|
||
<http://ftp.kernel.org/pub/linux/kernel/v2.2/linux-2.2.18.tar.gz> (x86
|
||
only) or 2.4.0-test1
|
||
<http://ftp.kernel.org/pub/linux/kernel/v2.4/linux-2.4.0-test1.tar.gz>
|
||
(x86, PowerPC, Alpha) into /usr/src/ and untar it. Also put a fresh
|
||
copy of RTLinux kernel (version 3.0) from www.rtlinux.org
|
||
<http://www.rtlinux.org> in /usr/src/rtlinux/. (We will use $ to
|
||
represent the prompt).
|
||
|
||
|
||
1. Now, configure the Linux kernel :
|
||
|
||
|
||
$ cd /usr/src/linux
|
||
$ make config
|
||
or
|
||
$ make menuconfig
|
||
or
|
||
$ make xconfig
|
||
|
||
|
||
|
||
2. For building the kernel image, type :
|
||
|
||
|
||
$ make dep
|
||
$ make bzImage
|
||
$ make modules
|
||
$ make modules_install
|
||
$ cp arch/i386/boot/bzImage /boot/rtzImage
|
||
|
||
|
||
|
||
3. Next step is to configure LILO. Type in the following lines in the
|
||
file /etc/lilo.conf
|
||
|
||
|
||
|
||
image=/boot/rtzImage
|
||
label=rtl
|
||
read-only
|
||
root=/dev/hda1
|
||
|
||
|
||
|
||
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=".
|
||
|
||
4. 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.
|
||
|
||
|
||
$ make config
|
||
or
|
||
$ make menuconfig
|
||
or
|
||
$ make xconfig
|
||
|
||
|
||
|
||
5. Finally, for compiling RTLinux
|
||
|
||
|
||
$ make
|
||
$ make devices
|
||
$ make install
|
||
|
||
|
||
|
||
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:
|
||
|
||
|
||
$ cd /usr/src/rtlinux
|
||
$ make clean
|
||
$ make
|
||
$ make install
|
||
|
||
|
||
|
||
3. Why RTLinux
|
||
|
||
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.
|
||
|
||
|
||
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.
|
||
|
||
|
||
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.
|
||
|
||
|
||
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.
|
||
|
||
|
||
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.
|
||
|
||
|
||
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.
|
||
|
||
|
||
4. Writing RTLinux Programs
|
||
|
||
4.1. Introduction to writing modules
|
||
|
||
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:
|
||
|
||
|
||
|
||
<20> 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.
|
||
|
||
<20> The cleanup_module() which is called just before the module is
|
||
removed.
|
||
|
||
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 :
|
||
|
||
|
||
$ gcc -c {SOME-FLAGS} my_module.c
|
||
|
||
|
||
|
||
This command creates a module file named module.o, which can now be
|
||
inserted into the kernel by using the 'insmod' command :
|
||
|
||
|
||
$ insmod module.o
|
||
|
||
|
||
|
||
Similarly, for removing the module, you can use the 'rmmod' command :
|
||
|
||
|
||
$ rmmod module
|
||
|
||
|
||
|
||
4.2. Creating RTLinux Threads
|
||
|
||
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.
|
||
|
||
|
||
4.3. An example program
|
||
|
||
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) :
|
||
|
||
|
||
|
||
#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);
|
||
}
|
||
|
||
|
||
|
||
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. This function must only be
|
||
called from the Linux kernel thread (i.e., using init_module()).
|
||
|
||
|
||
int pthread_create(pthread_t * thread,
|
||
pthread_attr_t * attr,
|
||
void * (*thread_code)(void *),
|
||
void * arg);
|
||
|
||
|
||
|
||
The new thread created is of type pthread_t, defined in the header
|
||
pthread.h. This thread executes the function thread_code(), passing it
|
||
arg as its argument. The attr argument specifies thread attributes to
|
||
be applied to the new thread. If attr 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().
|
||
|
||
|
||
int pthread_make_periodic_np(pthread_t thread,
|
||
hrtime_t start_time,
|
||
hrtime_t period);
|
||
|
||
|
||
|
||
pthread_make_periodic_np marks the thread as ready for execution. The
|
||
thread will start its execution at start_time and will run at
|
||
intervals specified by period given in nanoseconds.
|
||
gethrtime returns the time in nanoseconds since the system bootup.
|
||
|
||
|
||
hrtime_t gethrtime(void);
|
||
|
||
|
||
|
||
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.
|
||
|
||
|
||
5. Compiling and Executing
|
||
|
||
In order to execute the program, hello.c, (after booting rtlinux,
|
||
ofcourse) you must do the following :
|
||
|
||
|
||
1. 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'.
|
||
|
||
|
||
include rtl.mk
|
||
all: hello.o
|
||
clean:
|
||
rm -f *.o
|
||
hello.o: hello.c
|
||
$(CC) ${INCLUDE} ${CFLAGS} -c hello.c
|
||
|
||
|
||
|
||
2. 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.
|
||
|
||
3. For compiling the code, use the command 'make'.
|
||
|
||
|
||
$ make
|
||
|
||
|
||
|
||
4. 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).
|
||
|
||
|
||
$ rtlinux start hello
|
||
|
||
|
||
|
||
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:
|
||
|
||
|
||
$ dmesg
|
||
|
||
|
||
|
||
To stop the program, you need to remove it from the kernel. To do so,
|
||
type:
|
||
|
||
|
||
$ rtlinux stop hello
|
||
|
||
|
||
|
||
Alternate ways for inserting and removing the module is to use insmod
|
||
and rmmod respectively.
|
||
|
||
|
||
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!
|
||
|
||
|
||
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() :
|
||
|
||
|
||
struct sched_param p;
|
||
p . sched_priority = 1;
|
||
pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);
|
||
|
||
|
||
|
||
6. Inter-Process Communication
|
||
|
||
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.
|
||
|
||
|
||
There are several ways for inter-process communication. We will
|
||
discuss the most important and common way of communication - the
|
||
realtime FIFO.
|
||
|
||
6.1. Realtime FIFO
|
||
|
||
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.
|
||
|
||
|
||
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.
|
||
|
||
|
||
6.2. Application Using FIFO
|
||
|
||
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)
|
||
|
||
|
||
|
||
#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);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
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.
|
||
|
||
|
||
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)
|
||
|
||
#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);
|
||
}
|
||
|
||
|
||
|
||
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.
|
||
|
||
|
||
|
||
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.
|
||
|
||
|
||
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;
|
||
|
||
}
|
||
|
||
|
||
|
||
This time we can stop the process by just removing the module by using
|
||
the 'rmmod' command.
|
||
|
||
|
||
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.
|
||
|
||
|
||
7. What next
|
||
|
||
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 Ori Pomerantz.
|
||
|
||
|
||
|