Используя Linux AIO, способный выполнять операции ввода-вывода, но также записывать мусор в файл.

Это может показаться глупым, но я использую libaio (не posix aio), я могу что-то записать в файл, но я также записываю в файл дополнительные данные.

Я читал о требованиях к выравниванию и типе данных поля буфера iocb.

Вот пример кода (только соответствующие разделы использования для представления)

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);

Вывод:

Этот код создает файл и записывает предполагаемое переполнение стека строки приветствия в файл /tmp/someFile.

Проблема:

Файл /tmp/someFile также содержит после предполагаемой строки последовательно @^@^@^@^@^@^@^@^@^ и некоторые разделы из самого файла ( раздел кода), можно сказать фигня.

Я в какой-то степени уверен, что это неправильный указатель в поле данных, но не могу это взломать.

  • Как использовать aio (не posix), чтобы точно и только «привет мир» записать в файл?

Я знаю, что на данный момент вызовы aio могут поддерживаться не во всех файловых системах. Тот, против кого я выступаю, поддерживает.

Изменить. Если вам нужен стартовый пакет для этой попытки, вы можете получить его здесь.

http://www.fsl.cs.sunysb.edu/%7Evass/linux-aio.txt

Редактировать 2: Небрежность, я устанавливал большее количество байтов для записи в файле, и код учитывал это. Проще говоря, чтобы написать ровно 'hw', требовалось не более 2 байтов в поле байтов iocb.


person rrai    schedule 17.08.2017    source источник
comment
Длина вашей предполагаемой строки составляет 100 байт или, возможно, короче?   -  person Hasturkun    schedule 17.08.2017
comment
Наконец-то размер файла /tmp/someFile равен 100, верно?   -  person alk    schedule 17.08.2017
comment
@Hasturkun короче.   -  person rrai    schedule 17.08.2017
comment
Обратите внимание, что вы настраиваете запись 100 байт в somecb.aio_nbytes = 100;, что, как я полагаю, является вашим окончательным размером файла. Вероятно, вы пишете за пределами инициализированной части вашего буфера. Вы, вероятно, захотите использовать фактическую длину строки при записи.   -  person Hasturkun    schedule 17.08.2017
comment
1) somecb.aio_buf = (uint64_t)someBuffer; : Я не вижу здесь необходимости в гипсе. 2) struct iocb *somecbs[1]; Вам это не нужно. 3) вы предполагаете нулевые/нулевые заполненные буферы.   -  person wildplasser    schedule 18.08.2017


Ответы (1)


Здесь происходит несколько вещей. Во-первых, требование выравнивания, о котором вы упомянули, составляет либо 512 байт, либо 4096 байт, в зависимости от вашего базового устройства. Попробуйте 512 байт для начала. Это относится к:

  1. Смещение, которое вы записываете в файл, должно быть кратно 512 байтам. Это может быть 0, 512, 1024 и т. д. Вы можете писать по смещению 0, как вы делаете здесь, но вы не можете писать по смещению 100.

  2. Длина данных, которые вы записываете в файл, должна быть кратна 512 байтам. Опять же, вы можете записать 512 байт, 1024 байт или 2048 байт и т. д. — любое число, кратное 512. Вы не можете записать 100 байт, как вы пытаетесь сделать здесь.

  3. Адрес памяти, содержащей данные, которые вы записываете, должен быть кратен 512. (Я обычно использую 4096, чтобы быть в безопасности.) Здесь вам нужно будет сделать someBuffer % 512 и получить 0. (С кодируйте так, как есть, скорее всего, не будет.)

По моему опыту, несоблюдение любого из вышеперечисленных требований на самом деле не возвращает вам ошибку! Вместо этого он завершит запрос ввода-вывода, используя обычный старый блокирующий ввод-вывод.

Невыровненный ввод-вывод: если вам действительно нужно записать меньший объем данных или записать с невыровненным смещением, то все становится сложнее даже за пределами интерфейса io_submit. Вам нужно будет выполнить выровненное чтение, чтобы охватить диапазон данных, которые вам нужно записать, затем изменить данные в памяти и записать выровненную область обратно на диск.

Например, предположим, что вы хотите изменить смещение от 768 до 1023 на диске. Вам нужно будет прочитать 512 байтов по смещению 512 в буфер. Затем memcpy() 256 байт, которые вы хотели записать в этот буфер. Наконец, вы выполняете запись 512-байтового буфера по смещению 512.

Неинициализированные данные. Как указывали другие, вы не полностью инициализировали записываемый буфер. Используйте memset(), чтобы инициализировать его до нуля, чтобы избежать записи мусора.

Выделение выровненного указателя. Чтобы выполнить требования к указателю для буфера данных, вам необходимо использовать posix_memalign(). Например, чтобы выделить 4096 байт с ограничением выравнивания 512 байт: posix_memalign(&ptr, 512, 4096);

Наконец, подумайте, нужно ли вам это делать вообще. Даже в лучшем случае io_submit по-прежнему "блокирует", хотя и на уровне от 10 до 100 микросекунд. Обычный блокирующий ввод-вывод с pread и pwrite предлагает очень много преимуществ для вашего приложения. И, если это станет обременительным, вы можете перевести его в другой поток. Если у вас есть приложение, чувствительное к задержкам, вам все равно нужно выполнить io_submit в другом потоке!

person Mike Andrews    schedule 17.08.2017
comment
›По моему опыту, невыполнение любого из вышеперечисленных требований на самом деле не возвращает ошибку! Как мне это точно доказать? Достаточно ли будет strace? - person rrai; 18.08.2017
comment
Да, используйте strace -T. Это даст вам время, потраченное на каждый системный вызов. Если ваш вызов io_submit занимает более 50 микросекунд, скорее всего, используется буферизованный ввод-вывод. Если мой ответ помог вам, пожалуйста, примите его. Спасибо! - person Mike Andrews; 18.08.2017