DNIX - Asynchronous Events

Asynchronous Events

DNIX's native system call was the dnix(2) library function, analogous to the standard Unix unix(2) or syscall(2) function. It took multiple arguments, the first of which was a function code. Semantically this single call provided all appropriate Unix functionality, though it was syntactically different from Unix and had, of course, numerous DNIX-only extensions.

DNIX function codes were organized into two classes: Type 1 and Type 2. Type 1 commands were those that were associated with I/O activity, or anything that could potentially cause the issuing process to block. Major examples were F_OPEN, F_CLOSE, F_READ, F_WRITE, F_IOCR, F_IOCW, F_WAIT, and F_NAP. Type 2 were the remainder, such as F_GETPID, F_GETTIME, etc. They could be satisfied by the kernel itself immediately.

To invoke asynchronicity, a special file descriptor called a trap queue had to have been created via the Type 2 opcode F_OTQ. A Type 1 call would have the F_NOWAIT bit OR-ed with its function value, and one of the additional parameters to dnix(2) was the trap queue file descriptor. The return value from an asynchronous call was not the normal value but a kernel-assigned identifier. At such time as the asynchronous request completed, a read(2) (or F_READ) of the trap queue file descriptor would return a small kernel-defined structure containing the identifier and result status. The F_CANCEL operation was available to cancel any asynchronous operation that hadn't yet been completed, one of its arguments was the kernel-assigned identifier. (A process could only cancel requests that were currently owned by itself. The exact semantics of cancellation was up to each request's handler, fundamentally it only meant that any waiting was to be terminated. A partially completed operation could be returned.) In addition to the kernel-assigned identifier, one of the arguments given to any asynchronous operation was a 32-bit user-assigned identifier. This most often referenced a function pointer to the appropriate subroutine that would handle the I/O completion method, but this was merely convention. It was the entity that read the trap queue elements that was responsible for interpreting this value.

struct itrq { /* Structure of data read from trap queue. */ short it_stat; /* Status */ short it_rn; /* Request number */ long it_oid; /* Owner ID given on request */ long it_rpar; /* Returned parameter */ };

Of note is that the asynchronous events were gathered via normal file descriptor read operations, and that such reading was itself capable of being made asynchronous. This had implications for semi-autonomous asynchronous event handling packages that could exist within a single process. (DNIX 5.2 did not have lightweight processes or threads.) Also of note is that any potentially blocking operation was capable of being issued asynchronously, so DNIX was well equipped to handle many clients with a single server process. A process was not restricted to having only one trap queue, so I/O requests could be grossly prioritized in this way.

Read more about this topic:  DNIX

Famous quotes containing the word events:

    As I look at the human story I see two stories. They run parallel and never meet. One is of people who live, as they can or must, the events that arrive; the other is of people who live, as they intend, the events they create.
    Margaret Anderson (1886–1973)