Unaligned Memory Accesses
- Author: Daniel Drake ,
- Author: Johannes Berg
- With help from: Alan Cox, Avuton Olrich, Heikki Orsila, Jan Engelhardt, Kyle McMartin, Kyle Moffett, Randy Dunlap, Robert Hancock, Uli Kunitz, Vadim Lobanov
Linux runs on a wide variety of architectures which have varying behaviour when it comes to memory access. This document presents some details about unaligned accesses, why you need to write code that doesn't cause them, and how to write such code!
Linuxはメモリアクセスに関して様々な振る舞いをする、様々なアーキテクチャ上で動いています。このドキュメントでは、unaligned accessに関する詳細について紹介をします、なぜ我々はそれが起こらないようにコードを書かねばならないのか、そして、どのように書くべきなのか!
The definition of an unaligned access
Unaligned memory accesses occur when you try to read N bytes of data starting from an address that is not evenly divisible by N (i.e. addr % N != 0).
For example, reading 4 bytes of data from address 0x10004 is fine, but reading 4 bytes of data from address 0x10005 would be an unaligned memory access.
Unaligned memory accessは、N bytesのデータ構造をNで割り切れないアドレス位置から開始して読み出そうとする場合に発生します(つまり、add % N != 0 ).
例えば、データ4 byteをアドレス0x10004から読み出す事は問題ありませんが、データ4 byteをアドレス0x10005から読み出す事は、unaligned memory maccessとなります。
The above may seem a little vague, as memory access can happen in different ways. The context here is at the machine code level: certain instructions read or write a number of bytes to or from memory (e.g. movb, movw, movl in x86 assembly). As will become clear, it is relatively easy to spot C statements which will compile to multiple-byte memory access instructions, namely when dealing with types such as u16, u32 and u64.
メモリアクセスは異なる方法で発生するため、上記は少し曖昧に見えるかもしれません。コンテキストは、機械語コードレベルです。特定の命令は、メモリに対して、複数のbyteのreadやwriteを実行します(x86 アセンブラではmovb, movw, movl)。明らかなことに、コンパイルされるC言語のstatementで、複数のbyte memory accessする命令になるものは比較的容易です。それらはu16,u32,u64などのタイプを扱います。
Natural alignment
The rule mentioned above forms what we refer to as natural alignment: When accessing N bytes of memory, the base memory address must be evenly divisible by N, i.e. addr % N == 0.
上記のルールは、natural alignmentと呼ばれる振る舞いを定義します。N byteのメモリにアクセスする場合、base memory addressはNで割り切れる必要があります。つまり、add % N == 0。
When writing code, assume the target architecture has natural alignment requirements.
コードを記述する場合には、ターゲットとなるアーキテクチャには、natural alignment要求があると想定してください。
In reality, only a few architectures require natural alignment on all sizes of memory access. However, we must consider ALL supported architectures; writing code that satisfies natural alignment requirements is the easiest way to achieve full portability.
実際のところ、全てのサイズに対するメモリアクセスに対して、natural alignmentを要求するアーキテクチャは限定的です。ただし、全てのサポートするアーキテクチャを考慮しなければなりません。natural alignment要件を満たすコードを記述する事は、完全な移植性を実現するのに最も簡単な方法です。
Why unaligned access is bad
The effects of performing an unaligned memory access vary from architecture to architecture. It would be easy to write a whole document on the differences here; a summary of the common scenarios is presented below:
unaligned memory accessの実行による影響は、アーキテクチャによって異なります。ここで違いについてすべて記述するのは簡単です。一般的なシナリオの概要を以下に述べます。
- Some architectures are able to perform unaligned memory accesses transparently, but there is usually a significant performance cost.
-
Some architectures raise processor exceptions when unaligned accesses happen. The exception handler is able to correct the unaligned access, at significant cost to performance.
-
Some architectures raise processor exceptions when unaligned accesses happen, but the exceptions do not contain enough information for the unaligned access to be corrected.
-
Some architectures are not capable of unaligned memory access, but will silently perform a different memory access to the one that was requested, resulting in a subtle code bug that is hard to detect!
-
unaligned memory accessを等価的に実行できるアーキテクチャがあります。これらは、通常、パフォーマンス上のコストが大幅に大きくなります。
-
unaligned accessが発生したときに、processorでexceptionを発生させるアーキテクチャがあります。exception handlerは、unaligned accessを補正することができますが、パフォーマンスに非常に大きいコストがかかります。
-
unaligned accessが発生したときに、processorでexceptionを発生させるアーキテクチャがあります。しかし、exceptionにはunaligned accessを補正するのに十分な情報が含まれていない。
-
unaligned memory accessを許容していないアーキテクチャがあります。要求されたメモリとは異なるメモリアクセスが実行され、検出が困難なコードバグを引き起こします。
It should be obvious from the above that if your code causes unaligned memory accesses to happen, your code will not work correctly on certain platforms and will cause performance problems on others.
上記記載のように、コードが原因となってunaligned memory accessが発生すると、コードは特定プラットフォームで正しく動作せず、他のプラットフォームではパフォーマンス問題を引き越します。
Code that does not cause unaligned access
At first, the concepts above may seem a little hard to relate to actual coding practice. After all, you don't have a great deal of control over memory addresses of certain variables, etc.
最初に、上記の概念を実際のコーディングの実装に関連付けるのは難しい。結局、特定の変数のメモリアドレスを制御することは難しい。
Fortunately things are not too complex, as in most cases, the compiler ensures that things will work for you. For example, take the following structure::
幸いにもほとんどの場合、コンパイラがうまくいくことを保障するので、問題はそこまで複雑ではありません。例えば、下記のような構造体を扱う場合::
struct foo {
u16 field1;
u32 field2;
u8 field3;
};
Let us assume that an instance of the above structure resides in memory starting at address 0x10000. With a basic level of understanding, it would not be unreasonable to expect that accessing field2 would cause an unaligned access. You'd be expecting field2 to be located at offset 2 bytes into the structure, i.e. address 0x10002, but that address is not evenly divisible by 4 (remember, we're reading a 4 byte value here).
上記構造体のインスタンスが0x10000アドレスからスタートするメモリ上にあると仮定します。基本的なレベルの理解では、field2ではunaligned accessが発生する事を予期することは、不合理ではありません。field2は構造体の中でoffset 2byte、0x10002アドレスに位置しており、4で割り切ることができません(ここでは、4byteの値を読み込んでいることに注意してください)。
Fortunately, the compiler understands the alignment constraints, so in the above case it would insert 2 bytes of padding in between field1 and field2. Therefore, for standard structure types you can always rely on the compiler to pad structures so that accesses to fields are suitably aligned (assuming you do not cast the field to a type of different length).
幸いにも、コンパイラーはalignment形成を理解していますので、このようなケースではfield1とfield2の間に2byteのpaddingを入れ込みます。その結果、標準的な構造タイプの場合、フィールドへのアクセスが適切に調整されるように、コンパイラーは構造体にpaddingを埋め込みます(fieldに異なる長さのタイプでcastをしないことを前提としています)。
Similarly, you can also rely on the compiler to align variables and function parameters to a naturally aligned scheme, based on the size of the type of the variable.
同じように、コンパイラーによって、変数の型の大きさに基づき、変数の関数のパラメータをnaturally aligned schemeに調整することができます。
At this point, it should be clear that accessing a single byte (u8 or char) will never cause an unaligned access, because all memory addresses are evenly divisible by one.
この時点で、byte(u8 や char)にアクセスをしても、全てのメモリアドレスが1で割り切れるので、unaligned memory accessは発生する事はありません。
On a related topic, with the above considerations in mind you may observe that you could reorder the fields in the structure in order to place fields where padding would otherwise be inserted, and hence reduce the overall resident memory size of structure instances. The optimal layout of the above example is::
関連する話題として、上記の注意事項を念頭に置くと、構造体のfieldを並び替える事で、paddingが挿入される場所にfieldを置くことができることに気が付くでしょう。そして、構造体インスタンスの常駐するメモリサイズは小さくできます。上記の例の最適なレイアウトは以下となります。
struct foo {
u32 field2;
u16 field1;
u8 field3;
};
For a natural alignment scheme, the compiler would only have to add a single byte of padding at the end of the structure. This padding is added in order to satisfy alignment constraints for arrays of these structures.
natural alignment schemeでは、コンパイラは構造体の末尾に1byteのpaddingをを追加するだけです。このpaddingは、この構造体の配列が、配置制約を満たすために追加されます。
(訳注:u32+u16+u8 だと、4byte+2byte+1byte = 7byte。これだと次の配列がずれるので、末端に1byteをいれて、構造体全体を8byteにしてやることで整合性が取れる)。
Another point worth mentioning is the use of attribute((packed)) on a structure type. This GCC-specific attribute tells the compiler never to insert any padding within structures, useful when you want to use a C struct to represent some data that comes in a fixed arrangement 'off the wire'.
言及しなければならないもう1つのポイントは、構造体型に対する、attribute((packed))の適用です。このGCC特有の属性は、構造体の中にpaddingを入れないように指示するものです。これは、C構造体を使用して、「ワイヤーから外れた」固定配置のデータを表すのに有益です。
You might be inclined to believe that usage of this attribute can easily lead to unaligned accesses when accessing fields that do not satisfy architectural alignment requirements.
However, again, the compiler is aware of the alignment constraints and will generate extra instructions to perform the memory access in a way that does not cause unaligned access.
この属性を利用した場合に、architecturalのalignment requirementsを満たさないfieldのアクセスにより、unaligned accessが容易に引き起こされる可能性があるとと考えるかもしれません。しかし、繰り返しになりますが、compilerは、alignment制約を認識しており、unaligned memory accessを引き起こさない方法でメモリアクセスを実行するために、追加の命令を生成します。
Of course, the extra instructions obviously cause a loss in performance compared to the non-packed case, so the packed attribute should only be used when avoiding structure padding is of importance.
もちろん、追加命令は、non-packed caseと比べたらパフォーマンスのロスを引き起こします。そのため、packed属性は、構造体のpaddingを回避することが重要な場合だけ、利用してください。
もともと、Linux Kernelのソースコードの一部なので、GPLv2扱いになる(はずの認識)。
https://www.kernel.org/doc/html/latest/index.html
Licensing documentation
The following describes the license of the Linux kernel source code (GPLv2), how to properly mark the license of individual files in the source tree, as well as links to the full license text.
https://www.kernel.org/doc/html/latest/process/license-rules.html#kernel-licensing