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.
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.
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.
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.
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.
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.
Do not use the
select
system call with asynchronous I/O; the results are
undefined.
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.
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.
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.
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.
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.
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
<EMPHASIS>all
I/O operations on the specified file descriptor,
whether initiated by synchronous or asynchronous functions.
You may have applications which call for performing asynchronous I/O
operations by reading to and writing from raw partitions.
DIGITAL 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.
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.
/* * 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); }
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.
/* * 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.