Быстрое копирование файлов с прогрессом - PullRequest
2 голосов
/ 09 октября 2010

Я пишу SDL-приложение для Linux, которое работает с консоли (без X-сервера).У меня есть одна функция - механизм копирования файлов, который копирует определенные файлы с жесткого диска на USB-накопитель и отображает ход выполнения этой копии в пользовательском интерфейсе.Для этого я использую простой цикл while и копирую файл кусками по 8 КБ, чтобы получить прогресс копирования.Проблема в том, что это медленно.Я получаю копию файла размером 100 МБ почти за 10 минут, что недопустимо.

Как я могу реализовать более быстрое копирование файла?Я думал о каком-то асинхронном API, который считывал бы файл с жесткого диска в буфер и сохранял данные на USB в отдельном потоке, но я не знаю, должен ли я реализовать это сам, потому что это не выглядит легкой задачей.Может быть, вы знаете какой-нибудь C ++ API / библиотеку, которая может для меня это сделать?Или, может быть, какой-то другой, лучший метод?

Ответы [ 2 ]

4 голосов
/ 09 октября 2010

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

Вы также должны использовать больший размер буфера, чем 8 КБ. Поэкспериментируйте, но я думаю, вы получите более быстрые результаты с большими размерами буфера (например, в диапазоне 64-128 КБ).

Итак, это может выглядеть примерно так:

#define BUFSIZE (64*1024)

volatile off_t progress, max_progress;

void *thread_proc(void *arg)
{
    // Error checking omitted for expository purposes
    char buffer[BUFSIZE];
    int in = open("source_file", O_RDONLY);
    int out = open("destination_file", O_WRONLY | O_CREAT | O_TRUNC);

    // Get the input file size
    struct stat st;
    fstat(in, &st);

    progress = 0;
    max_progress = st.st_size;

    ssize_t bytes_read;
    while((bytes_read = read(in, buffer, BUFSIZE)) > 0)
    {
        write(out, buffer, BUFSIZE);
        progress += bytes_read;
    }

    // copy is done, or an error occurred
    close(in);
    close(out);

    return 0;
}

void start_file_copy()
{
    pthread_t t;
    pthread_create(&t, NULL, &thread_proc, 0);
}

// In your UI thread's repaint handler, use the values of progress and
// max_progress

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

Для систем Windows вы должны использовать CopyFileEx, который одновременно эффективен и обеспечивает выполнение процедуры обратного вызова.

4 голосов
/ 09 октября 2010

Пусть ОС сделает всю работу:

  1. Отображение файла в память: mmap , значительно ускорит процесс чтения.
  2. Сохраните его в файл, используя msync .
...