Если скорость важна, а сжатие не требуется, вы можете перехватить использованные оболочки syscall, tar
используя LD_PRELOAD
изменения, tar
чтобы рассчитать их для нас. Реализовав некоторые из этих функций в соответствии с нашими потребностями (вычисляя размер потенциальных выходных данных tar), мы можем устранить многие из них, read
и write
это выполняется при нормальной работе tar
. Это делает tar
намного быстрее, так как ему не нужно переключаться между ядрами назад и вперед в ядре, и только stat
с запрошенного входного файла / папок нужно читать с диска вместо фактических данных файла.
Ниже код включает в себя варианты реализации close
, read
и write
функции POSIX. Макрос OUT_FD
определяет, какой дескриптор файла мы ожидаем tar
использовать в качестве выходного файла. В настоящее время установлено значение stdout.
read
был изменен, чтобы просто возвращать значение успешного count
байта вместо заполнения buf данными, учитывая, что фактические данные не были прочитаны, buf не будет содержать действительных данных для передачи на сжатие, и, таким образом, если сжатие использовалось, мы вычислили бы неправильное размер.
write
был изменен, чтобы суммировать входные count
байты в глобальной переменной total
и возвращать значение успеха count
байтов, только если дескриптор файла совпадает OUT_FD
, в противном случае он вызывает исходную оболочку, полученную через, dlsym
для выполнения системного вызова с тем же именем.
close
все еще выполняет все свои первоначальные функции, но если дескриптор файла совпадает с OUT_FD, он знает, что tar
завершена попытка записи файла tar, поэтому total
число является окончательным и выводит его на стандартный вывод.
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <errno.h>
#include <dlfcn.h>
#include <string.h>
#define OUT_FD 1
uint64_t total = 0;
ssize_t (*original_write)(int, const void *, size_t) = NULL;
int (*original_close)(int) = NULL;
void print_total(void)
{
printf("%" PRIu64 "\n", total);
}
int close(int fd)
{
if(! original_close)
{
original_close = dlsym(RTLD_NEXT, "close");
}
if(fd == OUT_FD)
{
print_total();
}
return original_close(fd);
}
ssize_t read(int fd, void *buf, size_t count)
{
return count;
}
ssize_t write(int fd, const void *buf, size_t count)
{
if(!original_write)
{
original_write = dlsym(RTLD_NEXT, "write");
}
if(fd == OUT_FD)
{
total += count;
return count;
}
return original_write(fd, buf, count);
}
Сравнительный анализ решения, при котором доступ к диску чтения и все системные вызовы обычной операции tar выполняются с LD_PRELOAD
решением.
$ time tar -c /media/storage/music/Macintosh\ Plus-\ Floral\ Shoppe\ \(2011\)\ \[Flac\]/ | wc -c
332308480
real 0m0.457s
user 0m0.064s
sys 0m0.772s
tarsize$ time ./tarsize.sh -c /media/storage/music/Macintosh\ Plus-\ Floral\ Shoppe\ \(2011\)\ \[Flac\]/
332308480
real 0m0.016s
user 0m0.004s
sys 0m0.008s
Приведенный выше код, базовый сценарий сборки для сборки вышеупомянутого в виде разделяемой библиотеки и сценарий с « LD_PRELOAD
техникой» с его использованием предоставлен в репозитории:
https://github.com/G4Vi/tarsize.
Некоторая информация об использовании LD_PRELOAD: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
--totals
опцией. В любом случае, если вы заполните диск, вы можете просто удалить архив, imho. Чтобы проверить все доступные варианты, вы можете пройтиtar --help
.