Chapter 3
Introduction to OSKit Interfaces

The OSKit’s interfaces provide clean, well-defined intra-process or intra-kernel protocols that can be used to define the interaction between different modules of an operating system. For example, the OSKit provides a “block I/O” interface for communication between file systems and disk device drivers, a “network I/O” interface for communication between network device drivers and protocol stacks, and a file system interface similar to the “VFS” interface in BSD. However, the OSKit’s interfaces were designed with a number of properties that make them much more useful as parts of a toolkit than are comparable traditional OS-level interfaces. These properties partly stem from the use of the Component Object Model (COM), described in Chapter 4 as the underlying framework in which the interfaces are defined, and partly just from careful interface design with these properties in mind. The primary important properties of the OSKit interfaces include:

As with all other parts of the OSKit, the client is not required to use the OSKit’s interfaces as the primary inter-module interfaces within the system being designed. Similarly, the client may use only some of the interfaces and not others, or may use the OSKit’s interfaces as a base from which to build more powerful, efficient interfaces specialized to the needs of the system being developed. Naturally, since the specific components provided in the OSKit must have some interface, they have been designed to use the standardized OSKit interfaces so that they can easily be used together when desired; however, the OS developer can choose whether to adopt these interfaces as primary inter-module interfaces in the system, or simply to use them to connect to particular OSKit components that the developer would like to use.

3.1 Header File Conventions

This section describes some specific important properties of the design and organization of the OSKit header files.

3.1.1 Basic Structure

All of the OSKit’s public header files are installed in an oskit subdirectory of the main installation directory for header files (e.g., /usr/local/include by default). Assuming client code is compiled with the main include directory in its path, this means that OSKit-specific header files are generally included with lines of the form ‘#include <oskit/foo.h>’. This is also the convention used by all of the internal OSKit components. Confining all the OSKit headers into a subdirectory in this way allows the client OS to place its own header files in the same header file namespace with complete freedom, without worrying about conflicting with OSKit header files.

The OSKit follows this rule even for header files with well-known, standardized names: for example, the ANSI/POSIX header files provided by the minimal C library (e.g., string.h, stdlib.h, etc.) are all located in a header file subdirectory called oskit/c. On the surface this may seem to make it more cumbersome for the client OS to use these headers and hence the minimal C library, since for example it would have to ‘#include <oskit/c/string.h>’ instead of just the standard ‘#include <string.h>’. However, this problem can easily be solved simply by adding the oskit/c subdirectory to the C compiler’s include path (e.g., add -I/usr/local/include/oskit/c to the GCC command line); in fact this is exactly what most of the OSKit components themselves do. Furthermore, strictly confining the OSKit headers to the oskit subdirectory, it makes it possible for the client OS and the OSKit itself to have several different sets of “standard” header files coexisting in the same directory structure: for example, the OSKit components derived from Linux or BSD typically leave oskit/c out of the compiler’s include path and instead use the native OS’s header files; this makes it much easier to incorporate legacy code with minimal changes.

3.1.2 Namespace Cleanliness

A similar namespace cleanliness issue applies to the actual symbols defined by many the OSKit header files. In particular, all OSKit header files defining COM interfaces, as well as any related header files that they cross-include such as oskit/types.h and oskit/error.h, only define symbols having a prefix of oskit_, OSKIT_, osenv_, or OSENV_. This rule allows these headers to be included along with arbitrary other headers from different environments without introducing a significant chance of name conflicts. In fact, the OSKit components derived from legacy systems, such as the Linux driver set and the FreeBSD drivers and TCP/IP stack, depend on this property, to allow them to include the OSKit headers defining the COM interfaces they are expected to export, along with the native Linux or BSD header files that the legacy code itself relies on.

Once again, this rule creates a potential problem for header files whose purpose is to declare standard, well-known symbols, such as the minimal C library header files. For example, string.h clearly should declare memcpy simply as memcpy and not as oskit_memcpy or somesuch, since in the latter case the “C library” wouldn’t be conforming to the standard C library interface. However, there are many types, structures, and definitions that are needed in both the minimal C library headers and the COM interfaces: for example, both the oskit_ttystream COM interface and the minimal C library’s termios.h need to have some kind of termios structure; however, in the former case a disambiguating oskit_ prefix is required, whereas in the latter case such a prefix is not allowed. Although technically these two termios structures exist in separate contexts and could be defined independently, for practical purposes it would be very convenient for them to coincide, to avoid having to perform unnecessary conversions in code that uses both sets of headers. Therefore, the solution used throughout the OSKit header files is to define the non-prefixed versions of equivalent symbols with respect to the prefixed versions whenever possible: for example, the errno.h provided by the minimal C library simply does a ‘#include <oskit/error.h>’ and then defines all the errno values with lines of the form:

 #define EDOM    OSKIT_EDOM

Unfortunately this is not possible for structures since C does not have a form of typedef statement for defining one structure tag as an alias for another. Therefore, the few structures that need to exist in both contexts (such as the termios structure mentioned above) are simply defined twice. Since these structures are generally well-defined and standardized by ANSI C, POSIX, or CAE, they are not expected to change much over time, so the added maintenance burden should not be significant and is vastly outweighed by the additional flexibility provided by the clean separation of namespaces.

3.2 Common Header Files

This section describes a few basic header files that are used throughout the OSKit and are cross-included by many of the other OSKit headers.

3.2.1 boolean.h: boolean type definitions


#include <oskit/boolean.h>


Defines the fundamental values TRUE and FALSE for use with the machine-dependent oskit_bool_t type.

3.2.2 compiler.h: compiler-specific macro definitions


#include <oskit/compiler.h>


Defines a variety of macros used to hide compiler-dependent ways of doing things.

All function prototypes should be surrounded by these macros, so that a C++ compiler will identify them as C functions.
Identifies a function as being inline-able.
Identifies a function as “pure.” A pure function has no side-effects: it examines no values other than its arguments and changes no values other than its return value.
Identifies a function as never returning (e.g., _exit).
Indicates that a function uses an alternative calling convention compatibile with COM. In particular, this option indicates the called function will pop its parameters unless there were a variable number of them.

3.2.3 config.h: OSKit configuration-specific definitions


#include <oskit/config.h>


This file is generated by the configure program. It identifies a number of environment-dependent parameters. Currently these are all related to the compiler and assembler used to build the OSKit.

Defined if the assembler supports the %cr4 register.
Defined if the assembler supports the debug registers.
Defined if the assembler supports the .p2align pseudo-op.
Defined if your assembler supports the .code16 pseudo-op.
Defined if your assembler allows .space within .bss segments.
Defined if your compiler groks __attribute__((packed)) on structs.
Defined if your compiler groks __attribute__((pure)) on functions.
Defined if your compiler groks __attribute__((noreturn)) on functions.
Defined if your compiler groks __attribute__((stdcall)) on functions.

3.2.4 machine/types.h: basic machine-dependent types


#include <oskit/machine/boolean.h>


This header file defines a number of types whose exact definitions are dependent on the processor architecture and compiler in use.

The following set of types are guaranteed to be exactly the indicated width regardless of processor architecture; they are used to get around the fact that different C compilers assign different meanings to the built-in C types such as int and long:

Signed 8-bit integer
Signed 16-bit integer
Signed 32-bit integer
Signed 64-bit integer
Unsigned 8-bit integer
Unsigned 16-bit integer
Unsigned 32-bit integer
Unsigned 64-bit integer
32-bit floating point type
64-bit floating point type

The following types depend in various ways on the target processor architecture:

This type represents the most efficient integer type for storage of simple boolean values; on typical architectures it is the smallest integer type that the processor can handle with no extra overhead.
This is an unsigned integer type the same size as a pointer, which can therefore be used to hold virtual or physical addresses and offsets.
This is an unsigned integer type equivalent to oskit_addr_t, except that it is generally used to represent the size of a data structure or memory buffer, or a difference between two oskit_addr_ts.
This is a signed integer type the same size as oskit_size.
This is an unsigned integer type of the same size as a general-purpose processor register; it is generally but not necessarily always equivalent to oskit_addr_t.
This is a signed integer type the same size as oskit_reg_t.

3.2.5 types.h: basic machine-independent types


This header file defines a few basic types which are used throughout many of the OSKit’s COM interfaces. These types correspond to standard POSIX types traditionally defined in sys/types.h; however, this does not mean that the client OS must use these types as its standard definitions for the corresponding POSIX symbols; it only means that the client must use these types when interacting with OSKit components through the OSKit’s COM interfaces. All of the type names are prefixed by oskit_, for exactly this reason.

Type representing a device number; used in the oskit_stat structure in the OSKit’s file system interfaces. Note that the OSKit’s file system components themselves don’t know or care about the actual assignment or meaning of device numbers; it is up to the client OS to determine how these device numbers are used, if at all.
Type representing a file serial number (“inode” number), again used in the OSKit’s file system interfaces.
Type representing the link count of a file.
Type representing a process ID. This type is used in a few COM interfaces, such as the oskit_ttystream interface which includes POSIX-like job control methods. Currently the OSKit currently does not include any process management facilities, but this type and the related methods that use it are included in case such a facility is added in the future.
Type representing a traditional POSIX user ID. The current OSKit components do not know or care about POSIX security, but for example the NetBSD file system component knows how to store and retrieve user IDs in BSD file system partitions.
Type representing a traditional POSIX group ID. The same considerations apply as for oskit_uid_t.
Type representing a file type and access permissions bit mask; again used in the file system interface.
Type representing a 64-bit file offset; used in the file system interface.
Unicode “wide” character.