lxz mint memóriaellenőrző: priceless

Egy felhasználó megkeresett levélben, hogy az lxz hibás kimenetet állít elő, nem lehet kibontani xz-vel. A legkülönfélébb masinákon és szálszámokkal teszteltem az utóbbi pár napban, hiba nélkül.

Ma végre eljutottak hozzám a felhasználótól a várva-várt tesztfile-ok, így bebizonyosodott, amit az elejétől sejtettem (csak nem mertem neki mondani, mert nem akartam pofára esni): hibás a RAM a gépében. Időnként az 5-ös bit (nullától számozva, tehát aminek decimális 32 az értéke) akkor is nullára áll egy-egy byte-ban, amikor egyébként 1-nek kellene lennie. Ez már a feltöltés során kiderült (szerencsére rögtön az elején küldött md5-öt), de aztán összebarkácsoltuk az átvitelt korrektül, és akkor már az (újra)tömörített tartalomból is látszott a hiba.

Ez volt a file transzfer-ben (

cmp -l

kimenet, balra a rossz oktálisban, jobbra a jó):


 283227144   0  40
 283227176  30  70
 283227528 101 141
 283227656 313 353
 283227688 230 270
 283227816   6  46
 283228040 131 171
 283228584  11  51
 283228712 231 271
 283228840 132 172
 283229192 221 261
 283229320  23  63
 283229352  15  55
 283229704 122 162
 283229864 125 165
 354137864 301 341
 354137896 113 153
 354138248   4  44
 354138280 303 343
 354138536 137 177
 354139304 216 256
 674379432  12  52

Alább pedig a sérült xz. A csillaggal jelölt, egymás melletti byte-ok legnagyobb valószínűség szerint a CRC32-t alkotják rendre. Bal oszlop ismét a rossz.


 145461304   1  41
 155177152 113 153
 159392109 320 117      *
 159392110 323 310      *
 159392111 364 366      *
 159392112 326  33      *
 200112670 333 373
 201337409 125 370      *
 201337410 332 233      *
 201337411 275 326      *
 201337412   4  42      *
 382345354 303 343
 382345386 301 341
 385896729 253 100      *
 385896730 175  34      *
 385896731 314 316      *
 385896732 167 210      *
 411525144 324 364
 526835461 316 356
 528510749  55 273      *
 528510750 173  40      *
 528510751 142 333      *
 528510752 232 245      *
 559793626 133 173
 562066989  35 154      *
 562066990  33 102      *
 562066991 152 335      *
 562066992 122 332      *
 629699772 203 243
 637568529 117 224      *
 637568530 350 323      *
 637568531  13  63      *
 637568532  67  45      *
 755998744  31  71
 755998776 106 146
1309855768 230 270

(Ezek mind teljes differenciák, 1.5 GB méretű állományokban.)

Emberek, vegyetek ECC RAM-ot (hozzá való alaplappal), vagy brand gépet. (Én az előző gépemnél két hétig vártam, mire megjött a Kingston ECC RAM az Egyesült Királyságból, de akkor is megéri.) Vagy legalább hajtsátok a memtest86-ot az új gépen jó sokáig.

Végezetül egy hasznos programra hívnám fel a figyelmet:

rdiff

. Lényegében aszinkron

rsync

. Nem annyira egyértelmű hasznáni, úgyhogy egy kis útbaigazítást megpróbálok adni. Az egyszerűség kedvéért most "hibás file"-ról és "jó file"-ról fogok beszélni.

  1. Az első lépésben a hibás file-ról készítünk egy lenyomatot. Az erre való parancs:
    rdiff -s -b BLOKKMÉRET -S HASH_MÉRET signature HIBÁS_FILE LENYOMAT_FILE

    Ez nagyjából annyit csinál, hogy minden BLOKKMÉRET byte-ra számol egy hash-t, a hash-t (darabonként) csonkolja annyi byte-ra, amennyit megadtunk a HASH_MÉRET-ben, az eredményt meg leteszi a LENYOMAT_FILE-ba.

    Minél nagyobb a blokkméret, annál kisebb a lenyomat (kevesebb hash-t kell tárolni), azonban utána a delta csak ugyanazzal a blokkmérettel tud dolgozni, tehát a delta is annál nagyobb lesz.

    Minél nagyobb a hash mérete (max. 16 byte, azaz 128 bit lehet), annál kisebb az esélye annak, hogy egy blokkról tévesen gondoljuk az rdiff algoritmus során, hogy megegyezik a hibás file-ban és a jó file-ban. Ugyanakkor a lenyomat-file mérete is egyenes arányban nő.

  2. Ezután a hibás file lenyomatát átvisszük oda, ahol megvan a jó file (e-mail, postagalamb), majd ott elkészítjük az rdiff deltát:
    rdiff -s delta LENYOMAT_FILE JÓ_FILE JAVÍTÓ_FILE

    Ha nagy blokkméretet választottunk az elején, akkor a javító-file is nagyobbacska lesz.

  3. A javítót visszavisszük az eredeti helyre, a hibás file mellé (e-mail, postagalamb), majd a hibás file-ból plusz a deltából előállítjuk a jó file egy példányát:
    rdiff -s patch HIBÁS_FILE JAVÍTÓ_FILE JAVÍTOTT_FILE

Hozzászólások

Szerintem a "korrupt" szonak mar megvan a helye a magyar nyelvben, helyette jobban mutat a "serult", "hibas".

zsync nem hasonlóan működik rdiff-hez?

beszéltél upstream-mel? nem tervezik átvenni a patch-edet?

zsync nem hasonlóan működik rdiff-hez?

Úgy látom, hogy igen. Mindkét esetben ezt a halmazkülönbséget akarjuk előállítani: { elérendő file blokkjai } \ { frissítendő file blokkjai }.

Az rdiff esetén a lenyomat arról a file-ról készül, amelyet frissíteni szeretnénk. A lenyomattal a helyes (elérendő) file-t vizsgáljuk végig, a szerveren; a delta képzésében a változó a kliens frissítendő file-jának a lenyomata.

A zsync-nél a lenyomat arról a file-ról készül, amelyet el akarunk érni. A lenyomattal a frissítendő file-t vizsgáljuk végig, a kliensen; a delta-kérésben a változó a szerver elérendő file-jának a lenyomata.

beszéltél upstream-mel? nem tervezik átvenni a patch-edet?

Ezt nem értem: milyen upstream-mel? És milyen patch-et?

Az xz-re gondolsz? Az lxz nem egy patch az xz-hez képest, hanem egy lbzip2 fork, amelyben a tömörítést (amelyet a libbz2 végez) kicseréltem az xz lib használatára. Az xz fejlesztőjének elég határozott elképzelése van arról, hogy az xz-ben mit fog csinálni több szál együtt. Biztosan nem azt, amit az lxz-ben :) Az xz-5.0.0 manual-jából:


       -T threads, --threads=threads
              Specify  the number of worker threads to use.  The actual number
              of threads can be less than threads if using more threads  would
              exceed the memory usage limit.

              Multithreaded  compression and decompression are not implemented
              yet, so this option has no effect for now.

              As of writing (2010-09-27), it hasn't been  decided  if  threads
              will  be  used  by default on multicore systems once support for
              threading has been implemented.  Comments are welcome.  The com-
              plicating  factor  is  that using many threads will increase the
              memory usage dramatically.  Note that if multithreading will  be
              the  default,  it  will probably be done so that single-threaded
              and multithreaded modes produce the same output, so  compression
              ratio  won't  be  significantly  affected  if  threading will be
              enabled by default.

tervezed bejuttatni debian-ba?

Nem tervezem :)

Az lbzip2 UNIX98-ra (SUSv2-re) íródott, teljes mértékben. A SUSv2 a C89-re épül. A libbz2 szintén C89-re épül. Tiszta sor.

A liblzma C99-re épül. A fordítási utasítása szerint (*) ráadásul úgy, hogy a C99-es nyelvi elemeket használja ugyan, azonban a C89-ről C99-re megváltozott viselkedésű függvényeket régi (C89-es) viselkedéssel kéri. Ezzel az egésszel mindössze két baj van.

  1. Az első SUS, ami a C99-re épül, a SUSv3. Az egész programot fel kellene húznom C99-re, illetve SUSv3-ra, ha rendes, terjeszthető programot akarnék csinálni az lxz-ből. Természetesen van a SUS-hoz upgrade guide, meg a C99-hez is Rationale, azonban erre se időm, se kedvem.
  2. A liblzma forrása a fentiek szerint egy köztes univerzumban él -- jelenleg nem létezik olyan ipari szabvány, amelynek megfelelne. Azzal, hogy a C89-ben és C99-ben is meglévő függvényeknél a C89-es viselkedést kéri, egyből nem felel meg (feltételezésem szerint!) a SUSv3-nak sem. Így lényegében a liblzma-t lehetetlen olyan programba beépíteni, amely a SUSv[34] bármelyikére íródik.

    Szerkesztés: ez magát a .so-t tekintve nem igazán fontos (legyen feltelepítve, aztán kész, működjön jól, olyan áron, amilyen áron tud), azonban a header-ök fontosak. Persze lehet, hogy a .h-kban (makrókban) nincs C99-es függvényhívás, csak C99-es nyelvi elemek (és akkor a liblzma forrásának "köztes univerzuma" élesen elhatárolódik a kliensprogramtól), azonban ezt nincs kedvem ellenőrizni.

A fentiek miatt van az is, hogy az lxz nulla dokumentációt tartalmaz -- ez egy hordozhatatlan program, mindenki fordítsa le, ahogy tudja. Amúgy is csak arra az időre szántam, amíg az xz-ben meg nem jelennek a szálak.

Egyébként létezik pxz, meg p7zip is; valamint (egy szintén másik LZMA implementációra építve) létezik plzip is. Ez a terület most forr ki.

(*) Lásd itt: [3] Someone with Solaris 8 or 9 could test if compiling XZ Utils works also with Sun Studio. It probably won't work out of the box, but passing CC="cc -xc99=all,no_lib" ac_cv_prog_cc_c99= to configure might make it work.

Az

-xc99=all,no_lib

pedig a fentit jelenti.