Работали ли вы с низкоуровневыми операциями дисковой подсистемы в C++?

«Работали ли вы с низкоуровневыми операциями дисковой подсистемы в C++?» — вопрос из категории Linux и ОС, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, в задачах, требующих высокой производительности ввода-вывода или работы с сырыми устройствами, использовал как POSIX API, так и специфичные для Linux системные вызовы.

Основные подходы и инструменты:

  1. Прямые системные вызовы (POSIX/Linux):

    • open, read, write, close, lseek. Важно проверять возвращаемые значения и обрабатывать errno.
    • Асинхронный I/O (Linux AIO): io_setup, io_submit, io_getevents для неблокирующих операций без использования потоков.
      #include <libaio.h>
      #include <fcntl.h>
      // ... инициализация контекста io_context_t, подготовка iocb
  2. Отображение файлов в память (mmap):

    • Идеально для работы с большими файлами или реализации shared memory. Операционная система сама заботится о подгрузке нужных страниц.
      
      #include <sys/mman.h>
      #include <sys/stat.h>
      #include <fcntl.h>

    int fd = open("large_file.bin", O_RDONLY); struct stat sb; fstat(fd, &sb); size_t file_size = sb.st_size;

    void mapped = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0); if (mapped == MAP_FAILED) { / handle error */ }

    // Работаем с данными как с массивом байт const char data = static_cast<const char>(mapped); // ... munmap(mapped, file_size); close(fd);

  3. Прямой ввод-вывод (O_DIRECT):

    • Флаг O_DIRECT при открытии файла позволяет обходить кэш страниц ОС. Это требует выровненных буферов и размеров операций (обычно по 512 байт). Используется в СУБД и специализированных системах хранения для полного контроля над кэшированием.
      int fd = open("datafile", O_RDWR | O_DIRECT);
      // Буфер должен быть выровнен, например, через posix_memalign
      void* buf;
      posix_memalign(&buf, 512, 4096); // Выравнивание по 512 байт
      read(fd, buf, 4096);
  4. Интерфейс sendfile:

    • Для эффективной пересылки данных из файла в сокет (zero-copy внутри ядра), что часто используется в веб-серверах.

Почему это важно: Выбор правильного метода (буферизированный I/O, mmap, O_DIRECT) напрямую влияет на производительность приложения, особенно под высокой нагрузкой.