Is there a system command, in Linux, that reports the endianness?
What’s wrong with the od method? It’s simple and works everywhere. It’s what I thought of before reading the body of your question.
@Gilles — nothing really, feels a little like a hack (at least to me). True it would appear to be portable on other systems such as Solaris + AIX, but it seemed like a system’s Endianness should be a little more explicitly determined like 32-bit vs. 64-bit, so I was a little surprised that it wasn’t. The newer lscpu method is more what I would come to expect.
Endianness is in practice easier to determine than word size, because you’ll have a hard time finding platforms that aren’t either little-endian or big-endian (at least for integers, floats are another matter) whereas there are plenty of mixes between 32-bit and 64-bit (CPU, kernel, userland, a given process).
@Gilles — yes my view of the world is probably slighted since I primarily grew up with either Solaris or Linux. Not so much beyond that.
the od approach should work on most open systems, not only linux, which would be the case with using lscpu . So what is «best» depends on the circumstances.
5 Answers 5
lscpu
The lscpu command shows (among other things):
Systems this is known to work on
- CentOS 6
- Ubuntu (12.04, 12.10, 13.04, 13.10, 14.04)
- Fedora (17,18,19)
- ArchLinux 2012+
- Linux Mint Debian (therefore assuming Debian testing as well).
Systems this is known to not work on
Why the apparent differences across distros?
After much digging I found out why. It looks like version util-linux version 2.19 was the first version that included the feature where lscpu shows you the output reporting your system’s Endianness.
As a test I compiled both version 2.18 and 2.19 on my Fedora 14 system and the output below shows the differences:
$ util-linux-ng-2.18/sys-utils/lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit CPU(s): 4 Thread(s) per core: 2 Core(s) per socket: 2 CPU socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 37 Stepping: 5 CPU MHz: 1199.000 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K NUMA node0 CPU(s): 0-3
$ util-linux-2.19/sys-utils/lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 2 Core(s) per socket: 2 CPU socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 37 Stepping: 5 CPU MHz: 2667.000 BogoMIPS: 5320.02 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K NUMA node0 CPU(s): 0-3
The above versions were downloaded from the kernel.org website.
Linux little endian or big endian
Понятие Byte order, или порядок следования байт (или endianness) относится к очередности размещения в памяти многобайтовых величин (обычно целых чисел и чисел с плавающей точкой; хотя числа с плавающей точкой не используются в Linux kernel, они могут работать в пользовательских программах), как это поддерживается аппаратурой процессора. Соответственно бывает 2 варианта порядка байт — big endian и little endian. Big endian это такой порядок байт, когда самый значимый по значению байт числа (most significant byte) сохранен в памяти первым по порядку (т. е. у него самый маленький абсолютный адрес байта, в сравнении с остальными байтами числа). Соответственно little endian это противоположный порядок байт, когда наименее значимый байт сохраняется в памяти первым.
Чтобы было понятнее, рассмотрим пример. 4-байтное целое число 0x01020304 будет сохранено в памяти системы big endian следующим образом:
Байт0 | Байт1 | Байт2 | Байт3 |
0x01 | 0x02 | 0x03 | 0x04 |
Big endian всегда используется для так называемого сетевого порядка байт (network byte order), который применяется при кодировании адресов в сетевых протоколах.
Та же самая величина, которая будет храниться в памяти системы little endian, разместится в противоположном порядке:
Байт0 | Байт1 | Байт2 | Байт3 |
0x04 | 0x03 | 0x02 | 0x01 |
Обычно при программировании можно не обращать внимания на endianness, то есть не важно, как процессор сохранит байты чисел в системе — big endian или little endian; ядро CPU просто загружает данные из памяти и сохраняет данные в память, и представляет данные в Вашей программе уже в правильном виде. Однако, когда нужно обмениваться данными с другой системой, обе системы должны учитывать формат хранения данных в памяти (endianness).
Linux kernel может быть либо big endian, либо little endian, в зависимости от архитектуры, в расчете на которую kernel скомпилировано. Ниже в таблице показан endianness для различных типов архитектур процессоров и протоколов.
Примечание: процессор ARM может быть либо с архитектурой big endian, либо little endian, в зависимости от типа применяемого чипа, однако чаще всего это big endian. Архитектура PowerPC может быть сконфигурирована для работы либо в режиме big endian, либо little endian, но в Linux используется только big endian.
[Почему следует беспокоиться об endianness]
Как уже упоминалось, порядок байт на низком уровне в системе полностью прозрачен для программиста (про endianness можно часто забыть). Однако могут быть проблемы, когда происходит обмен данными с другой системой, поскольку эта другая система может ожидать появления данных в блоке памяти в противоположном порядке.
Например, если заранее нельзя предсказать тип системы на каком-то дальнем окончании сетевого соединения, сетевые протоколы должны заранее определить порядок байт, используемый для хранения многобайтных величин в заголовках пакетов. В этом случае порядок байт называют сетевым порядком байт (network byte order), и для протокола TCP/IP это будет big endian. Таким образом, отправляющая пакеты система конвертирует данные из локального порядка хранения байт в сетевой. После этого принимающая система преобразует данные из сетевого порядка байт в локальный. На практике, когда есть жесткие требования к быстродействию и заранее известно, что локальный порядок байт такой же, как сетевой, операция конверсии отбрасывается в целях оптимизации.
Другой хороший пример — протокол USB, у которого порядок байт для многобайтных величин little endian.
[Как программно определить endianness]
Можно написать простую программу, которая будет определять порядок байт в имеющейся системе.
union < int i; char c[sizeof(int)]; > foo; foo.i = 1;
if (foo.c[0] == 1) printf("Little endian\n");
Строки 1..4 определяют переменную foo, к которой можно обращаться либо как к числу типа int (тип int почти всегда состоит из нескольких байт) или как к массиву символов characters. На строке 6 переменная инициализируется целым значением 1, так что как минимум один байт в многобайтном числе станет равен 1 (наименее значащий байт), а все остальные значащие байты будут нулями. Если байт 0 массива наименее значимый, то он станет равным 1, и это означает, что система little endian. Если байт 0 массива самый значимый байт, то он будет нулем, и значит система big endian.
Kernel определяет различные типы переменных и макросы для обработки значений, которые зависят от порядка байт, отличающегося от используемого процессором текущей системы.
[Идентификаторы типов]
Следующие идентификаторы соответствуют типам u16, u32 и u64, за исключением случаев, когда они определены с поразрядным (bitwise) атрибутом, который вводят для ограничения применения их как целых чисел. Bitwise-атрибут используется утилитой sparse, чтобы гарантировать, что переменная преобразована в локальный тип процессора перед тем, как над переменной выполнятся другие (небезопасные, unsafe) операции.
Следующие типы можно применять для endian-зависимых переменных, после подключения header-файла linux/kernel.h.
__le16 __le32 __le64 __be16 __be32 __be64
[Макросы для преобразований]
Имеется множество макросов для преобразования порядка байт, используемого текущим процессором, в порядок либо big, либо little endian. Дополнительно для каждого типа конверсии имеются отдельные макросы для 16-, 32- и 64-разрядных значений. Имена макросов кодируют исходный и целевой порядок байт значения, так что по имени сразу понятно, что каждый макрос делает.
В целях написания портируемого кода Вы должны всегда применять эти макросы для преобразования из одного целевого порядка байт в другой, даже если заранее знаете, что это не требуется для того процессора, который сейчас используете. Если исходный и целевой порядок байт одинаковый, то макрос не будет выполнять никаких действий, так что применение макросов на быстродействии никак не скажется.
Следующие макросы вернут значение после конвертации. Обратите внимание, что заголовочный файл linux/kernel.h является заголовком, который должен быть подключен к файлам исходного кода, где макросы используются, но это не тот файл заголовка, где макросы реально определены.
__u16 le16_to_cpu(const __le16); __u32 le32_to_cpu(const __le32); __u64 le64_to_cpu(const __le64); __le16 cpu_to_le16(const __u16); __le32 cpu_to_le32(const __u32); __le64 cpu_to_le64(const __u64); __u16 be16_to_cpu(const __be16); __u32 be32_to_cpu(const __be32); __u64 be64_to_cpu(const __be64); __be16 cpu_to_be16(const __u16); __be32 cpu_to_be32(const __u32); __be64 cpu_to_be64(const __u64);
Следующие макросы такие же, как и предыдущие, отличие только в том, что параметр макроса — это указатель на преобразуемую величину. Обратите внимание, что имена этих макросов такие же, только добавлен суффикс «p» (от слова pointer) в конце каждого имени.
__u16 le16_to_cpup(const __le16 *); __u32 le32_to_cpup(const __le32 *); __u64 le64_to_cpup(const __le64 *); __le16 cpu_to_le16p(const __u16 *); __le32 cpu_to_le32p(const __u32 *); __le64 cpu_to_le64p(const __u64 *); __u16 be16_to_cpup(const __be16 *); __u32 be32_to_cpup(const __be32 *); __u64 be64_to_cpup(const __be64 *); __be16 cpu_to_be16p(const __u16 *); __be32 cpu_to_be32p(const __u32 *); __be64 cpu_to_be64p(const __u64 *);
Следующие макросы делают то же самое, что и предыдущие, но здесь место расположения исходной величины и преобразованной величины совпадают. Обратите внимание, что имена этих макросов такие же, только добавлен суффикс «s» (от латинской фразы in situ, что обозначает «в том же месте») в конце каждого имени.
Следующие макросы предоставляют алиасы для имен функций, которые обычно применяются для преобразования порядка байт в коде сетевых приложений. Первые два макроса используются для преобразования из локального в сетевой порядок байт. Остальные два предоставляют обратное преобразование. Буквы «s» и «l» в конце имен в этом случае означают short (16-битное значение) и long (32-битное значение).
#define htons(x) cpu_to_be16(x)
#define htonl(x) cpu_to_be32(x)
#define ntohs(x) be16_to_cpu(x)
#define ntohl(x) be32_to_cpu(x)
Как отмечалось ранее, сетевой порядок байт всегда big endian, и реализация этих макросов гарантирует корректное использование порядка байт сетевым хостом.
[Отличия между шинами BE-32 и BE-8]
Различия между обработкой шин данных Word-Invariant, или BE-32, и Byte-Invariant, или BE-8, следующее (BE означает Big Endian):
● В системе BE-32, Word-Invariant, представление 32-битного доступа по шине к слову (word access) является одинаковым в сравнении с доступом LE (Little Endian) к одному и тому же адресу слова. Однако представление байтового доступа шины (byte access) и доступа к половине слова (half-word access) различается.
● В BE-8, Byte Invariant, система представляет байтовый доступ одинаковым в сравнении с доступом LE к одному и тому же байтовому адресу.
Причем в обоих реализациях big-endian доступа BE-32 и BE-8 самый малый байтовый адрес соответствует самому значимому байту.
Таблица ниже показывает эффект доступа LE, BE-8 и BE-32 на 64-разрядной шине. Базовая форма для доступа к байту столбцов LE и BE-8 одинаковая, и также она одинаковая для доступа к слову столбцов LE и BE-32.
Примечание: в обоих случаях BE-8 и BE-32, доступ к байту по адресу 0 (самый младший адрес в системе) соответствует самому старшему байту доступа к слову, поэтому соответствует описанию big-endian.
Биты шины данных | Байтовый доступ | Доступ к половине слова | Доступ к слову | ||||||
LE | BE8 | BE32 | LE | BE8 | BE32 | LE | BE8 | BE32 | |
63:56 | A7 | A7 | A4 | A6:MS | A6:LS | A4:MS | A4:MS | A4:LS | A4:MS |
55:48 | A6 | A6 | A5 | A6:LS | A6:MS | A4:LS | A4:MS-1 | A4:LS+1 | A4:MS-1 |
47:40 | A5 | A5 | A6 | A4:MS | A4:LS | A6:MS | A4:LS+1 | A4:MS-1 | A4:LS+1 |
39:32 | A4 | A4 | A7 | A4:LS | A4:MS | A6:LS | A4:LS | A4:MS | A4:LS |
31:24 | A3 | A3 | A0 | A2:MS | A2:LS | A0:MS | A0:MS | A0:LS | A0:MS |
23:16 | A2 | A2 | A1 | A2:LS | A2:MS | A0:LS | A0:MS-1 | A0:LS+1 | A0:MS-1 |
15:8 | A1 | A1 | A2 | A0:MS | A0:LS | A2:MS | A0:LS+1 | A0:MS-1 | A0:LS+1 |
7:0 | A0 | A0 | A3 | A0:LS | A0:MS | A2:LS | A0:LS | A0:MS | A0:LS |
A < Num >Доступ к байту к address[2:0] = Num.
A< Num >: < Byte >Байт < Byte >доступа к слову / половине слова к address[2:0]=Num.
< Byte >: MS Самый значимый байт.
MS-1 Следующий по значимости байт.
LS+1 Следующий по минимуму значимости байт.
LS Наименее значимый байт.
1. Byte Order site:bruceblinn.com .
2. Макросы для реверсирования порядка байт.
3. Differences between BE-32 and BE-8 buses site:developer.arm.com.