Ran these commands:
cvs -d:pserver:anonymous@cvs.gna.org:/cvs/rtai co vulcano
cd vulcano
git init
git add .
git commit
186 lines
10 KiB
Text
186 lines
10 KiB
Text
*** SERVING LINUX SYSCALLS IN RTAI HARD REAL TIME MODE ***
|
|
|
|
The default RTAI way of dealing with Linux syscalls done while in hard real
|
|
time consists in moving a task wanting to use Linux back to soft mode,
|
|
recovering it to hard real time again, as soon as possible, afterward. See the
|
|
related RTAI configuration helper for an explanation of the different available
|
|
modes to implement the "as soon as possible" above on various architectures.
|
|
|
|
This README documents possible alternative ways to access Linux services from
|
|
hard real time RTAI applications in user space. Such a support is based on the
|
|
creation of a general purpose server thread that takes over Linux requests.
|
|
|
|
It must be noticed that if Linux is to be used synchronously at the very moment
|
|
Linux is called the task becomes timed by Linux, wherever a Linux server is
|
|
used or not. So hard real time constraints cannot be satisfied anyhow. The use
|
|
of a Linux server has an advantage though. In fact it takes just two context
|
|
switches, in place of the four used by hard-soft-hard transitions. Moreover it
|
|
ensures that RTAI returns to hard real time immediately after the Linux syscall
|
|
has finished while, if the architecture does not support the immediate Linux
|
|
syscall mode reliably for all Linux syscalls, there will be the need to wait
|
|
for a call to an RTAI function to go back in RTAI hard real time fully. That
|
|
will add even far more latencies when a hard RTAI task does some processing
|
|
for a significant amount of time after the Linux syscall, without using any
|
|
RTAI service. So, provided it is allowed loosing real time, installing a
|
|
synchronous server might be the best way to interact with Linux anyhow.
|
|
|
|
There are however instances in which Linux can be used asynchronously. In
|
|
such cases RTAI hard real time tasks might use Linux without loosing hard
|
|
real time determinism. Such a capability can be implemented in many ways,
|
|
the one chosen by RTAI is based on a buffering of async Linux syscalls mated
|
|
either to an optional callback function or to inquiry mechanisms to help
|
|
implementing any user own async call policy. Beware of not reusing read/write
|
|
data buffers made available to async calls though.
|
|
If you do not want, or cannot, either inquire for or check async terminations,
|
|
to avoid running the risk of reusing an yet unserved buffer, use RTAI dynamic
|
|
memory or SCB to allocate them in real time, freeing them afterward, possibly
|
|
with the help of a support callback function.
|
|
Async support is a bit tricky but, provided one cares of what said above, it
|
|
should work always when Linux syscalls arguments are fully contained in the
|
|
passed registers structure. That is not the case for a few Linux syscalls that
|
|
are multiplexed within a single framework, whereas libc can end in building a
|
|
hidden object on the stack, so that it is not assured it will be available at
|
|
the time the server uses it. On some archs a notable example of that is the
|
|
socketcall, but RTAI should already care of it appropriately. There remain
|
|
nonetheless the chance of other instances to be cared as well. So beware that
|
|
some async calls might not work with a few Linux services yet, in which case
|
|
you should let us know about it for a possible fix.
|
|
Just to mention another typical care to be taken we notice that printf might
|
|
work as it is but it will often crash. So the safe way to printf is as follows:
|
|
- sprintf(buffer, format, args, ....);
|
|
- write(STDOUT_FILENO, buffer, strlen(buffer));
|
|
|
|
Provided the socketcall has been intercepted well you should be able to do many
|
|
IO operations asynchronously, e.g.: dumping data directly to disk, Linux ipcs
|
|
and POSIX mq, fifos, pipes, serial and networked communications etc. etc.
|
|
In many applications one often requires RTAI specific IO drivers just to avoid
|
|
loosing real time for communications with non real time support/interface
|
|
processes and tasks. The async server should be of much help in such cases by
|
|
letting you use standard Linux, with the ensuing advantage of not needing any
|
|
special hard real time driver.
|
|
|
|
Clearly what above does not impede you to set up your own async server to
|
|
which your hard real time task can be connected by using RTAI non blocking
|
|
communication functions, i.e. those ending with "_if". So the choice of the
|
|
most viable solution is left to you.
|
|
|
|
How it works
|
|
------------
|
|
|
|
By using the functions described below a server thread is created that will
|
|
block waiting for parent task requests to Linux. Whenever the RTAI scheduler
|
|
intercepts a Linux syscall in hard real time mode it passes it to the server
|
|
thread and:
|
|
- remains waiting for its execution if in sync mode;
|
|
- returns immediately if in async mode.
|
|
The server carries out the syscall and:
|
|
- resumes the hard real time parent, returning what returned by Linux, if in
|
|
sync mode:
|
|
- calls a callback function, if one has been made available, in async mode,
|
|
returning an identifier.
|
|
|
|
As said in sync mode real time is lost, but there will be two task switches per
|
|
Linux service request only, while there will be four if no sync server is used.
|
|
In async mode there is no need for any task switch, as the server will execute
|
|
in soft mode when there will be no RTAI real time activity any more.
|
|
The need of passing syscall data to the server is responsible for most of the
|
|
penalty incurred by using a Linux server.
|
|
Let's stress once more that this is a far better alternative way to what the
|
|
RTAI scheduler will have to do anyhow, i.e. make you soft and recover hard mode
|
|
at the next RTAI proper service call, which will require four task switches,
|
|
keeping you in Linux hands from the Linux syscall till an RTAI function is
|
|
called again.
|
|
With a server instead you will stay soft just either till Linux has finished
|
|
servicing your request, when in sync mode, or kept working in real time, when
|
|
in async mode.
|
|
|
|
API Functions prototypes
|
|
------------------------
|
|
|
|
The available functions are:
|
|
|
|
- void *rt_create_linux_syscall_server(RT_TASK *task, int mode, void
|
|
(*callback_fun)(long, long), int nr_bufd_async_calls);
|
|
create a Linux syscall server for task, a NULL task means the current one; a
|
|
non NULL return is the handle allowing you to link to the server for any
|
|
further management request, see functions below, NULL indicates a failure in
|
|
setting the server up; a NULL callback_fun is allowed. It will operate
|
|
according to mode setting, either SYNC_LINUX_SYSCALL or ASYNC_LINUX_SYSCALL.
|
|
Beware of using an appropriate nr_bufd_async_calls. If async requests overrun
|
|
the latest will be discarded till some space is available again.
|
|
If a server exists already it is terminated and a new one replaces it.
|
|
|
|
- void rt_destroy_linux_syscall(RT_TASK *task);
|
|
destroy the linux_syscall_server of task, if NULL use the current task.
|
|
|
|
- void *rt_sync_async_linux_syscall_server_create(RT_TASK *task, int mode,
|
|
void (*callback_fun)(long, long), int nr_bufd_async_calls);
|
|
legacy interface, the same as:
|
|
rt_create_linux_syscall_server(task, mode, callback_fun, nr_bufd_async_calls).
|
|
|
|
- void *rt_linux_syscall_server_create(RT_TASK *task);
|
|
legacy call, the same as:
|
|
rt_create_linux_syscall_server(task, SYNC_LINUX_SYSCALL, NULL, 1).
|
|
|
|
Notice that in sync mode Linux functions will return their usual values while
|
|
that is not possible in async mode and what returned is an identifier, id, of
|
|
that specific async call, a negative id meaning the call was not satisfied
|
|
because of a buffer overrun. When <= 0 ids can be used for monitoring async
|
|
calls execution, as explained below.
|
|
|
|
- int rt_set_linux_syscall_mode(int mode, void (*callback_fun)(long, long);
|
|
to both switch between sync and async mode and change the call back function.
|
|
It returns EINVAL if there is no server or the mode setting is not correct.
|
|
|
|
Notice that this is a legacy call, you'd better use:
|
|
|
|
- int rt_linux_syscall_mode(struct linux_syscalls_list *syscalls, int mode);
|
|
to switch between sync and async mode; syscalls is the handle returned by
|
|
rt_sync_async_linux_syscall_server_create; mode can be: SYNC_LINUX_SYSCALL,
|
|
ASYNC_LINUX_SYSCALL, LINUX_SYSCALL_GET_MODE.
|
|
The function returns the previous mode and LINUX_SYSCALL_GET_MODE can be used
|
|
to just inquire for it, in which case mode is discarded. EINVAL is returned if
|
|
syscalls is not a valid pointer.
|
|
|
|
- void *rt_linux_syscall_cbfun(struct linux_syscalls_list *syscalls, void (*cbfun)(long, long));
|
|
to change the callback function; syscalls is the handle returned by
|
|
rt_sync_async_linux_syscall_server_create; cbfun can be either a NULL, no
|
|
callback will be used, a pointer to a true callback function,
|
|
LINUX_SYSCALL_GET_CALLBACK.
|
|
The function returns the pointer to the previous callback function and
|
|
LINUX_SYSCALL_GET_CALLBACK can be used to just inquire for it. EINVAL is
|
|
returned if syscall is not a valid pointer
|
|
|
|
The callback function afford a notification control mechanism for async calls.
|
|
An alternative way to monitor async calls execution is given by the following
|
|
functions:
|
|
|
|
- int rt_linux_syscall_status(struct linux_syscalls_list *syscalls, int id, int *retval);
|
|
syscalls is the handle returned from rt_sync_async_linux_syscall_server_create;
|
|
id is the value returned by the call to Linux, retval is a pointer to an int
|
|
that will receive the value returned by Linux after executing the related
|
|
service.
|
|
It returns:
|
|
- EINVAL if syscalls is not a valid pointer or id < 0
|
|
- ENOENT if no id is found, meaning the function has been overwritten;
|
|
- ECANCELED when the function has call has been explicitly canceled by the
|
|
user;
|
|
- EINPROGRESS when the function is pending waiting for execution;
|
|
- 0 when the function has been executed, in such a case retval, if not NULL,
|
|
will receive the value returned by Linux.
|
|
|
|
- int rt_linux_syscall_cancel(struct linux_syscalls_list *syscalls, int id);
|
|
Syscalls is the handle returned from rt_sync_async_linux_syscall_server_create;
|
|
id is the value returned by the call to Linux.
|
|
It returns:
|
|
- EINVAL if syscalls is not a valid pointer or id < 0
|
|
- ENOENT if no id is found, meaning the function has been overwritten;
|
|
- -id when the function has been executed already;
|
|
- 0 if the function has been successfully canceled.
|
|
|
|
Examples
|
|
-------
|
|
|
|
They are in RTAI "showroom" CVS: linux_server and printer_server.
|
|
|
|
Paolo.
|