Если скорость важна, а сжатие не требуется, вы можете перехватить использованные оболочки 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.