Re: What is required to build a user server?

From: Eric Jacobs <Eric_Jacobs_at_nospam.org>
Date: Wed Aug 18 1999 - 19:36:12 PDT

esd@gbb.com.au writes:
>What specifications are required to build a user server?
>
>eg. What would be required to build a server that printed "Hello World"
>
>How is the server invoked?
>
>cheers
>
>Eric

In VSTa, a server is a process that creates one or more message ports
and presumably receives on them every once in a while. Other than
that, they're no different from any other process. They run as user
code and can make any system calls that other processes can. They can
be clients to other servers as well as being a server itself. For
example, if you run vstafs on your hard disk, then vstafs is a client
to wd and server to sh, ls, gcc, etc, whatever process is using that
filesystem. In this way the process has two different "faces" to the
operating system. Any process can behave this way. (What would you
call 'em, servents? Or cliers?)

Servers are also invoked just as you would other processes. If you
decide you need to use the floppy drive in the middle of a session,
just do:

# fd &

Don't forget the &, as otherwise you connect your terminal to fd, and
fd doesn't interact on stdin or stdout. There's nothing saying it
couldn't, however, as a matter of convention, servers run unattended
and don't usually expect to talk to a human user.

To be considered a server, a process creates a message port using the
syscall msg_port(). There are basically two ways to call it:

        handle = msg_port(###, NULL);

        handle = msg_port(NULL, &portname);

You would use the first syntax if you wanted to establish the server
on a specific global port name, which you would specify as the first
parameter. There are a couple of officially assigned port names in the
VSTa headers. You can, of course, pick any number you like, but
msg_port() will fail if that number is already taken.

Mostly likely you'll want to use the second form. In this case, the
system will pick a port number for you and store it in the variable
whose address you pass in the second parameter. This is usually what
you want. Then you can give that port number to other processes.
That's the number they'll use to connect to your server.

The VSTa standard service namer allows servers to associate their
port numbers with text identifiers in a global, hierarchical
namespace. So, for instance, programs can use //fs/root:a/b/c instead
of //1025:a/b/c. The library function namer_register() can do the
grudge work for you. Lookup helper functions are also included in the
library.

The value that's returned by msg_port is your process's private handle
on to that message port. Don't pass this value to other processes; it
has no meaning in their context. It is not the same as the global port
name.

Note that you can create multiple ports if you want to. The limit is
defined in the kernel; I think it is set to 4 currently. Most servers
will only need one message port.

To handle messages that come in from clients, you would use the
msg_receive syscall:

        struct msg m;

        x = msg_receive(handle, &m);

This is a blocking call that waits for the next message to come in
from any client (or potential client.) The message structure has the
following fields:

      m_sender : an opaque number used to identify the client

      m_op : this is the operation code. It's either a number that
                  the client gave in its msg_send() call, or a
                  kernel-generated (and trusted) M_ system message. See
                  below.

      m_arg : a single argument to go with the operation.

      m_arg1 : another argument for the operation. (Not usually
                  used.)

      m_nseg : The number of data buffers that the client has sent.
                  Usually only one, but clients can send up to four
                  buffers in a single call. Servers should handle clients
                  who send more than one buffer at a time. (There are
                  library functions to help with this.)

      m_seg : An array of buffer definitions that the client sent.

      m_buf : A quick way to reference the first buffer.

      m_buflen : A quick way to reference the length of the first buffer.

If msg_receive() returns a negative number, the receive operation
was interrupted by an event while waiting for a client.

Bit 31 of m_op, known as M_READ, has a special meaning. It is used to
indicate the direction of data flow between the client and server. If
clear, it means that the client is sending buffers to the server. If
set, it means that the server is sending buffers to the client. It's
the client's responsibility to get the value of M_READ right. The
server will usually mask it out. If a client asks for FS_READ instead
of FS_READ|M_READ, for example, the server will work just fine;
however, the kernel will never transfer the buffers to the client.

Once a msg_receive() has returned successfully, the client will be
blocked waiting for some kind of response from the server. In general,
the two possible responses (but see below) are msg_reply(), indicating
success (you can pass an integer back, as the return value for
msg_send()), and msg_err(), with which you can pass back an error
string.

Message operation codes that are at or below the predefined constant
M_RESVD are reserved for the kernel; ordinary clients can't
deliberately send them with msg_send(). The most important ones are
the following:

      M_CONNECT : a client is trying to connect. The m_buf contains
                    a trusted array of the user's permissions. In this
                    case, you don't use msg_reply() to indicate
                    success. You rather use msg_accept() instead.
                    Errors are still reported with msg_err().

      M_DISCONNECT: a client has disconnected. This message is a bit
                    different in that it is asynchronous: no reply is
                    necessary; the client is already gone.

      M_DUP : a client has requested a clone of this connection
                    instance. Essentially, the client becomes two
                    initially identical clients, each independent from
                    then on. This is necessary for fork() as well as
                    the clone() syscall. In general, servers should let
                    their clients clone themselves.

      M_ABORT : the client is no longer requesting the operation.
                    Happens when the client is interrupted (by event,
                    for example) while they're waiting for the server
                    to respond. You'll likely only need to do any
                    processing for this message if you poll for
                    messages in between the msg_receive() and the
                    msg_reply()/msg_err() of a client operation (i.e.,
                    you're asynchronous). In that case, you need to
                    clear the asynchronous request and msg_reply()
                    to the M_ABORT. If you're not asynchronous, too
                    late; you've already replied and your message
                    has been thrown out. In this case, just msg_reply()
                    to the M_ABORT.

      M_ISR : this message is sent by the kernel when a hardware
                    interrupt has occurred that your server has
                    requested. You must have enabled the IRQ in order
                    to receive this message.

Messages above M_RESVD are user messages. The most common ones are the
filesystem messages:

All devices:
FS_STAT
FS_WSTAT - these handle reading and writing of attributes

Character special: (pipes, console, serial, TCP, etc.) above plus:
FS_READ
FS_WRITE

Files: all above plus
FS_SEEK
FS_ABSREAD
FS_ABSWRITE - optional, these two combine a seek and r/w operation

Directories/file systems: all above except FS_WRITE and FS_ABSWRITE
plus
FS_OPEN
FS_RENAME
FS_REMOVE

In the case of FS_OPEN, the client descends a level into the hierarchy.
The client is then "inside" the file they opened. The other two
manipulate the current directory, but don't change the client. FS_READ
on a directory reads a list of names of items that may be FS_OPEN'ed.

Any other messages can be defined; the messages numbers are not
interpreted by the kernel at all. I would encourage the creation of
additional classes of message operations if a server has actions that
do not fit well into the already existing message codes. (Some VSTa
servers, such as sema, insist on using the FS_ message codes even
though they don't really make sense in what the server has to do.)

Hopefully this will get you started. Also don't be afraid to look at
the source of some of the VSTa servers. They're well commented.

A couple of other things: message ports exist in a process-wide
context; they aren't associated with any particular thread. Any
thread may receive messages on any port. If two threads msg_receive
on the same port at the same time, one will get the next message,
the other will get the message after that. However, the most common
use of threads in servers is to have one run the message loop while
the others do client operations.
Received on Wed Aug 18 18:32:11 1999

This archive was generated by hypermail 2.1.8 : Thu Sep 22 2005 - 15:12:56 PDT