Search code examples
clinuxpointersioaio

Using Linux AIO, able to do IOs but writing garbage as well into the file


This might seem silly, but, I am using libaio ( not posix aio), I am able to write something into the file, but I am also writing extra stuff into the file.

I read about the alignment requirement and the data type of the buffer field of iocb.

Here is the code sample ( only relevant sections of use, for representation )

aio_context_t someContext;
struct iocb somecb;
struct io_event someevents[1];
struct iocb *somecbs[1];
somefd = open("/tmp/someFile", O_RDWR | O_CREAT);
char someBuffer[4096];

... // error checks 
someContext = 0; // this is necessary 
io_setup(32, &someContext ); // no error checks pasted here
strcpy(someBuffer, "hello stack overflow"); 


memset(&somecb, 0, sizeof(somecb));
somecb.aio_fildes = somefd ;
somecb.aio_lio_opcode = IOCB_CMD_PWRITE;
somecb.aio_buf = (uint64_t)someBuffer;
somecb.aio_offset = 0;
somecb.aio_nbytes = 100; // // // 
// I am avoiding the memeaign and sysconf get page part in sample paste
somecbs[0] = &somecb;  // address of the solid struct, avoiding heap
// avoiding error checks for this sample listing 
io_submit(someContext, 1, somecbs); 
// not checking for events count or errors 
io_getevents(someContext, 1, 1, someevents, NULL);

The Output:

This code does create the file, and does write the intended string hello stack overflow into the file /tmp/someFile.

The problem:

The file /tmp/someFile also contains after the intended string, in series, @^@^@^@^@^@^@^@^@^ and some sections from the file itself ( code section), can say garbage.

I am certain to an extent that this is some pointer gone wrong in the data field, but cannot crack this.

  • How to use aio ( not posix) to write exactly and only 'hello world' into a file?

I am aware that aio calls might be not supported on all file systems as of now. The one I am running against does support.

Edit - If you want the starter pack for this attempt , you can get from here.

http://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt

Edit 2 : Carelessness, I was setting up more number of bytes to write to within the file, and the code was honoring it. Put simply, to write 'hw' exactly one needed no more than 2 bytes in the bytes field of iocb.


Solution

  • There's a few things going on here. First up, the alignment requirement that you mentioned is either 512 bytes or 4096 bytes, depending on your underlying device. Try 512 bytes to start. It applies to:

    1. The offset that you're writing in the file must be a multiple of 512 bytes. It can be 0, 512, 1024, etc. You can write at offset 0 like you're doing here, but you can't write at offset 100.

    2. The length of data that you're writing to the file must be a multiple of 512 bytes. Again, you can write 512 bytes, 1024 bytes, or 2048 bytes, and so on - any multiple of 512. You can't write 100 bytes like you're trying to do here.

    3. The address of the memory that contains the data you're writing must be a multiple of 512. (I typically use 4096, to be safe.) Here, you'll need to be able to do someBuffer % 512 and get 0. (With the code the way it is, it most likely won't be.)

    In my experience, failing to meet any of the above requirements doesn't actually give you an error back! Instead, it'll complete the I/O request using normal, regular old blocking I/O.

    Unaligned I/O: If you really, really need to write a smaller amount of data or write at an unaligned offset, then things get tricky even above and beyond the io_submit interface. You'll need to do an aligned read to cover the range of data that you need to write, then modify the data in memory and write the aligned region back to disk.

    For example, say you wanted to modify offset 768 through 1023 on the disk. You'd need to read 512 bytes at offset 512 into a buffer. Then, memcpy() the 256 bytes you wanted to write 256 bytes into that buffer. Finally, you issue a write of the 512 byte buffer at offset 512.

    Uninitialized Data: As others have pointed out, you haven't fully initialized the buffer that you're writing. Use memset() to initialize it to zero to avoid writing junk.

    Allocating an Aligned Pointer: To meet the pointer requirements for the data buffer, you'll need to use posix_memalign(). For example, to allocate 4096 bytes with a 512 byte alignment restriction: posix_memalign(&ptr, 512, 4096);

    Lastly, consider whether you need to do this at all. Even in the best of cases, io_submit still "blocks", albeit at the 10 to 100 microsecond level. Normal blocking I/O with pread and pwrite offers a great many benefits to your application. And, if it becomes onerous, you can relegate it to another thread. If you've got a latency-sensitive app, you'll need to do io_submit in another thread anyway!