|HP-UX Reference > A
HP-UX 11i Version 3: February 2007
aio — POSIX asynchronous I/O facility
The POSIX Asynchronous I/O facility implements Section 6.7 of IEEE Standard 1003.1b-1993, Standard for Information Technology, Portable Operating System Interface (POSIX), Part 1: System Application Program Interface (API), Amendment 1: Realtime Extensions (C Language). It allows a process or thread to start multiple simultaneous read and/or write operations to multiple files, to wait for or obtain notification of completion of requested operations, and to retrieve the status of completed operations. The purpose of the POSIX Asynchronous I/O facility is to allow a process or thread to overlap some elements of computation and information processing with I/O processing.
The POSIX Asynchronous I/O facility includes the following interface functions:
To use these functions, link in the realtime library by specifying -lrt on the compiler or linker command line.
Asynchronous I/O Control Block
The Asynchronous I/O Control Block (aiocb) is used as a parameter to all of the asynchronous I/O functions. The aiocb specifies parameters for an asynchronous I/O operation in a call to aio_read(), aio_write(), or lio_listio() and then may be used as a "handle" for the enqueued asynchronous I/O operation in a subsequent call to aio_cancel(), aio_suspend(), aio_fsync(), aio_error(), or aio_return().
The aiocb structure contains the following members:
int aio_fildes; /* file descriptor */ off_t aio_offset; /* file offset */ void *aio_buf; /* location of buffer */ size_t aio_nbytes; /* length of transfer */ int aio_reqprio; /* request priority offset */ struct sigevent aio_sigevent; /* signal number and value */ int aio_lio_opcode; /* operation to be performed */
The aiocb supplied to aio_read(), aio_write(), or lio_listio() must contain the parameters that would be supplied in a normal synchronous read() or write() function call, where aio_fildes corresponds to fildes, aio_nbytes corresponds to nbytes, and aio_offset corresponds to the implicit file offset. The aiocb may also specify a request priority delta value (aio_reqprio), and signaling information to satisfy unique realtime and asynchronous I/O requirements. For the lio_listio() function, the aio_lio_opcode field specifies whether the operation is a read or write.
Once an asynchronous I/O operation has been enqueued for a particular aiocb, its address is used as a handle for other asynchronous I/O functions and can only be used to refer to a single enqueued operation.
Other fields defined in the aiocb structure are reserved for future use and extension. They are all ignored by the asynchronous I/O facility.
Certain values as defined by the POSIX standard are declared in aio.h.
The following values are returned by the aio_cancel() function:
The following values are valid values of the flags field that controls return from the lio_listio() function:
The following values are operation codes supplied in the aio_lio_opcode field the designate the type of an operation started with lio_listio().
Enqueuing of Operations
If an error condition is detected that prevents an operation from being started, aio_read() and aio_write() do not enqueue a request. Instead they immediately return -1 and set errno to indicate the cause of the failure. Once an operation has been successfully enqueued, calls to aio_error() and aio_return() must be used to determine the status of the operation and any error conditions, including those normally reported by read() and write(). The request remains enqueued and consumes process and system resources until aio_return() is called.
Error reporting of operations enqueued by lio_listio() may be less immediate than that of operations enqueued by aio_read() and aio_write(). With the exception of resource shortages, errors for which aio_read() and aio_write() would immediately return -1 and an errno value do not cause lio_listio() to stop enqueuing the current or subsequent requests in its list. Instead, partial success occurs. In this case, the application must use aio_error() to determine which operations in its list have been enqueued and which resulted in errors.
Asynchronous I/O operations are said to be complete when one of the following is true:
If a valid sigevent is specified in the aiocb used to start the operation, then that signal is delivered when the operation completes.
Reading and Writing Asynchronously
Asynchronous read and write operations are started using the aio_read(), aio_write(), and lio_listio() interfaces. The parameters for each operation are provided in the aiocb used to start the operation. A list of aiocb pointers can be provided to the lio_listio() function call, in which case the type (read or write) of the operation is determined from the aio_lio_opcode field of the aiocb. Once started, the I/O operations may proceed concurrently with execution of the process or thread that initiated the operation.
With the implementation of HP-UX kernel threads, an application may achieve asynchronous I/O behavior by using synchronous read() and write() functions from independent threads within the process. However, the application may have to implement many of the status management facilities provided in the POSIX Asynchronous I/O facility.
Waiting for Completion
The POSIX Asynchronous I/O facility supports both polling and notification models. The polling model is implemented by repeatedly calling the aio_error() function to test the status of an operation. The notification model is implemented by designating a sigevent in the aiocb used to start the operation. The specified notification, if any, is then performed when the operation completes.
The aio_suspend() function allows the application to wait for completion of one or more asynchronous I/O operations. A timeout may be set so that the process can again execute and take appropriate recovery actions if the expected operations do not complete as expected. If the aio_suspend() references multiple operations, return is made when any one of the operations completes.
Once an asynchronous I/O operation has been started, its status can be tested with the aio_error() and aio_return() functions, which return the current status of a referenced aiocb. For a polling implementation, the aio_error() function is used to check the status until a completion status is seen; then aio_return() is used to free the aiocb for re-use.
For a notification implementation, status of the completed I/O can be determined and the aiocb freed with a single call to aio_return().
The errors reported by aio_error() and aio_return() include all of the errors normally reported by read() and write() plus errors unique to asynchronous I/O processing. After an asynchronous I/O operation is started but before an error is detected or the operation completes successfully, aio_error() will return EINPROGRESS.
The aio_cancel() function allows an application to request cancellation of an asynchronous I/O operation. The aiocb used to start the operation may be used as a handle to identify it for cancellation. Cancellation of all operations pending for a file may also be requested. Not all asynchronous I/O operations can be canceled.
Synchronizing Permanent Storage
The aio_fsync() function supports synchronizing the contents of permanent storage when multiple asynchronous I/O operations are outstanding for the file or device. Only those requests already enqueued for the designated file at the time of the function call are included in the synchronization operation.
Asynchronous I/O operations are not inherently sequential. Each operation must specify an offset, and the file offset is never updated as a result of an asynchronous I/O operation.
Setting the O_APPEND flag on a file limits the value of asynchronous I/O to that file. When O_APPEND is set, operations on the file will be handled serially with the ending file length after one request providing the starting offset for the next. While there may be some advantage to allowing the system to queue requests, care should be taken not to exhaust system or process resources by enqueuing a large number of requests that must be processed serially.
System Limitations and Restrictions
The operation of the POSIX Asynchronous I/O interfaces is subject to certain system limitations and restrictions.
Since each enqueued asynchronous I/O operation requires allocation of system memory for its internal control structure, the number of simultaneously enqueued asynchronous I/O operations that the system can have pending is limited. The maximum number of asynchronous I/O operations that all active processes on the system may have enqueued concurrently is tunable. The current maximum value can be obtained using the sysconf() call with the argument _SC_AIO_MAX. The default maximum value is 2048. In addition to the system-wide limit, there is a per-process limit. It is controlled using the argument RLIMIT_AIO_OPS to the getrlimit() and setrlimit() system calls. Even though an asynchronous I/O operation has completed, it still remains enqueued until aio_return() is called for that operation.
Asynchronous I/O operations which use the request and call back mechanism rather than the threads mechanism for I/O, are subject to a system-wide limit on the amount of physical memory that can be locked during asynchronous I/O transfers. This system-wide maximum number of bytes of memory that can be locked simultaneously for aio requests is tunable. This can be set as a percentage of the physical memory available on the system. By default, it is set to 10% of the physical memory. In addition to the system-wide limit, there is a per-process limit which is controlled using the argument RLIMIT_AIO_MEM to the getrlimit() and setrlimit() system calls. Further, the amount of memory that can be locked at a time for any reason, not just for asynchronous I/O, is controlled by the system-wide limit lockable_mem. Other system activity, including explicit memory locking with plock() and/or mlock() interfaces may affect the amount of lockable memory at any given time.
The maximum priority change that can be specified in aio_reqprio is limited. The maximum priority change value is tunable. The current maximum value can be obtained using the sysconf() call with the argument _SC_AIO_PRIO_DELTA_MAX. The default value is 20.
The maximum number of asynchronous I/O operations that can be specified in a single lio_listio() call is limited. This limit is tunable. The current maximum value can be obtained using the sysconf() call with the argument _SC_AIO_LISTIO_MAX. The default maximum value is 256.
Some asynchronous I/O operations are also subject to both system-wide and per-process limits on the number of simultaneously active threads. See pthread(3T).
Programming Limitations and Restrictions
Altering the contents of or deallocating memory associated with the aiocb referred to by aiocbp or the buffer referred to by aiocbp->aio_buf while an asynchronous read operation is outstanding, that is aio_return() has not been called for the aiocb, may produce unpredictable results.
The following code sequence illustrates an asynchronous read operation and polling for completion.
#include <fcntl.h> #include <errno.h> #include <aio.h> char buf; int retval; struct aiocb myaiocb; bzero( &myaiocb, sizeof (struct aiocb)); myaiocb.aio_fildes = open( "/dev/null", O_RDONLY); myaiocb.aio_offset = 0; myaiocb.aio_buf = (void *) buf; myaiocb.aio_nbytes = sizeof (buf); myaiocb.aio_sigevent.sigev_notify = SIGEV_NONE; retval = aio_read( &myaiocb ); if (retval) perror("aio:"); /* continue processing */ ... /* wait for completion */ while ( (retval = aio_error( &myaiocb) ) == EINPROGRESS) ; /* free the aiocb */ retval = aio_return( &myaiocb);