This chapter provides an introduction to programming considerations for input and output handling and the input and output handling (I/O) subroutines.
The input and output (I/O) library subroutines can send data to or from either devices or files. The system treats devices as if they were I/O files. For example, you must also open and close a device just as you do a file.
Some of the subroutines use standard input and standard output as their input and output channels. For most of the subroutines, however, you can specify a different file for the source or destination of the data transfer. For some subroutines, you can use a file pointer to a structure that contains the name of the file; for others, you can use a file descriptor (that is, the positive integer assigned to the file when it is opened).
The I/O subroutines stored in the C Library (libc.a) provide stream I/O. To access these stream I/O subroutines, you must include the stdio.h file using the following statement:
#include <stdio.h>
Some of the I/O library subroutines are macros defined in a header file and some are object modules of functions. In many cases, the library contains a macro and a function that do the same type of operation. Consider the following when deciding whether to use the macro or the function:
The files, commands, and subroutines used in I/O handling provide the following interfaces:
Low-level | Basic open and close functions for files and devices. |
Stream | Read and write I/O for pipes and FIFOs. |
Terminal | Formatted output and buffering. |
Asynchronous | Concurrent I/O and processing. |
Input Language | The lex and yacc commands generate a lexical analyzer and a parser program for interpreting I/O. |
Low-level I/O interfaces are direct entry points into a kernel, providing functions such as opening files, reading to and writing from files, and closing files.
The line command provides the interface that allows one line from standard input to be read and the following subroutines provide other low-level I/O functions:
open, openx, or creat | Prepare a file, or other path object, for reading and writing by means of an assigned file descriptor |
read, readx, readv, or readvx | Read from an open file descriptor |
write, writex, writev, or writevx | Write to an open file descriptor |
close | Relinquish a file descriptor |
The open and creat subroutines set up entries in three system tables. A file descriptor indexes the first table, which functions as a per process data area that can be accessed by read and write subroutines. Each entry in this table has a pointer to a corresponding entry in the second table.
The second table is a per-system data base, or file table, that allows an open file to be shared among several processes. The entries in this table indicate if the file was open for reading, writing, or as a pipe, and when the file was closed. There is also an offset to indicate where the next read or write will take place and a final pointer to indicates entry to the third table, which contains a copy of the file's i-node.
The file table contains entries for every instance of an open or create subroutine on the file, but the i-node table contains only one entry for each file.
Note: While processing an open or creat subroutine for a special file, the system always calls the device's open subroutine to allow any special processing (such as rewinding a tape or turning on a data-terminal-ready modem lead). However, the system uses the close subroutine only when the last process closes the file (that is, when the i-node table entry is deallocated). This means that a device cannot maintain or depend on a count of its users unless an exclusive-use device (that prevents a device from being reopened before its closed) is implemented.
When a read or write operation takes place, the user's arguments and the file table entry are used to set up the following variables:
If the file referred to is a character-type special file, the appropriate read or write subroutine is called to transfer data and update the count and current location. Otherwise, the current location is used to calculate a logical block number in the file.
If the file is an ordinary file, the logical block number must be mapped to a physical block number. A block-type special file need not be mapped. The resulting physical block number is used to read or write the appropriate device.
Block device drivers can provide the ability to transfer information directly between the user's core image and the device in blocks as large as the caller requests without using buffers. The method involves setting up a character-type special file corresponding to the raw device and providing read and write subroutines to create a private, non-shared buffer header with the appropriate information. If desired, separate open and close subroutines can be provided, and a special-function subroutine can be called for magnetic tape.
Stream I/O interfaces provide data as a stream of bytes that is not interpreted by the system, which offers more efficient implementation for networking protocols than character I/O processing. There are no record boundaries when reading and writing using stream I/O. For example, a process reading 100 bytes from a pipe cannot tell if the process that wrote the data into the pipe did a single write of 100 bytes, or two writes of 50 bytes, or even if the 100 bytes came from two different processes.
Stream I/Os can be pipes or FIFOs, first in, first out files. FIFOs are similar to pipes because they allow the data to flow only one way (left to right). However, a FIFO can be given a name and can be accessed by unrelated processes, unlike a pipe. FIFOs are sometimes referred to as named pipes. Because it has a name, a FIFO can be opened using the standard I/O fopen subroutine. To open a pipe, you must call the pipe subroutine, which returns a file descriptor, and the standard I/O fdopen subroutine to associate an open file descriptor with a standard I/O stream.
Stream I/O interfaces are accessed through the following subroutines and macros:
fclose | Closes a stream |
feof, ferror, clearerr, or fileno | Check the status of a stream |
fflush | Write all currently buffered characters from a stream |
fopen, freopen, or fdopen | Open a stream |
fread or fwrite | Perform binary input |
fseek, rewind, ftell, fgetpos, or fsetpos | Reposition the file pointer of a stream |
getc, fgetc, getchar, or getw | Get a character or word from an input stream |
gets or fgets | Get a string from a stream |
getwc, fgetwc, or getwchar | Get a wide character from an input stream |
getws or fgetws | Get a string from a stream |
printf, fprintf, sprintf, wsprintf, vprintf, vfprintf, vsprintf, or vwsprintf | |
Print formatted output | |
putc, putchar, fputc, or putw | Write a character or a word to a stream |
puts or fputs | Write a string to a stream |
putwc, putwchar, or fputwc | Write a character or a word to a stream |
putws or fputws | Write a wide character string to a stream |
scanf, fscanf, sscanf, or wsscanf | Convert formatted input |
setbuf, setvbuf, setbuffer, or setlinebuf | Assign buffering to a stream |
ungetc or ungetwc | Push a character back into the input stream |
Terminal I/O interfaces operate between a process and the kernel, providing functions such as buffering and formatted output.
Every terminal and pseudo-terminal has a tty structure that contains the current terminal group ID. This field identifies the process group to receive the signals associated with the terminal.
Terminal I/O interfaces are accessed through the iostat command, which monitors I/O system device loading, and the uprintfd daemon, which allows kernel messages to be written to the terminal screen.
A daemon opens a terminal device in order to log error messages to the /dev/tty or /dev/console file. If background writes are not allowed, disassociate the daemon process from the controlling terminal.
Terminal characteristics can be enabled or disabled through the following subroutines:
cfgetospeed, cfsetospeed, cfgetispeed, or cfsetispeed | |
Get and set input and output baud rates | |
ioctl | Performs control functions associated with open file descriptors, such as controlling the ability of background processes to produce output on the control terminal |
termdef | Queries terminal characteristics |
tcdrain | Waits for output to complete |
tcflow | Performs flow control functions |
tcflush | Discards data from the specified queue |
tcgetaattr | Gets terminal state |
tcgetpgrp | Gets foreground process group ID |
tcsendbreak | Sends a break on an asynchronous serial data line |
tcsetattr | Sets terminal state |
ttylock, ttywait, ttyunlock, or ttylocked | |
Control tty locking functions | |
ttyname or isatty | Get the name of a terminal |
ttyslot | Finds the slot in the utmp file for the current user |
Asynchronous I/O subroutines allow a process to start an I/O operation and have the subroutine return immediately after the operation is started or queued. Another subroutine is required to wait for the operation to complete (or return immediately if the operation is already finished). This means that a process can overlap its execution with its I/O or overlap I/O between different devices. Although asynchronous I/O does not significantly improve performance for a process that is reading from a disk file and writing to another disk file, asynchronous I/O provides significant performance improvements for other types of I/O driven programs, such as programs that dump a disk to a magnetic tape or display an image on an image display.
Although not required, a process performing asynchronous I/O can tell the kernel to notify it when a specified descriptor is ready for I/O (also called signal-driven I/O). The kernel notifies the user process with the SIGIO signal.
To use asynchronous I/O, a process must perform three steps:
The following asynchronous I/O subroutines are provided:
aio_cancel | Cancels one or more outstanding asynchronous I/O requests |
aio_error | Retrieves the error status of an asynchronous I/O request |
aio_read | Reads asynchronously from a file descriptor |
aio_return | Retrieves the return status of an asynchronous I/O request |
aio_suspend | Suspends the calling process until one or more asynchronous I/O requests is completed |
aio_write | Writes asynchronously to a file descriptor |
lio_listio | Initiates a list of asynchronous I/O requests with a single call |
poll or select | Check I/O status of multiple file descriptors and message queues |
For use with the poll subroutine, the following header files are supplied:
poll.h | Defines the structures and flags used by the poll subroutine |
aio.h | Defines the structure and flags used by the aio_read, aio_write, and aio_suspend subroutines |