Jeśli prędkość jest ważna i kompresja nie jest potrzebna, można zahaczyć syscall obwolut używanych przez tarużyciem LD_PRELOAD, aby zmienić targo obliczyć dla nas. Reimplementując kilka z tych funkcji w celu zaspokojenia naszych potrzeb (obliczając rozmiar potencjalnej wyjściowej danych smoły), jesteśmy w stanie wyeliminować wiele z nich readi writejest to wykonywane przy normalnym działaniu tar. To sprawia, że jest tarznacznie szybszy, ponieważ nie musi kontekstowo przełączać się w jądro tam iz powrotem tak blisko, a tylko statżądany plik / folder (pliki) wejściowy musi zostać odczytany z dysku zamiast z rzeczywistych danych pliku.
Poniższy kod zawiera implementacje close, readoraz writefunkcji POSIX. Makro OUT_FDkontroluje, którego deskryptora pliku będziemy tarużywać jako pliku wyjściowego. Obecnie jest ustawiony na standardowe wyjście.
readzostał zmieniony tak, aby zwracał wartość sukcesu countbajtów zamiast wypełnienia bufora danymi, biorąc pod uwagę, że rzeczywiste dane nie zostały odczytane, buf nie zawierałby prawidłowych danych do przekazania na kompresję, a zatem gdyby zastosowano kompresję, obliczilibyśmy niepoprawne rozmiar.
writezostał zmieniony, aby sumować countbajty wejściowe do zmiennej globalnej totali zwracać wartość sukcesu countbajtów tylko wtedy, gdy deskryptor pliku jest zgodny OUT_FD, w przeciwnym razie wywołuje pierwotne opakowanie pozyskane przez, dlsymaby wykonać wywołanie systemowe o tej samej nazwie.
closenadal wykonuje wszystkie oryginalne funkcje, ale jeśli deskryptor pliku pasuje do OUT_FD, wie, że tarpróbowano zapisać plik tar, więc totalnumer jest ostateczny i wypisuje go na standardowe wyjście.
#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);
}
Benchmark porównujący rozwiązanie, w którym dostęp do dysku odczytu i wszystkie wywołania systemowe normalnej operacji tar wykonywane są przeciwko LD_PRELOADrozwiązaniu.
$ 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
Powyższy kod, podstawowy skrypt do budowania powyższego jako biblioteka współdzielona oraz skrypt LD_PRELOADwykorzystujący go w „ technice” znajduje się w repozytorium:
https://github.com/G4Vi/tarsize
Kilka informacji na temat korzystania z LD_PRELOAD: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
--totalsopcją. Tak czy inaczej, jeśli zapełnisz dysk, możesz po prostu usunąć archiwum, imho. Aby sprawdzić wszystkie dostępne opcje, możesz przejśćtar --help.