Microbian manual pages

Copyright © 2017–2023 J. M. Spivey
Jump to navigation Jump to search

This page has a unix-style manual page for each micro:bian system call, and for the type of messages that are passed as argument to send and receive.

exit

Name

exit – terminate current process

Synopsis

void exit(void);

Description

The exit() function causes the calling process to terminate. The current process is put in a state where it will never be scheduled again, and the scheduler selects another process to run. The exit() function never returns to the calling process.

Notes

If the main function of a process returns, this is equivalent to calling exit() just before returning.

start

Name

start – create a process

Synopsis

int start(char *name, void (*body)(int), int arg, int stacksize);

#define STACK 1024

Description

The function start() must be called only before concurrent execution of the program begins, typically from the main program init(). It creates a process with a specified name and body, and arranges to pass an specified integer argument to the body when it is activated.

  • The string name is a descriptive name for the process used only for debugging. The string should be statically allocated (a string constant is acceptable). Nothing bad will happen if several processes have the same name, apart from a possible ambiguity in printed messages.
  • The function body(arg) is the body of the process: it will be invoked with a single integer argument. It is acceptable to use the same function as the bodies of multiple processes, which then run the same code independently of each other; the integer argument assists in allowing such processes to tell themselves apart.
  • The integer arg is the argument passed to the body when it is called.
  • The integer stacksize gives the size in bytes of the stack space that will be allocated to the process. The total space allocated for all processes must fit within the available RAM; the pre-defined constant STACK is a useful default for use before the true stack requirement of each process have been determined.

The function returns the process ID of the newly created process.

send

Name

send – send a message

Synopsis

void send(int dest, int type, message *msg);

New interface:

void send(int dest, message *msg);
void send_msg(int dest, int type);
void send_int(int dest, int type, int val);
void send_ptr(int dest, int type, void *val);

Description

The function send() suspends the calling process until the process with id dest is ready to receive a message from the calling process. The message is then copied from sender to receiver, and both are free to continue. The argument type is an integer identifiying the kind of message begin sent. The argument msg may be NULL, or may point to a message buffer containing data associated with the message. When the message is delivered, the operating system fills in the sender and type fields automatically, and copies other message fields from the buffer, if any.

In the new interface, the type field and any desired data fields should have been filled in the message buffer msg before calling send; the msg parameter must not be NULL. The alternative forms send_msg, send_int and send_ptr are provided as a convenience for when the message contains just a type field, or a type field together with a single integer or pointer field.

receive

Name

receive – receive a message

Synopsis

void receive(int type, message *msg);

#define ANY (-1)

Description

The function receive() suspends the calling process until another process sends it a message, either with a specified type, or (if the argument type has the special value ANY) a message of any type. The argument msg, if non-null, must point to a buffer that will store the received message; when a sender is available, the message is coped from sender to receiver, and the sender and type fields are filled in with the true identity of the sender and the type of message, and other fields are copied across from the sender's message buffer.

Processes waiting to send to a given process are held in a queue, and are admitted on a first come, first served basis according to the type argument specified in calls to receive from the process. Interrupt messages of type INTERRUPT from the fictitious process HARDWARE jump the queue and are delivered in preference to others.

sendrec

Name

sendrec – combined send and receive

Synopsis

void sendrec(int dest, int type, message *msg);

#define REPLY 2

New interface:

void sendrec(int dest, message *msg);

#define REPLY 2

Description

The call sendrec(dst, type, msg) is equivalent to the sequence

send(dest, type, msg); receive(REPLY, msg);

but is slightly more efficient, and avoids a potential problem with priority inversion. It is useful where messages are used to implement a form of remote procedure call by making the client send a request message and then wait for a REPLY. An implementation restriction means that it is forbidden to use a call to sendrec() to send a REPLY message, but that is not needed in common usage, where sendrec() is used to receive a reply.

In the new interface, again sendrec(dst, msg) is equivalent to the sequence

send(dest, msg); receive(REPLY, msg);

This time, fields of the message, including its type field, should have been filled in before the call, and the msg parameter must not be NULL.

yield

Name

yield – allow scheduling of other processes

Synopsis

void yield(void);

Description

The function yield() invokes the scheduler, allowing other processes to run, just as they would if the calling process invoked send() or receive(). The calling process joins the back of the queue of processes at its priority level, and will be scheduled again once other ready processes of the same or higher priority have had a chance to run. Calls to yield() are not generally needed by applications, but the function is used internally by the operating system as part of the start-up mechanism.

connect

Name

connect – connect to interrupt source

Synopsis

void connect(int irq);

Description

The function connect() associates the calling process with a source of interrupts, identified by number. The priority of the process is also set to 0, the most urgent priority. Subsequent interrupts will be converted into messages of type INTERRUPT to the process from the fictitious process HARDWARE. In the standard implementation of micro:bian, an interrupt is disabled (with disable_irq()) before sending the message, and must be re-enabled in the driver process if future interrupts are to be sent.

If the process is not ready to receive the INTERRUPT message when it is due to be sent, one such message can be queued for each process to be sent later. Normally, the interrupt will be disabled at the same time that such a message is queued; but if a second or subsequent interrupt happens before the first message is delivered, then those interrupts are lost.

priority

Name

priority – set priority of current process

Synopsis

void priority(int prio);

#define P_HANDLER 0
#define P_HIGH 1
#define P_LOW 2
#define P_IDLE 3

Description

The function priority() sets the priority of the calling process to a specified value. The possible priority levels for a process range from 0 to 3, with lower numbers corresponding to more urgency. Priority 0 is automatically associated with processes connected to interrupts, and priority 3 is reserved for the idle process, which puts the processor to sleep when there is nothing to do. Other processes may have a priority of either 1 or 2. When the scheduler is choosing a process to run, processes with numerically lower priority are preferred. Since processes become ready by sending or receiving messages, and the scheduler runs at those times, it is normally true that the running process is one of the most urgent ones that are ready.

message

Name

message – data type for messages

Synopsis

typedef struct {                /* 16 bytes */
    unsigned short type;        /* Type of message */
    short sender;               /* PID of sender */
    union {                     /* An integer, a pointer, or four bytes */
        int int1; void *ptr1;
        struct { byte byte1, byte2, byte3, byte4; };
    };
    union { int int2; void *ptr2; }; /* Another integer or pointer */
    union { int int3; void *ptr3; }; /* A third integer or pointer */
} message;

Description

Structures of type message are the units of information transmitted by calls to send and receive. Each message has two fixed fields: type, a small integer that identifies the type of message, and sender, which is filled in by the transmission process with the true process id of the sender. In the context of an application, the type field determines the interpretation of the rest of the message. Note that there is no protection in the type system against inconsistencies of interpretation between the sender and receiver of a message: a value that is sent as an integer could be interpreted by the receiver as a pointer, with disastrous results.

In addition to the fixed fields, there is space for up to three other pieces of information, each of which can be an integer, a pointer, or (in for the first slot) a group of four bytes. Passing a pointer from one process to another raises the possibility that the memory accessed by the pointer may be shared between the sending and receiving processes. Programs must be written so that concurrent access to this memory does not introduce race conditions. The pointer fields of a message are given the universal pointer type void *, and it is up to sender and receiver to ensure that they are interpreted as pointers to the same kind of object.

Given a message buffer m, the fields of m can be referred to as m.type and m.sender, and either m.int1 or m.ptr1 or m.byte1 up to m.byte4, etc. The C idiom being used here, anonymous nested structures and unions, is not described in the classic text by Kernighan and Ritchie, but is an innovation in C'2011. The meaning should be clear: by m.byte1 we mean the byte1 member of the four-byte structure that is one of the alternatives for the first word of payload in the message. Previous versions of C would have made us write m.word1.bytes.byte1 for the same member.