Краш системы при read_aio

Модератор: Fastman

Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
EugenOS
проходил мимо
Сообщения: 4
Зарегистрирован: 2015-10-30 16:44:14

Краш системы при read_aio

Непрочитанное сообщение EugenOS » 2015-10-30 17:22:25

Система вылетает с крашем, где-то на 10-15-м чтении aio_read().

Перевожу работающий проект на асинхронный доступ к файлам. Пока, так скажем, в режиме "эмуляции блокирующего доступа". Т.к. не имел с этим дела нигде кроме Win. Заработает - буду менять алгоритм. Все данные в файлах обрабатываются блоками по 4К. (изначально использовали RAW доступ к разделам. чтобы исключить системное кеширование, и использовали свое. Здесь используется обычная freebsd-ufs и файлы в ней. ).

в общем имею класс:

Код: Выделить всё

class FileDB {
public:
...
    void read( int key ); // блокирующее чтение
    void read_aio( int key ); // асинхронное чтение
    void getLastKeyData(uint32_t* buffer); // получение данных после блокирующего чтения
    void getLastKeyData_aio(uint32_t* buffer); // получение данных после асинхронного чтения
    int getLastKeyRecordCount(){ return count; }; // кол-во считанных записей, в процессе последнего чтения.
private:
    int fd;
    bool volatile asyncOpInProgress;
    int sectorSize;    
    uint64_t currentIdxSector;
    uint64_t currentDataSector;
    uint32_t* tmpIdx;
    uint64_t* dataSectorBuf;
    uint64_t lastDataPos;
    int rdOffs;
    int rdSize;
    std::string aFileName;
    int count; // кол-во записей, указанных в индексе последнего чтения.
    struct aiocb aio; 
    struct aiocb *cblist[1];
    int err_r;
}
функции блокирующего и асинхронного чтения идентичны, за исключением следующего кода:

Код: Выделить всё

void FileDB::read( int key )
{        
.....
    if (currentDataSector != sectorNum)
    { // если этот сектор данных еще не читали, читаем
        lseek( fd, sectorNum * sectorSize, SEEK_SET );
        rdSize_l = (rdSize_l > sectorSize) ? sectorSize * 2 : sectorSize;
        ::read( fd, dataSectorBuf, rdSize_l  );
        currentDataSector = sectorNum;
    }    
}

void FileDB::read_aio( int key )
{
.....
    memset(&aio, 0, sizeof( aio ));     
    if (currentDataSector != sectorNum)
    { // если этот сектор данных еще не читали, читаем
        aio.aio_fildes = fd;
        aio.aio_offset = sectorSize * sectorNum;        
        aio.aio_buf = (char*)tmpIdx;
        rdSize_l = (rdSize_l > sectorSize) ? sectorSize * 2 : sectorSize;
        aio.aio_nbytes = rdSize_l;
        aio_read(&aio);
        currentDataSector = sectorNum;
        asyncOpInProgress = true;// возводим флаг, чтобы функция получения дождалась конца операции.
    }    
}

// получение данных
void FileDB::getLastKeyData(uint32_t* buffer)
{
    memcpy( buffer, ((char*)dataSectorBuf)+rdOffs, rdSize );
   // .. тут далее обработка данных. ее мы опускаем
}

void FileDB::getLastKeyData_aio(uint32_t* buffer)
{    
    if( asyncOpInProgress ) // если было чтение с диска - дожидаемся конца операции.
    {
        while( (err_r = aio_error(&aio)) == EINPROGRESS ) 
        {
            cblist[0] = &aio;
            aio_suspend( cblist, 1, NULL );
        } 
        if (err_r == 0)
        {
            aio_return(&aio);                        
        }
//        вариант с тем, что не прочиталось игнорируем пока...в крайнем случае получим ранее прочитанные данные.
//        исходный буфер чтения-то не пересоздается...
        asyncOpInProgress = false; // сброс флага асинхронной операции
    }    
    memcpy( buffer, ((char*)dataSectorBuf)+rdOffs, rdSize );
   // .. тут далее обработка данных. ее мы опускаем
}
теперь если в программе использовать такой вариант, то все работает как часы:

Код: Выделить всё

        FileDB db = new FileDB(); //
        uint32_t* tmpbuf = uint32_t[2048]; // 2k*4 = 2 "сектора"
        ...
        for(...){
        ....
            db->read(currIdx);
            db->getLastKeyData(tmpbuf);
            recCnt = db->getLastKeyRecordCount();            
        .....
        }
        delete db;
        delete []tmpbuf;        
а если вот такой, то через 10-15 чтений система перегружается с крашем.

Код: Выделить всё

       	FileDB db = new FileDB(); //
        uint32_t* tmpbuf = uint32_t[2048]; // 2k*4 = 2 "сектора"
        ...
        for(...){
            db->read_aio(currIdx);
            db->getLastKeyData_aio(tmpbuf);
            recCnt = db->getLastKeyRecordCount();            
         ....
        }
        delete db;
        delete []tmpbuf;
Функция - функция потока обработки данных с большим циклом. Для каждого жесткого диска создается свой поток. При отладке использую в основном один поток (один диск).

из настроек. в boot/loader.conf включил aio_load="YES". С остальным решил разбираться позже (я все же новичок в UNIX-like системах. и пока не заработает с минимальными изменениями, стараюсь не трогать лишнего.) может еще чего-то нужно в обязательном порядке настраивать, чтоб AIO работал нормально? Или это я его не правильно использую?

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/



EugenOS
проходил мимо
Сообщения: 4
Зарегистрирован: 2015-10-30 16:44:14

Краш системы при read_aio

Непрочитанное сообщение EugenOS » 2015-11-07 9:31:07

как обычно. устранение последствий копипаста.