Home > Articles > Hardware > Upgrading & Repairing

  • Print
  • + Share This
Like this article? We recommend

Asynchronous Completion

In the previous example, the I/O operation isn’t completely asynchronous. While it does happen in the background, the application explicitly has to wait for it to finish. In many cases, this is undesirable. In these cases, you can use signal notifications to finish the AIO operations.

The standard way of sending asynchronous notifications to a process in UNIX is via the signals mechanism. Traditional UNIX signals were very primitive; they told you that an event had occurred, but nothing else. POSIX extends this capability to allow the delivery of an integer or a pointer to the signal handler.

To arrange for a signal to be delivered, we use the aio_sigevent member of the aiocb structure. This member tells the system the kind of notifications that should be used when an AIO operation completes. The following table lists the members.

Field

Contents

sigev_notify

The mechanism used to send the notification. For single-threaded programs, this should be SIGEV_SIGNAL.

sigev_signo

The number of the signal to send.

sigev_value

A union of an integer and a pointer, which is delivered to the signal handler.

The first thing we need to do is decide on a signal to use for asynchronous I/O notifications. The POSIX Realtime Signals Extension defines a range of signals between SIGRTMIN and SIGRTMAX that support some additional semantics; if signals in this range are delivered while a signal handler is running, they’ll be enqueued.

When installing the signal handler, using sigaction, we use the SA_SIGINFO flag to tell the system that we want to use the new-style signal handler. This technique delivers the sigev_value field, specified in our AIO request, to our signal handler, which allows us to know exactly which operation has completed.

In the following example, aio2.c, we’ll initiate two AIO operations, as we did previously. This time, the AIO control blocks will be stored in a global array, and the array index of the control block will be sent to the signal.

#include <aio.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

struct aiocb * cb[2];

//The signal number to use.
#define SIG_AIO SIGRTMIN+5

//Signal handler called when an AIO operation finishes
void aio_handler(int signal, siginfo_t *info, void*uap)
{
	int cbNumber = info->si_value.sival_int;
	printf("AIO operation %d completed returning %d\n", 
		cbNumber,
		aio_return(cb[cbNumber]));
}

int main(void)
{
	struct sigaction action;
	//Create a buffer to store the read data
	char * foo = calloc(1,20);
	//Set up the signal handler
	action.sa_sigaction = aio_handler;
	action.sa_flags = SA_SIGINFO;
	sigemptyset(&action.sa_mask);
	sigaction(SIG_AIO, &action, NULL);
	FILE * file = fopen("bar", "r+");
	
	//Allocate space for the aio control blocks
	cb[0] = calloc(1,sizeof(struct aiocb));
	cb[1] = calloc(1,sizeof(struct aiocb));
	//Somewhere to store the result
	cb[0]->aio_buf = foo;
	cb[1]->aio_buf = foo + 10;
	//The file to read from
	cb[0]->aio_fildes = fileno(file);
	cb[1]->aio_fildes = fileno(file);
	//The number of bytes to read, and the offset
	cb[0]->aio_nbytes = 10;
	cb[1]->aio_nbytes = 10;
	cb[0]->aio_offset = 0;
	cb[1]->aio_offset = 10;
	//The signal to send, and the value of the signal
	cb[0]->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
	cb[0]->aio_sigevent.sigev_signo = SIG_AIO;
	cb[0]->aio_sigevent.sigev_value.sival_int = 0;
	cb[1]->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
	cb[1]->aio_sigevent.sigev_signo = SIG_AIO;
	cb[1]->aio_sigevent.sigev_value.sival_int = 1;

	aio_read(cb[0]);
	aio_read(cb[1]);
	while(1){sleep(1);}
	sleep(1);
}

When we run this code, we should get output like the following:

$ gcc aio2.c -lrt
$ ./a.out
AIO operation 0 completed returning 10
AIO operation 1 completed returning 10

The program will end in an infinite loop, so be sure to terminate it with Ctrl+C.

This mechanism can be used to have a pool of AIO control blocks which can be used when needed and then returned to the pool when the associated operation completes.

  • + Share This
  • 🔖 Save To Your Account