Bájtsorrend
A számítástechnikában, az angol endianness kifejezés (nem lévén rá általánosan használt magyar kifejezés, így talán a bájtsorrend a jó fordítás) jelzi azt a tulajdonságot, ami bizonyos adatok – többnyire kisebb egységek egymást követő sorozata – tárolási és/vagy hálózaton való továbbítási sorrendjét jellemzi. Tipikus esetekben ez a tulajdonság döntő fontosságú az integer értékeknek a számítógép memóriájában bájtonként való tárolása (egy memóriacímhez relatívan), illetve ezek hálózaton vagy más hordozón való továbbítása esetében. Ha speciálisan bájtokról beszélünk egy számítógép esetében, akkor az endian egyszerűen a bájtsorrendre, vagy (ritkábban) a byte sex-re vonatkozik. Az eredeti angol kifejezés az endianness egy utalás arra a háborúra, amely a két szemben álló csoport között zajlik, akik közül az egyik szerint a lágytojás nagyobb végét (big-endian), míg a másik csoport szerint a lágytojás kisebb végét (little-endian) kell feltörni. Erről Jonathan Swift ír a Gulliver utazásai című könyvében.
Bájtsorrend a számítógépeken
Úgy tűnik, nincs jelentős előnye egyik tárolási módnak sem a másikkal szemben, és mindkettőnek vannak követői a különböző számítógép architektúráknál. Ugyan mondhatjuk, hogy a világon jelenleg inkább a növekvő bájtsorrend van túlsúlyban, mivel az Intel x86 alapú processzorok (és azok klónjai), amit a legtöbb személyi számítógép és laptop használ, a növekvő bájtsorrendet – a „kicsi elöl”, little-endian – módszert alkalmazzák. Ez a gyakran emlegetett „Intel formátum”. A hálózatok viszont általában „nagy az elején” tulajdonságú, magyarul csökkenő bájtsorrendű számokat használnak a csomagok címeiben; ez történetileg alakult így, mivel ez a megoldás engedte meg a telefonszámok alapján történő útvonal-irányítást (routing-ot). A Motorola processzorok általában a csökkenő bájtsorrendű – „nagy az elején”, big-endian – megoldást használják, míg az ARM (és több más modern architektúra is) rendelkezik azzal a képességgel, hogy átkapcsolható benne a bájtsorrend, hogy nagyobb teljesítményt nyújthasson például hálózati eszközökben is.
Általában egy bájt tekinthető elemi egységnek vagy legalsóbb szintnek a tárolás szempontjából vagy az adatátviteli protokollok területén. Ezért az egy bájtos adatok sorozata (például ASCII kódolású szöveg, UTF-8-as, az ISO 8859 kódolás valamelyike) általában nem érintett a bájtsorrend okozta problémák szempontjából. Másrészről, a szövegek változó-hosszúságú kódolása, amely a bájtot használja egységként, mint az UTF-8, már tekinthető úgy, mint amiben „beépített” bájtsorrend problémák is lehetnek, bár csaknem minden általánosan használt kódolási eljárás tervezésekor erre valamilyen szinten figyelmet fordítottak. Bár, a Unicode stringek UTF-16 vagy UTF-32 szerinti kódolása érintett a bájtsorrend szempontjából, mivel minden kód egységet két vagy négy bájt képvisel.
Logikai és aritmetikai leírás
- Megjegyzés: a következő fejezetben minden szám
hexadecimális
formában szerepel.
Nagy az elején – Big-endian
Amikor (számos) számítógép 32 bites egész értéket, (ami legyen esetünkben 4A3B2C1D
, a 100
címtől kezdve), tárol a memóriájában, ami 1 bájtos elemi tárolókból, 1 bájtonként növekvő címekkel rendelkezik, akkor a tárolást a következők szerint végzi:
100 | 101 | 102 | 103 | ||
… | 4A | 3B | 2C | 1D | … |
Ebben az esetben, a legnagyobb helyiértékű bájt – angolul most significant byte, rövidítve MSB -, ami esetünkben 4A
a memóriában a legalacsonyabb címen van tárolva, míg az eggyel kisebb helyiértékű (3B
) a következő címen, és így tovább.
A továbbiakban lépjünk tovább az elemi tárolók méreteinek vonatkozásában: tegyük fel, hogy továbbra is 32 bites értéket kell tárolni, de most a tárolás elemi egységének 2 bájtot tekintsünk, a memóriacím 2 bájtonként 1-gyel nő. Azonos példánál maradva (4A3B2C1D
értéket a 100
-as címtől kezdve), a 100
-tól 101
-ig terjedő címtartományban kell tárolni, ahogyan azt a következő példa mutatja:
100 | 101 | ||||
… | 3B | 4A | 1D | 2C | … |
A legnagyobb helyiértékű adat a példában most 4A3B
, amit a 2C1D
érték követ a 101
-es címen.
A harmadik példa célja: vizsgálni a különbséget, amit az elemi tároló elem és a tárolási lépés hosszának változása okozhat. Ebben a példában is 32 bites egészet kell tárolni a memóriában, 2 bájtos elemi tárolóelem hossz, és bájtonkénti címnövelés esetén (tehát a memóriacím 2 bájtonként 2-vel nő):
100 | 102 | ||||
… | 3B | 4A | 1D | 2C | … |
A legnagyobb helyiértékű adat most is 4A3B
a példában, amit a 2C1D
érték követ, de most a 102
címen.
Azokat az architektúrákat, amelyek a fenti szabályokat követik, nevezik „nagy az elején” vagy angolul big-endian bájtsorrendűeknek (az angol end jelen esetben a kezdő véget jelenti, tehát a memorizáláshoz: a nagy vég lesz az első helyen) ide tartoznak a Motorola 68000, SPARC, PowerPC (az Intel-re való áttérés előtt az Apple Macintosh processzora volt), és a System/370.
Más számítógépek a 4A3B2C1D
értéket a következő bájtsorrenddel tárolják:
Kicsi az elején – Little-endian
100 | 101 | 102 | 103 | ||
… | 1D | 2C | 3B | 4A | … |
Így, a legkisebb helyiértékű byte (least significant byte, rövidítve LSB) az első, és ez példánkban 1D
.
100 | 101 | 102 | 103 | ||
… | 1D | 2C | 3B | 4A | … |
A legkisebb helyiértékű adathoz most a 2C1D
érték tartozik a példánkban, ezt követi a 4A3B
a 101
-es címen.
100 | 101 | ||||
… | 1D | 2C | 3B | 4A | … |
A legkisebb helyiértékű adat még mindig a 2C1D
a példánkban, amit a 4A3B
érték követ a 102
címen.
A fenti tárolási módot követő architektúrákat nevezik „kicsi az elején” vagy angol kifejezéssel little-endian (mnemonic: „little end in” – the little end goes in first, a kis vég kerül előre) bájtsorrendűeknek, ide tartoznak többek között a MOS Technology 6502, DEC VAX, és legtöbbet emlegetett Intel x86 alapú processzor család tagjai, ideértve az Intel Pentium alapú személyi számítógép és laptop processzorokat is.
Kicsit érthetőbb közelítés szerint, a bájtsorrend nem a vég értékét határozza meg, hanem inkább azt, hogy melyik vég kerül hová.
Kettős bájtsorrend – Bi-endianness
Egyes architektúrák esetében beállítható vagy egyik, vagy másik bájtsorrend; ide tartoznak az ARM, PowerPC (kivéve a PPC970/G5), DEC Alpha, MIPS, PA-RISC és az IA-64. A bi-endian kifejezés a hardverre nézve azt jelenti, hogy számítások végzésekor vagy adattovábbításkor a két lehetőség közül bármelyiket tudja használni (beállítható, hogy éppen melyikre van szükség). A legtöbb architektúra szoftveresen kapcsolható át a kiválasztott bájtsorrend módra/formára (többnyire a gép indításakor); bár léteznek olyan architektúrák amelyeknél az alapértelmezett bájtsorrendet hardveresen állítják be (alaplapon), így szoftveresen nem is lehet átkapcsolni (például a DEC Alpha, amelyik „nagy az elején” módban működik a Cray T3E-ben).
Középső az elején – middle-endianness
Bizonyos architektúrák, amelyeket középső az elején vagy middle-endian-nak neveznek (vagy néha kevert bájtsorrend vagy mixed-endian), az előzőeknél sokkal bonyolultabb tárolási módot használnak.
Ennek bemutatásához ismét a megszokott példához fordulunk: tegyük fel, hogy a 32 bites értéket most szószervezésű (egy szó 2 bájt) egység mellett kell tárolni a memóriában. Ez a memória 2 bájtos elemi egységeket képes kezelni, a 2 bájtonkénti címnövekedés pedig 1.
100 | 101 | ||||
… | 4A | 3B | 2C | 1D | … |
vagy egy másik lehetőség:
100 | 101 | ||||
… | 2C | 1D | 4A | 3B | … |
A fenti példával kapcsolatosan két megjegyzést kell tenni:
- az első példa a „nagy az elején” szerinti lehetséges megoldást mutatja, míg a másik a „kicsi az elején” szerintit.
- A valódi megoldások ennél bonyolultabbak, ui. a bájtok cseréjére sokkal több lehetőség van.
Az a formátum, amiben a VAX és az ARM processzorok a kétszeres pontosságú lebegőpontos számokat tárolják, kevert bájtsorrendű. A 32 bites szavak „középső az elején” formátumban tárolódnak a PDP-11-ben, ezért használják a pdp-endian kifejezést is, ha erre a formátumra akarnak utalni.
A bájtsorrend bit szintű értelmezése
A bájtsorrend koncepció kevéssé fontos a bitek bájtban elfoglalt helyét illetően, mivel az architektúrák általában nem támogatják egyes bitek bájton belüli direkt címzését. A bájton belüli bitek, bitcsoportok címzése helyett a logikai műveletek adnak jól definiált eredményeket, így mondhatjuk, hogy a bitek szempontjából az architektúrák bájtsorrend függetlenek.
Sajnálatos módon azonban, a bájtszintű „bájtsorrend” mégiscsak okozhat problémákat: ha a tárolandó bit sorozat nem pontosan egy bájt hosszúságú, akkor az egy bájt tárolásával kapcsolatos bájtsorrend megfontolások mégsem hagyhatók figyelmen kívül. Egy tárolt bájt legszignifikánsabb bitjének az értelmezése elvezet a bit szintű „bájtsorrend” kérdéseihez. Tovább bonyolítja a helyzetet, hogy egyes programnyelvek, például a C megengedi a rekord adatszerkezetben a bitmező használatát. Ebben az esetben már fel kell tételeznünk bit szintű „bájtsorrendet” (szerencsére ez nem architektúra szinten, hanem fordítóprogram szinten jelentkezik, azonban a hordozhatóság szempontjából egyáltalán nem elhanyagolható).
Ha egy file beolvasása a memóriából rekordonként történik, vagy az írása úgy történik, mint egy bitekből álló nagy blokk, akkor felmerül a lehetősége annak, hogy a rekord mezői nem lesznek megfelelő bájt sorrendűek. Különböző eljáráshívásokkal megvalósított konverziókkal lehet biztosítani, hogy a rekordok bájtszintű „bájtsorrendje” megfelelő legyen. Hasonló meggondolásokat kell tenni, ha a fent említett rekordokat hálózaton keresztül adatcsomagokban továbbítják. Ekkor ugyanis lehetséges, hogy a bitcsoportok határai nem esnek egybe byte határokkal, így a küldött csomagok értelmezése szinte megjósolhatatlan lehet.
Hordozhatósági problémák
A bájtsorrend alapvetően érinti a szoftverek hordozhatóságát. Például, egy bináris formában tárolt adat értelmezése egy megfelelő bitmaszk használatával feltétlenül érintett a bájtsorrend szempontjából, mivel a különböző bájtsorrend szerint tárolt adatok más és más eredményeket adnak, a maszk értékétől függetlenül.
Egy program által, közös formátumba felírt bináris adatok ugyancsak érintettek lehetnek: például ha az adatokat a BMP bitmap formátumban kell felírni, („elől a kicsi” egészek), és ha az adat tárolása „elől a nagy” megoldású, akkor az adatok sérülhetnek, és nem illeszthetők az adott formátumhoz.
Azoknak a szoftvereknek, amelyeknek információkat kell megosztaniuk különböző eltérő bájtsorrendű hálózati csomópontok között, általában két lehetséges stratégia közül választhatnak. Vagy kiválasztanak egy adott bájtsorrendet és csak azt használják, vagy valamilyen formában közlik, hogy milyen bájtsorrend az adott adat. Az adott bájtsorrend kezelését mindkét esetben a fogadónak kell megoldania. Mindkét közelítési módnak vannak előnyei. Egyfelől csak egyféle bájtsorrendet kell dekódolni, másfelől, a többféle bájtsorrend megengedése lehetővé teszi, hogy az adott architektúra – szoftveres támogatás nélkül – képes kezelni az adatokat (ez hatékonyság növekedését jelenthet). A legtöbb Internet szabvány az első megközelítést alkalmazza: meghatározza, hogy a „nagy az elején” bájtsorrend a kötelező. Több szállító azt a bájtsorrendet használja, amit az adott platform biztosít, és vannak alkalmazások, mint például az X11, amelyek a második megközelítést alkalmazzák.
Az UTF-16 kódokat írhatjuk akár „nagy az elején” vagy „kicsi az elején” sorrend szerint. Az ábrázolási mód megengedi az un. bájtsorrend jelzést (Byte Order Mark – BOM) egy 2 bájtos string, ami jelzi a használt bájtsorrendet. Hasonló 4 bájtos bájtsorrend jelzést használ a ritkán alkalmazott UTF-32.
Programozási példa a probléma bemutatására
A következő, C nyelven írt alkalmazás jól mutatja, hogy milyen veszélyeket rejt az eltérő bájtsorrend:
#include <stdio.h> #include <string.h> int main(void) { FILE *fp; /* Adatstruktura a peldahoz*/ struct { char one[4]; int two; char three[4]; } data; /* Az adatstruktura adatokkal valo feltoltese */ strcpy(data.one, "foo"); data.two = 0x01234567; strcpy(data.three, "bar"); /* Iras a file-ba */ fp = fopen("output", "wb"); if (fp != NULL) { fwrite(&data, sizeof data, 1, fp); fclose(fp); } }
A kódot egy i386 processzort használó gépen fordították le, majd futtatták, utána egy Solaris SPARC64 gépen is lefordították és futtatták, mégis a kinyomtatott eredmények eltérőek (a nyomtatások a hexdump programmal készültek).
i386 $ hexdump -C output 00000000 66 6f 6f 00 67 45 23 01 62 61 72 00 |foo.gE#.bar.| 0000000c
sparc64 $ hexdump -C output 00000000 66 6f 6f 00 01 23 45 67 62 61 72 00 |foo..#Egbar.| 0000000c
Bájtsorrend a kommunikációban
Általában, az ún. NUXI probléma (ismert még, mint endian problem) a számítógépek közötti adtátvitel során fellépő, az eltérő bájtsorrendek okozta jelenségekre utal. A példa a „UNIX” string két 16 bájtos egészként való tárolása és hálózati átvitele következtében esetleg előforduló jelenséget mutatja, ugyanis lehetséges, a vevő oldalon a „NUXI” lesz olvasható, ha a két gép bájtsorrendje eltérő. A problémát először – a számítástechnikai legenda szerint – a Unix korai változatának PDP-11-ről (egy „középső az elején” bájtsorrend architektúra) egy IBM Series 1 minicomputerre való portolásakor fedezték fel. Az IBM gép „nagy az elején” bájtsorrendű volt, és a rendszer indítása után a „UNIX” helyett mindenütt a „NUXI” szöveg jelent meg.
Az Internet Protocol definiál egy szabványos „nagy az elején”, (big-endian) hálózati bájtsorrendet. Ezt a bájtsorrendet kell használni minden csomag fejlécében és több magas szintű protokollban és fájlformátumban, amit IP szerinti kezelésre terveznek.
A Berkeley sockets API definiál egy eljárás halmazt a 16 és 32 bites egészek konverziójára a hálózati küldés és fogadás esetére, a hálózati bájtsorrend biztosítására: a htonl és a htons eljárások a 32 bites („long”) és a 16 bites („short”) értékeket konvertálják a hoszt-hálózat irányba, míg a ntohl és a ntohs eljárások a hálózat-hoszt irányú konverzókat végzik.
Azok a berendezések, amelyek soros kommunikáció megvalósítására szolgálnak, bit szintű „bájtsorrend” érzékenyek: egy bájt bitjeit küldhetik akár „nagy az elején” (a legszignifikánsabb bit először), akár „kicsi az elején” (a legkevésbé szignifikáns bit először) rendszer szerint.
Ez a probléma a nagyon alacsony szintű protokolloknál jelentkezik, pl. az OSI modell adatkapcsolati rétegében.
Bájtsorrend a dátumformátumoknál
A bájtsorrend egyszerűen bemutatható a különböző országok által használt dátumformátumokkal.
Az Amerikai Egyesült Államokban használt adatformátum a hónap; nap; év (pl.: „May 24th, 2006”, „5/24/2006”). Ez a középső-elöl (middle-endian) sorrendet követi.
A legtöbb Óceániai, Dél-Amerikai és Európai ország (kivéve Svédországot és Magyarországot, ahol az ISO 8601 elterjedt), dátumformátum nap; hónap; év (pl.: „24th May, 2006”, „24/5/2006”, „24/5-2006”, „24.5.06”). Ez például egy kicsi az elején (little-endian) sorrend.
Több ország, többek között Kína és Japán, az ISO 8601 nemzetközi szabvány szerinti sorrendet használja a dátumoknál: év; hónap; nap (Pl.: „2006 May 24.”, vagy, a leggyakoribb „2006-05-24”). Ez pedig a nagy az elején (big-endian) sorrend.
Az ISO 8601 elrendezési sémájára azt a javaslatot adja, hogy annak olyannak kell lennie, hogy azokat egy dátumra vonatkozó számítógépes rendezés lexikografikus sorrendbe, vagy szótár sorrendbe rendezze. Ez azt jelenti, hogy a rendezési algoritmus nem kezeli másként egy szövegben előforduló számot, mint magát a szöveg nem numerikus karaktereitet, így a dátumok rendezése időrendi sorrendet kell, hogy adjon. Meg kell jegyezni, hogy ennek működéséhez alapvetően szükséges, hogy az évet négy számjeggyel, a hónapot és a napot két számjeggyel ábrázolják. Ezért az egyszámjegyű napokat és hónapokat ki kell egészíteni egy vezető nullával a következők szerint: ‘01′, ‘02′, …, ‘09′.
Bájtsorrend a címzéseknél
A nyugaton használt postai címeket nagyrészt a „kicsi az elején” rendszerben írják, a legkisebb összetevővel kezdik, (a címzett neve), majd következik a ház száma, az utca neve, a város neve, a régió, majd az ország. Néhány ázsiai ország (például Japán) a „kicsi az elején” rendszer helyett a „nagy az elején” rendszert használja: a cím az országgal kezdődik, majd a régió, város, és az elején a címzett neve. Mi a kevert bájtsorrendet használjuk: címzett neve, város, utca, házszám, ország.
Az Internet domain név rendszere és az e-mail címzés „kicsi az elején” rendszerű, követve a nyugati postai címzési rendszert. A UNIX (és más korszerű operációs rendszer) fájlrendszerének elérési útja (pathname) viszont „nagy az elején” rendszerű, a legmagasabb szintű könyvtárnév van legelöl. Az URL-ek, amelyek a két jelölési módot kombinálják, kevert bájtsorrendűek, egy „kicsi az elején” bájtsorrendű host nevet követ egy „nagy az elején” bájtsorrendű elérési út, például:
- protocol://organization.region.country/department/subdepartment/person
Háttér, érdekességek, etimológia
A „nagy-elöl” módon ábrázolt számok könnyen olvashatók, ha programhibákat keresünk (debug). Néhányan azt gondolják, hogy ezek az emberek kevéssé intuitívak, mivel a „legjellemzőbb” bájt van a „kisebb” címeken. Mások úgy gondolják, hogy ez kevéssé zavaró, mivel az ábrázolási mód hasonló, mint ahogyan a normál szöveget a számítógépben tárolják, éppen úgy mint egy nem-számítógépes szövegben (lásd később).
A „kicsi-elöl” – little-endian formájú számok esetében némi előnyt jelent a számítástechnikában, hogy a memóriában tárolt változó esetében nem kell a szám teljes hosszát elolvasni, illetve kezelni. Például, egy 32 bites változó a memóriában, mint a 00 00 00 4A azonos címről olvasható, mint egy 8 bites szám (4A), vagy 16 bites (00 4A), vagy akár 32 bites (00 00 00 4A) és így tovább, a hossz nem korlátos, és nincsen hatása az olvasott értékre. A „nagy az elején” megoldásra a fentiek már nem igazak; ekkor a legjellemzőbb bájtok függenek a relatív elhelyezéstől. Például a 00 00 00 4A adhat 00 eredményt, ha 8 bitesként olvassuk ugyanarról a címről. Egy „nagy az elején” formájú szám minden esetben „torzulhat”, ha címzésnél nem figyelünk a hossz különbségből adódó korrekcióra.
Az, hogy egyes emberek melyik ábrázolási módot kedvelik, az nagyobbrészt azon múlik, melyik ábrázolási módot tanulták meg először, illetve attól, hogy az illető mentális beállítódása milyen. Kifejezetten azoknak az embereknek az esetében, akiknek alacsony szintűek a számítógépes ismeretei, valamint a legtöbb beszélt nyelv – különösen a 100-nál nagyobb számok esetében – a „nagy az elején” módot használják. Magyarul például az mondjuk, hogy „háromszáz-huszonnégy”, de nem mondjuk, hogy „négy és húsz és háromszáz”. (A magyar példa kicsit erőltetettnek tűnhet, mivel mi amúgy sem teszünk kötőszót számok közé, de az angolban már más a helyzet: „three hundred twenty-four”, nem pedig „four-and-twenty and three hundred”.) [1] [2]. Meg kell azonban jegyezni, ellenpélda fentiekre a német és a holland nyelv, amelyek „kicsi az elején” rendszert használnak a számoknál 21 és 99 között, és kevert „bájtsorrendet” a nagyobb számoknál (például vierundzwanzig/vierentwintig (24, szó szerint négy-és-húsz), és hundertvierundzwanzig (124, szó szerint száz négy-és-húsz).
A hindu-arab számrendszert használják világszerte, és ebben a legjellemzőbb szám mindig balra van a kevésbé jellemzőnél (a legnagyobb nagyságrend van bal oldalon legelöl). Mivel balról jobbra írunk, ez a rendszer ezért „nagy az elején” módszer szerinti. Ennek számunkra különösebb jelentősége nincsen, azonban van néhány nyelv, ahol a számok olvasási sorrendje ellentétes az írásának sorrendjével, mint például a héber. Ekkor ugyanis meg kell szakítani a szöveg írási irányát (jobbról balra) és a számokat ellenkező irányban (balról jobbra) kell írni. A németül vagy hollandul beszélők, ennek ellenére mégsem írják a kisebb számokat jobbról balra.
A „nagy az elején” vagy „kicsi az elején” használata koncepcionális kérdés, valójában egy mindig egy flame war tárgya. Mindkét oldal elegendő érvvel rendelkezik a maga igaza mellett, ahogyan a Jonathan Swift szatirikus regényében, ahol Liliput és Blefuscu lakói két táborra szakadtak, attól függően, hogy ki melyik végét töri fel a főtt tojásnak.
Mindazonáltal, a „kicsi elöl” sorrend használatos az úgynevezett „visszafelé szótáraknál”, ismertebb néven a kiejtési szótáraknál, mint például a Cantonese jyt jɐm dziŋ duk dzi wɐi (ISBN 962-948-509-5) ami a z „a, ba, da, dza,…” kifejezésekkel kezdődik, és a „…tyt, tsyt, m̩, ŋ̩” szavakkal végződik.
Külső, angol nyelvű linkek
- David Cary’s Endian FAQ – beleértve a On Holy Wars and a Plea for Peace Danny Cohen, 1980. április 1-jei cikkét
- Kalid’s Endian issues page at Princeton C programozási nyelvi példákkal.
- big-endian, little-endian, etc., at the Jargon File.
- [3] Eredeti szöveg a „Gulliver Utazásai”, első rész, IV. fejezet, ahol az endianitásról ír.
- White Paper: Endianness or Where is Byte 0?.
Informatikai portál • összefoglaló, színes tartalomajánló lap