I/O operations on a file can be either synchronous or asynchronous.
For synchronous I/O operations, the process calling the I/O request is blocked
until the I/O operation is complete and regains control of execution only
when the request is completely satisfied or fails.
For asynchronous I/O operations,
the process calling the I/O request immediately regains control of execution
once the I/O operation is queued to the device.
When the I/O operation is
completed (either successfully or unsuccessfully), the calling process can
be notified of the event by a signal passed through the
aiocb
structure for the asynchronous I/O function.
Alternatively, the calling process
can poll the
aiocb
structure for completion status.
This chapter includes the following sections:
Data Structures Associated with Asynchronous I/O, Section 7.1
Asynchronous I/O Functions, Section 7.2
Asynchronous I/O to Raw Devices, Section 7.3
Asynchronous I/O Examples, Section 7.4
Asynchronous I/O is most commonly used in realtime applications requiring high-speed or high-volume data collection and/or low-priority journaling functions. Compute-intensive processes can use asynchronous I/O instead of blocking. For example, an application may collect intermittent data from multiple channels. Because the data arrives asynchronously, that is, when it is available rather than according to a set schedule, the receiving process must queue up the request to read data from one channel and immediately be free to receive the next data transmission from another channel. Another application may require such a high volume of reads, writes, and computations that it becomes practical to queue up a list of I/O operation requests and continue processing while the I/O requests are being serviced. Applications can perform multiple I/O operations to multiple devices while making a minimum number of function calls. The P1003.1b asynchronous I/O functions are designed to help meet these realtime needs.
You can perform asynchronous I/O operations using any open file descriptor.
7.1 Data Structures Associated with Asynchronous I/O
The P1003.1b asynchronous I/O functions use the asynchronous I/O control
block
aiocb
.
This control block contains asynchronous operation
information such as the initial point for the read operation, the number of
bytes to be read, and the file descriptor on which the asynchronous I/O operation
will be performed.
The control block contains information similar to that
required for a
read
or
write
function,
but additionally contains members specific to asynchronous I/O operations.
The
aiocb
structure contains the following members:
int aio_fildes; /* File descriptor */ off_t aio_offset; /* File offset */ volatile void *aio_buf; /* Pointer to buffer */ size_t aio_nbytes; /* Number of bytes to transfer */ int aio_reqprio; /* Request priority offset */ struct sigevent aio_sigevent; /* Signal structure */ int aio_lio_opcode;/* Specifies type of I/O operation */
Note that you cannot reuse the
aiocb
structure while
an asynchronous I/O request is pending.
To determine whether the
aiocb
is in use, use the
aio_error
function.
7.1.1 Identifying the Location
When you call either the
aio_read
or
aio_write
function, you must specify how to locate the data to be read or
to position the data to be written.
The
aio_offset
and
aio_nbytes
members of the
aiocb
structure provide information
about the starting point and length of the data to be read or written.
The
aio_buf
member provides information about where the information
should be read or written in memory.
When you use the
aio_write
function to write to a
new file, data is written to the end of a zero-length file.
On additional
write operations, if the O_APPEND flag is set, write operations are appended
to the file in the same order as the calls to the
aio_write
function were made.
If the O_APPEND flag is not set, write operations take
place at the absolute position in the file as given by the
aio_offset
as if the
lseek
function were called immediately
prior to the operation with an
offset
equal to
aio_offset
and a
whence
equal to
SEEK_SET.
On a call to the
aio_read
function, the read operation
takes place at the absolute position in the file as given by
aio_offset
as if the
lseek
function were
called immediately prior to the operation with an
offset
equal to
aio_offset
and a
whence
equal to SEEK_SET.
After a successful call to queue an asynchronous write operation with O_APPEND or to queue an asynchronous read, you must update the value of the offset with the value returned from the read or write operation. The file offset is not dynamically updated, and failure to update the value of the offset can produce incorrect results.
To determine whether the read or write operation was successful, call
the
aio_error
function.
If the operation was successful,
call the
aio_return
function to update the value of the
aio_offset
member after each successful read or write operation.
See
Section 7.2.3
for an example of using these functions
to determine status.
7.1.2 Specifying a Signal
You can send a signal on completion of every read and write operation,
regardless of whether the operation is issued from a call to the
aio_read
,
aio_write
, or
lio_listio
function.
In addition, you can send a signal on completion of the
lio_listio
function.
See
Chapter 5
for more information
on signals and signal handling.
The
aio_sigevent
member refers to a
sigevent
structure that contains the signal number of the signal
to be sent upon completion of the asynchronous I/O request.
The
sigevent
structure is defined in the
signal.h
header file and contains the following members:
union sigval sigev_value; /* Application-defined value */ int sigev_signo; /* Signal to raise */ int sigev_notify /* Notification type */
The sigev_notify member specifies the notification mechanism to use when an asynchronous event occurs. There are two values defined for sigev_notify in P1003.1b: SIGEV_NONE and SIGEV_SIGNAL. SIGEV_NONE indicates that no asynchronous notification is delivered when an event occurs. SIGEV_SIGNAL indicates that the signal number specified in sigev_signo and the application-defined value specified in sigev_value are queued when an event occurs. When the signal is queued to the process, the value of aio_sigevent.sigev_value will be the si_value component of the generated signal. See Chapter 5 for more information.
The
sigev_signo
member specifies the signal
number to be sent on completion of the asynchronous I/O operation.
Setting
the
sigev_signo
member to a legal signal value
will cause that signal to be posted when the operation is complete, if
sigev_notify
equals SIGEV_SIGNAL.
Setting the value to NULL
means that no signal is sent, but the error status and return value for the
operation are set appropriately and can be retrieved using the
aio_error
and
aio_return
functions.
Instead of specifying a signal, you can poll for I/O completion when
you expect the I/O operation to be complete.
7.2 Asynchronous I/O Functions
The asynchronous I/O functions combine a number of tasks normally performed
by the user during synchronous I/O operations.
With synchronous I/O, the application
typically calls the
lseek
function, performs the I/O operation,
and then waits to receive the return status.
Asynchronous I/O functions provide the following capabilities:
Both regular and special files can handle I/O requests.
One file descriptor can handle multiple read and write operations.
Multiple read and write operations can be issued to multiple open file descriptors.
Both sequential and random access devices can handle I/O requests.
Outstanding I/O requests can be canceled.
The process can be suspended to wait for I/O completion.
I/O requests can be tracked when the request is queued, in progress, and completed.
The functions for performing and managing asynchronous I/O operations are as follows:
Function | Description |
aio_cancel |
Cancels one or more requests pending against a file descriptor |
aio_error |
Returns the error status of a specified operation |
aio_fsync |
Asynchronously writes system buffers containing a file's modified data to permanent storage |
aio_read |
Initiates a read request on the specified file descriptor |
aio_return |
Returns the status of a completed operation |
aio_suspend |
Suspends the calling process until at least one of the specified requests has completed |
aio_write |
Initiates a write request to the specified file descriptor |
lio_listio |
Initiates a list of requests |
Refer to the online reference pages for a complete description of these
functions.
7.2.1 Reading and Writing
Asynchronous and synchronous I/O operations are logically parallel operations.
The asynchronous functions
aio_read
and
aio_write
perform the same I/O operations as the
read
and
write
functions.
However, the
aio_read
and
aio_write
functions return control to the calling process
once the I/O is initiated, rather than after the I/O operation is complete.
For example, when reading data from a file synchronously, the application
regains control only after all the data is read.
Execution of the calling
process is delayed until the
read
operation is complete.
In contrast, when reading data from a file asynchronously, the calling
process regains control right after the call is issued, before the read-and-return
cycle is complete.
The
aio_read
function returns once the
read request is initiated or queued for delivery, even if delivery could be
delayed.
The calling process can use the time normally required to transfer
data to execute some other task.
A typical application using asynchronous I/O includes the following steps:
Create and fill the asynchronous I/O control block (aiocb
).
Call the
open
function to open a specified
file and get a file descriptor for that file.
After a call to the
open
function, the file pointer is set to the beginning of the file.
Select flags as appropriate.
[Footnote 2]
If you use signals, establish a signal handler to catch the signal returned on completion of the asynchronous I/O operation.
Call the
aio_read
,
aio_write
,
or
aio_fsync
function to request asynchronous I/O operations.
Call
aio_suspend
if your application needs
to wait for the I/O operations to complete; or continue execution and poll
for completion with
aio_error
; or continue execution until
the signal arrives.
After completion, call the
aio_return
function
to retrieve completion value.
Call the
close
function to close the file.
The
close
function waits for all asynchronous I/O to complete
before closing the file.
On a call to either the
_exit
or
fork
function, the status of outstanding asynchronous I/O operations is undefined.
If you plan to use asynchronous I/O operations in a child process, call the
exec
function before you call the I/O functions.
7.2.2 Using List-Directed Input/Output
To submit list-directed asynchronous read or write operations, use the
lio_listio
function.
As with other asynchronous I/O functions, you
must first establish the control block structures for the individual read
and write operations.
The information contained in this structure is used
during the operations.
The
lio_listio
function takes as
an argument an array of pointers to I/O control block structures, which allows
the calling process to initiate a list of I/O requests.
Therefore, you can
submit multiple operations as a single function call.
You can control whether the
lio_listio
function returns
immediately after the list of operations has been queued or waits until all
the operations have been completed.
The
mode
argument
controls when the
lio_listio
function returns and can have
one of the following values:
Value | Description |
LIO_NOWAIT | Queues the operation, returns, and can signal when the operation is complete. |
LIO_WAIT | Queues the operation, suspends the calling
process until the operation is complete, and does not signal when the
lio_listio
operation is complete. |
Completion means that all the individual operations in the list have
completed, either successfully or unsuccessfully.
In either case, the return
value indicates only the success or failure of the
lio_listio
function call, not the status of individual I/O requests.
In some cases one
or more of the I/O requests contained in the list may fail.
Failure of an
individual request does not prevent completion of any other individual request.
To determine the outcome of each I/O request, examine the error status associated
with each
lio_aiocb
control block.
The
list
argument to the
lio_listio
function is a pointer to an array of
aiocb
structures.
The
aio_lio_opcode
member of the
aiocb
structure defines the I/O operation to be performed and the
aio_fildes
member identifies the file descriptor.
The combination
of these members makes it possible to specify individual read and write operations
as if they had been submitted individually.
Each read or write operation in
list-directed asynchronous I/O has its own status, return value, and
sigevent
structure for signal delivery.
To use list-directed asynchronous I/O in your application, use the following steps:
Create and fill the
aiocb
control blocks.
Call the
open
function to open the specified
files and get file descriptors for the files.
After a call to the
open
function, the file pointer is set to the beginning of the file.
Select flags as appropriate.
If you use signals, establish signal handlers to catch the
signals returned on completion of individual operations after the
lio_listio
function completes, or to catch a signal returned on
completion of the entire list of I/O operations in the
lio_listio
request.
Call the
lio_listio
function.
Call the
close
function to close the files.
The
close
function waits for all I/O to complete before
closing the file.
As with other asynchronous I/O operations, any open function that returns
a file descriptor is appropriate.
On a call to either the
_exit
or
fork
function, the status of outstanding
asynchronous I/O operations is undefined.
7.2.3 Determining Status
Asynchronous I/O functions provide status values when the operation
is successfully queued for servicing and provides both error and return values
when the operation is complete.
The status requirements for asynchronous I/O
are more complex than the functionality provided by the
errno
function, so status retrieval for asynchronous I/O is accomplished through
using the
aio_error
and
aio_return
functions
in combination with each other.
The
aiocbp
argument to the
aio_error
or
aio_return
function provides the address
of an
aiocb
structure, unique for each asynchronous I/O
operation.
The
aio_error
function returns the error status
associated with the specified
aiocbp.
The error
status is the
errno
value that is set by the corresponding
asynchronous I/O
read
or
write
operation.
The
aio_error
function returns EINPROGRESS if the
operation is ongoing.
Once the asynchronous I/O operation is complete, EINPROGRESS
is not returned.
A subsequent call to the
aio_return
function
will show if the operation is successful.
Once you call the
aio_return
function, the system
resources associated with the
aiocb
for the duration of
the I/O operation are returned to the system.
If the
aio_return
function is called for an
aiocb
with incomplete I/O, the
result of the operation is undefined.
To avoid losing data, use the
aio_error
function to ensure completion before you call the
aio_return
function.
Then use the
aio_return
function to retrieve the number of bytes read or written during the asynchronous
I/O operation.
If you do not call the
aio_return
function, the number
of asynchronous I/O resources available for use in your application is reduced
by one for every completed asynchronous I/O operation that does not return
data through a call to the
aio_return
function.
The following example shows how to use the
aio_error
and
aio_return
functions to track the progress of asynchronous
write operations.
.
.
.
return_value = aio_error(aiocbp); if (return_value != EINPROGRESS) { total = aio_return(aiocbp); if (total == -1) { errno = return_value; perror("aio_read"); } }
.
.
.
In this example, the variable total receives the number of bytes read in the operation. This variable is then used to update the offset for the next read operation.
If you use list-directed asynchronous I/O, each asynchronous I/O operation
in the list has an
aiocb
structure and a unique
aiocbp.
7.2.4 Canceling I/O
Sometimes there is a need to cancel an asynchronous I/O operation after it has been issued. For example, there may be outstanding requests when a process exits, particularly if the application uses slow devices, such as terminals.
The
aio_cancel
function cancels one or more outstanding
I/O requests against a specified file descriptor.
The
aiocbp
argument points to an
aiocb
control block
for a specified file descriptor.
If the operation is successfully canceled,
the error status indicates success.
If, for some reason, the operation cannot
be canceled, normal completion and notification take place.
The
aio_cancel
function can return one of the following
values:
Value | Description |
AIO_ALLDONE | Indicates that none of the requested operations
could be canceled because they had already completed when the call to the
aio_cancel
function was made. |
AIO_CANCELED | Indicates that all requested operations were canceled. |
AIO_NOTCANCELED | Indicates that some of the requested operations
could not be canceled because they were in progress when the call to the
aio_cancel
function was made. |
If the value of AIO_NOTCANCELED is returned, call the
aio_error
function and check the status of the individual operations to determine
which ones were canceled and which ones could not be canceled.
7.2.5 Blocking to Completion
The
aio_suspend
function lets you suspend the calling
process until at least one of the asynchronous I/O operations referenced by
the
aiocbp
argument has completed or until a signal
interrupts the function.
If the operation had completed when the call to
the
aio_suspend
function was made, the function returns
without suspending the calling process.
Before using the
aio_suspend
function, your application must already have initiated an I/O request
with a call to the
aio_read
,
aio_write
,
aio_fsync
, or
lio_listio
function.
7.2.6 Asynchronous File Synchronization
The
aio_fsync
function is similar to the
fsync
function; however, it executes in an asynchronous manner,
in the same way that
aio_read
performs an asynchronous
read.
The
aio_fsync
function requests that all I/O operations
queued to the specified file descriptor at the time of the call to
aio_fsync
be forced to the synchronized I/O completion state.
Unlike
fsync
,
aio_fsync
returns control to the calling
process once the operation has been initiated, rather than after the operation
is complete.
I/O operations that are subsequently initiated on the file descriptor
are not guaranteed to be completed by any previous calls to
aio_fsync
.
Like the
aio_read
and
aio_write
functions,
aio_fsync
takes an
aiocbp
value as an argument, which can then be used in subsequent calls to
aio_error
and
aio_return
in order to determine
the error and return status of the asynchronous operation.
In addition, the
aio_sigevent
member of
aiocbp
can
be used to define the signal to be generated when the operation is complete.
Note that the
aio_fsync
function will force to completion
all
I/O operations on the specified file descriptor, whether initiated
by synchronous or asynchronous functions.
7.3 Asynchronous I/O to Raw Devices
You may have applications which call for performing asynchronous I/O
operations by reading to and writing from raw partitions.
Tru64 UNIX
provides the
libaio_raw.a
library for those applications
which will only perform asynchronous I/O operations to raw devices, When using
this library, you are not required to link with
pthreads
,
libmach
, or
libc_r
.
If you attempt to perform asynchronous I/O operations to a file when
linked with
libaio_raw.a
, the request fails with an error
of ENOSYS.
The syntax for compiling or linking with
libaio_raw.a
is as follows:
%
cc -o binary_name my_program -laio_raw
The examples in this section demonstrate the use of the asynchronous
I/O functions.
Example 7-1
uses the
aio
functions;
Example 7-2
uses the
lio_listio
function.
7.4.1 Using the aio Functions
In
Example 7-1, the input file (read synchronously) is copied
to the output file (asynchronously) using the specified transfer size.
A signal
handler counts the number of completions, but is not required for the functioning
of the program.
A call to the
aio_suspend
function is sufficient.
Example 7-1: Using Asynchronous I/O
/* * Command line to build the program: * cc -o aio_copy aio_copy.c -laio -pthread */ /* * * * aio_copy.c * * * */ #include <unistd.h> #include <aio.h> #include <stdio.h> #include <sys/types.h> #include <sys/file.h> #include <signal.h> #include <errno.h> #include <malloc.h> #define BUF_CNT 2 /* number of buffers */ /* To run completion code in the signal handler, define the following: */ #define COMPLETION_IN_HANDLER struct sigaction sig_act; volatile int sigcnt = 0; volatile int total = 0; /* * * * Signal handler * * * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ void sig_action(signo,info,context) int signo; siginfo_t *info; void *context; { printf("Entered sig_action\n"); printf(" signo = %d \n",signo); printf(" si_code = %d \n",info->si_code); #ifndef COMPLETION_IN_HANDLER printf(" si_value.sival_int = %d decimal\n", info->si_value.sival_int); #else printf(" si_value.sival_ptr = %lx hex \n",info->si_value.sival_ptr); /* Call aio_error and aio_return from the signal handler. * Note that si_value is the address of the write aiocb. */ while (aio_error((struct aiocb *)info->si_value.sival_ptr) == EINPROGRESS); /* * * * Update total bytes written to set new file offset * * * */ total += aio_return((struct aiocb *)info->si_value.sival_ptr); #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ sigcnt++; return; } void sig_handler(signo) int signo; { /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ printf("Entered sig_handler, signo = %d \n",signo); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ sigcnt++; return; } /* * * * Main Routine * * * */ main(int argc, char **argv) { int in_file, out_file, rec_cnt = 0; typedef char *buf_p; buf_p buf[BUF_CNT]; aiocb_t a_write; size_t xfer_size; int buf_index, ret; /* * * * Check number of input arguments * * * */ if (argc < 4) { fprintf(stderr, "Usage: %s input-file output-file buf-size-in-Kb\n", argv[0]); exit(0); } /* * * * Open input file * * * */ if ((in_file = open(argv[1], O_RDONLY)) == -1) { perror(argv[1]); exit(errno); } printf("Opened Input File\n"); /* * * * Open output file * * * */ /* If O_APPEND is added to flags, all writes will appear at end */ if ((out_file = open(argv[2], O_WRONLY|O_CREAT, 0777)) == -1) { perror(argv[2]); exit(errno); } printf("Opened Output File \n"); /* * * * Calculate transfer size (# bufs * 1024) * * * */ xfer_size = atol(argv[3]) * 1024; /* * * * Allocate buffers for file copy * * * */ for (buf_index = 0; buf_index < BUF_CNT; buf_index++) buf[buf_index] = (buf_p) malloc(xfer_size); buf_index = 0; /* * * * Init. signal action structure for SIGUSR1 * * * */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ sigemptyset(&sig_act.sa_mask); /* block only current signal */ /* If the SA_SIGINFO flag is set in the sa_flags field then * the sa_sigaction field of sig_act structure specifies the * signal catching function: */ sig_act.sa_flags = SA_SIGINFO; sig_act.sa_sigaction = sig_action; /* If the SA_SIGINFO flag is NOT set in the sa_flags field * then the the sa_handler field of sig_act structure specifies * the signal catching function, and the signal handler will be * invoked with 3 arguments instead of 1: * sig_act.sa_flags = 0; * sig_act.sa_handler = sig_handler; */ /* * * * Estab. signal handler for SIGUSR1 signal * * * */ printf("Establish Signal Handler for SIGUSR1\n"); if (ret = sigaction (SIGUSR1, /* Set action for SIGUSR1 */ &sig_act, /* Action to take on signal */ 0)) /* Don't care about old actions */ perror("sigaction"); /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* * * * Init. aio control block (aiocb) * * * */ a_write.aio_fildes = out_file; a_write.aio_offset = 0; /* write from current */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ a_write.aio_sigevent.sigev_notify = SIGEV_SIGNAL; a_write.aio_sigevent.sigev_signo = SIGUSR1; /* completion signal */ #ifdef COMPLETION_IN_HANDLER /* Fill in a user-specified value which will be the si_value * component of the generated signal. sigev_value is a union * of either an int (sival_int) or a void * (sival_ptr). * In this example, we use the sival_ptr field, and pass * the address of the aiocbp into the signal handler, so that * the signal handler can call aio_error and aio_return directly: */ a_write.aio_sigevent.sigev_value.sival_ptr = &a_write; #else /* Pass an integer value into the signal handler: */ a_write.aio_sigevent.sigev_value.sival_int = 1; #endif /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* * * * Copy from in_file to out_file * * * */ while (in_file != -1) { int buf_len; /* * * * Read next buffer of information * * * */ buf_len = read( in_file, buf[buf_index], xfer_size); #ifdef COMPLETION_IN_HANDLER if (rec_cnt) { /* will be >1 on all but first write... */ aiocb_t *wait_list = &a_write; /* Wait until previous write completes */ aio_suspend(&wait_list,1,NULL); } /* if (rec_cnt) */ #else if (rec_cnt) { /* will be >1 on all but first write... */ /* previous write completed? If not, wait */ while (aio_error(&a_write) == EINPROGRESS) { aiocb_t *wait_list = &a_write; /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ /* No timeout specified */ aio_suspend(&wait_list,1,NULL); /* aio_suspend(1, &wait_list); */ /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ } /* * * * Update total bytes written to set new file offset * * * */ total += aio_return(&a_write); } /* if (rec_cnt) */ #endif /* * * * Check for end-of-file (won't have filled buffer) * * */ if (buf_len <= 0) break; /* * * * Set buffer up for next write * * * */ a_write.aio_nbytes = buf_len; a_write.aio_buf = buf[buf_index]; /* if file is opened for append, can ignore offset field */ a_write.aio_offset = total; ret = aio_write(&a_write); if (ret) { perror ("aio_write"); exit(ret); } /* * * Update record count, and position to next buffer * * */ rec_cnt++; buf_index ^= 1; } printf("total number of bytes written to output file = %d\n",total); /* * * * Close files * * * */ close(in_file); printf("Closed Input File\n"); close(out_file); printf("Closed Output File\n"); printf("Copied: %d records, %d signals taken\n", rec_cnt, sigcnt); }
7.4.2 Using the lio_listio Function
In
Example 7-2
the input file is read synchronously to a
specified number of output files (asynchronously) using the specified transfer
size from the
lio_listio
function.
After the list-directed
I/O completes, it checks the return status and value for the write to each
file and continues in a loop until the copy is complete.
Example 7-2: Using lio_listio in Asynchronous I/O
/* * Command line to build the program: * cc -o lio_copy lio_copy.c -non_shared -O0 -L/usr/ccs/lib \ * -laio -pthread */ /* * * * lio_copy.c * * * */ #include <unistd.h> #include <aio.h> #include <stdio.h> #include <sys/types.h> #include <sys/file.h> #include <signal.h> #include <errno.h> #include <malloc.h> #define FOR_EACH_FILE for (i = 0; i < out_cnt; i++) #define BUF_CNT 2 /* number of buffers */ /* * * * ------------------ Main Routine ------------------- * * * */ main(int argc, char **argv) { register int i, rec_cnt = 0, out_cnt = 0; char outname[128], temp[8]; int in_file, out_file[AIO_LISTIO_MAX], len; typedef char *buf_p; buf_p buf[BUF_CNT]; aiocb_t a_write[AIO_LISTIO_MAX], *wait_list[AIO_LISTIO_MAX]; size_t xfer_size; int buf_index, total[AIO_LISTIO_MAX], ret; struct sigevent lio_sigevent = {0,0}; /* * * * Check the number of input arguments * * * */ if (argc < 5) { fprintf(stderr, "Usage: %s in_file out_file buffsz-in-kb #-out-files\n", argv[0]); exit(0); } /* * * * Open the input file * * * */ if ((in_file = open(argv[1], O_RDONLY)) == -1) { perror(argv[1]); exit(errno); } printf("\tOpened Input File %s\n", argv[1]); /* * * * Open the output files * * * */ out_cnt = atoi(argv[4]); if ((out_cnt <= 0) || (out_cnt > AIO_LISTIO_MAX)) { fprintf(stderr, "Number of output files must be 1-%d.\n", AIO_LISTIO_MAX); exit(EINVAL); } outname[0] = '\0'; len = strlen(argv[2]); strcpy(outname, argv[2]); FOR_EACH_FILE { sprintf(&outname[len], "%d", i); /* * If O_APPEND is added to flags, all writes will appear at * end */ if ((out_file[i] = open(outname, O_WRONLY|O_CREAT, 0777)) == -1) { perror(outname); exit(errno); } printf("\tOpened output file %s\n", outname); } /* * * * Calculate the transfer size (# bufs * 1024) * * * */ xfer_size = atol(argv[3]) * 1024; /* * * * Allocate buffers for file copy * * * */ for (buf_index = 0; buf_index < BUF_CNT; buf_index++) { buf[buf_index] = (buf_p) malloc(xfer_size); if (buf[buf_index] == NULL) { perror("malloc"); exit(1); } } buf_index = 0; /* * * * Init the aio control blocks and wait list * * * */ FOR_EACH_FILE { a_write[i].aio_fildes = out_file[i]; a_write[i].aio_lio_opcode = LIO_WRITE; a_write[i].aio_sigevent.sigev_signo = 0; wait_list[i] = &a_write[i]; total[i] = 0; } /* * * * Copy from in_file to out_file * * * */ while (in_file != -1) { int buf_len; /* * * * Read the next buffer of information * * * */ buf_len = read(in_file, buf[buf_index], xfer_size); if (rec_cnt) { /* will be >1 on all but the first write... */ /* * * * Update the bytes written to set new offset * * * */ FOR_EACH_FILE { errno = aio_error(&a_write[i]); ret = aio_return(&a_write[i]); if (ret == -1) { perror("Write error"); exit(1); } else { total[i] += ret; } } } /* * * * Check for end-of-file (won't have filled buffer) * * */ if (buf_len <= 0) break; /* * * * Set the buffer up for the next write * * * */ FOR_EACH_FILE { a_write[i].aio_nbytes = buf_len; a_write[i].aio_buf = buf[buf_index]; /* if opened for append, ignore offset field */ a_write[i].aio_offset = total[i]; } ret = lio_listio(LIO_WAIT, wait_list, out_cnt, &lio_sigevent); if (ret) /* report failure status, but don't exit yet */ perror("lio_listio"); /* * * Update record count, and position to next buffer * * */ buf_index ^= 1; rec_cnt++; } /* * * * Close the files * * * */ close(in_file); printf("\tClosed input file\n"); FOR_EACH_FILE { close(out_file[i]); } printf("\tClosed output files\n"); printf("Copied %d records to %d files\n", rec_cnt * out_cnt, out_cnt); }
Note
Use of the
printf
function in this example is for illustrative purposes only. You should avoid usingprintf
and any similar functions in signal handlers because they can affect scheduling characteristics.