3.4   Buffered I/O: Stdio

The VxWorks I/O library provides a buffered I/O package that is compatible with the UNIX and Windows stdio package and provides full ANSI C support. To include the stdio package in the VxWorks system, select INCLUDE_ANSI_STDIO for inclusion in the project facility VxWorks view; see Tornado User's Guide: Projects for information on configuring VxWorks.

Note that the implementation of printf( ), sprintf( ), and sscanf( ), traditionally considered part of the stdio package, is part of a different package in VxWorks. These routines are discussed in 3.5 Other Formatted I/O.

3.4.1   Using Stdio

Although the VxWorks I/O system is efficient, some overhead is associated with each low-level call. First, the I/O system must dispatch from the device-independent user call (read( ), write( ), and so on) to the driver-specific routine for that function. Second, most drivers invoke a mutual exclusion or queuing mechanism to prevent simultaneous requests by multiple users from interfering with each other.

Because the VxWorks primitives are fast, this overhead is quite small. However, an application processing a single character at a time from a file incurs that overhead for each character if it reads each character with a separate read( ) call:

n = read (fd, &char, 1);

To make this type of I/O more efficient and flexible, the stdio package implements a buffering scheme in which data is read and written in large chunks and buffered privately. This buffering is transparent to the application; it is handled automatically by the stdio routines and macros. To access a file with stdio, a file is opened with fopen( ) instead of open( ) (many stdio calls begin with the letter f):

fp = fopen ("/usr/foo", "r");

The returned value, a file pointer (or fp) is a handle for the opened file and its associated buffers and pointers. An fp is actually a pointer to the associated data structure of type FILE (that is, it is declared as FILE *). By contrast, the low-level I/O routines identify a file with a file descriptor (fd), which is a small integer. In fact, the FILE structure pointed to by the fp contains the underlying fd of the open file.

An already open fd can be associated belatedly with a FILE buffer by calling fdopen( ):

fp = fdopen (fd, "r");

After a file is opened with fopen( ), data can be read with fread( ), or a character at a time with getc( ), and data can be written with fwrite( ), or a character at a time with putc( ).

The routines and macros to get data into or out of a file are extremely efficient. They access the buffer with direct pointers that are incremented as data is read or written by the user. They pause to call the low-level read or write routines only when a read buffer is empty or a write buffer is full.


*

WARNING: The stdio buffers and pointers are private to a particular task. They are not interlocked with semaphores or any other mutual exclusion mechanism, because this defeats the point of an efficient private buffering scheme. Therefore, multiple tasks must not perform I/O to the same stdio FILE pointer at the same time.

The FILE buffer is deallocated when fclose( ) is called.

3.4.2   Standard Input, Standard Output, and Standard Error

As discussed earlier in 3.3 Basic I/O, there are three special file descriptors (0, 1, and 2) reserved for standard input, standard output, and standard error. Three corresponding stdio FILE buffers are automatically created when a task uses the standard file descriptors, stdin, stdout, and stderr, to do buffered I/O to the standard fds. Each task using the standard I/O fds has its own stdio FILE buffers. The FILE buffers are deallocated when the task exits.