← Documents Documentation/core-api/unaligned-memory-access.rst GitHub 원문 ↗

Linux 6.18.37 · Core API

Unaligned Memory Accesses

Natural alignment 규칙, unaligned access의 architecture별 영향, compiler padding과 packed structure, 안전한 access macro 및 networking alignment를 설명합니다.

Source pathDocumentation/core-api/unaligned-memory-access.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

unaligned-memory-access.rst:1-265

N byte를 읽거나 쓰는 address가 N의 배수가 아니면 unaligned access입니다. 일부 architecture는 이를 느리게 처리하고, 일부는 exception을 발생시키며, 일부는 요청과 다른 access를 조용히 수행할 수 있습니다.

일반 variable과 structure field는 compiler가 natural alignment와 padding을 보장합니다. 위험은 길이가 다른 type으로 cast하거나 pointer arithmetic 뒤 여러 byte를 직접 access할 때 주로 생깁니다.

정렬을 보장할 수 없다면 `<linux/unaligned.h>`의 `get_unaligned()`와 `put_unaligned()`를 사용하고, 적절한 경우 byte 단위 `memcpy()`를 사용합니다.

Networking에서는 IP header의 4-byte alignment와 DMA 제약을 함께 고려해야 하며, architecture capability에 따라 `NET_IP_ALIGN`과 `CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS`를 적용합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 =========================
2 Unaligned Memory Accesses
3 =========================
4
5 :Author: Daniel Drake <[email protected]>,
6 :Author: Johannes Berg <[email protected]>
7
8 :With help from: Alan Cox, Avuton Olrich, Heikki Orsila, Jan Engelhardt,
9 Kyle McMartin, Kyle Moffett, Randy Dunlap, Robert Hancock, Uli Kunitz,
10 Vadim Lobanov
11
12
13 Linux runs on a wide variety of architectures which have varying behaviour
14 when it comes to memory access. This document presents some details about
15 unaligned accesses, why you need to write code that doesn't cause them,
16 and how to write such code!
17
18
19 The definition of an unaligned access
20 =====================================
21
22 Unaligned memory accesses occur when you try to read N bytes of data starting
23 from an address that is not evenly divisible by N (i.e. addr % N != 0).
24 For example, reading 4 bytes of data from address 0x10004 is fine, but
25 reading 4 bytes of data from address 0x10005 would be an unaligned memory
26 access.
27
28 The above may seem a little vague, as memory access can happen in different
29 ways. The context here is at the machine code level: certain instructions read
30 or write a number of bytes to or from memory (e.g. movb, movw, movl in x86
31 assembly). As will become clear, it is relatively easy to spot C statements
32 which will compile to multiple-byte memory access instructions, namely when
33 dealing with types such as u16, u32 and u64.
34
35
36 Natural alignment
37 =================
38
39 The rule mentioned above forms what we refer to as natural alignment:
40 When accessing N bytes of memory, the base memory address must be evenly
41 divisible by N, i.e. addr % N == 0.
42
43 When writing code, assume the target architecture has natural alignment
44 requirements.
45
46 In reality, only a few architectures require natural alignment on all sizes
47 of memory access. However, we must consider ALL supported architectures;
48 writing code that satisfies natural alignment requirements is the easiest way
49 to achieve full portability.
50
51
52 Why unaligned access is bad
53 ===========================
54
55 The effects of performing an unaligned memory access vary from architecture
56 to architecture. It would be easy to write a whole document on the differences
57 here; a summary of the common scenarios is presented below:
58
59 - Some architectures are able to perform unaligned memory accesses
60 transparently, but there is usually a significant performance cost.
61 - Some architectures raise processor exceptions when unaligned accesses
62 happen. The exception handler is able to correct the unaligned access,
63 at significant cost to performance.
64 - Some architectures raise processor exceptions when unaligned accesses
65 happen, but the exceptions do not contain enough information for the
66 unaligned access to be corrected.
67 - Some architectures are not capable of unaligned memory access, but will
68 silently perform a different memory access to the one that was requested,
69 resulting in a subtle code bug that is hard to detect!
70
71 It should be obvious from the above that if your code causes unaligned
72 memory accesses to happen, your code will not work correctly on certain
73 platforms and will cause performance problems on others.
74
75
76 Code that does not cause unaligned access
77 =========================================
78
79 At first, the concepts above may seem a little hard to relate to actual
80 coding practice. After all, you don't have a great deal of control over
81 memory addresses of certain variables, etc.
82
83 Fortunately things are not too complex, as in most cases, the compiler
84 ensures that things will work for you. For example, take the following
85 structure::
86
87 struct foo {
88 u16 field1;
89 u32 field2;
90 u8 field3;
91 };
92
93 Let us assume that an instance of the above structure resides in memory
94 starting at address 0x10000. With a basic level of understanding, it would
95 not be unreasonable to expect that accessing field2 would cause an unaligned
96 access. You'd be expecting field2 to be located at offset 2 bytes into the
97 structure, i.e. address 0x10002, but that address is not evenly divisible
98 by 4 (remember, we're reading a 4 byte value here).
99
100 Fortunately, the compiler understands the alignment constraints, so in the
101 above case it would insert 2 bytes of padding in between field1 and field2.
102 Therefore, for standard structure types you can always rely on the compiler
103 to pad structures so that accesses to fields are suitably aligned (assuming
104 you do not cast the field to a type of different length).
105
106 Similarly, you can also rely on the compiler to align variables and function
107 parameters to a naturally aligned scheme, based on the size of the type of
108 the variable.
109
110 At this point, it should be clear that accessing a single byte (u8 or char)
111 will never cause an unaligned access, because all memory addresses are evenly
112 divisible by one.
113
114 On a related topic, with the above considerations in mind you may observe
115 that you could reorder the fields in the structure in order to place fields
116 where padding would otherwise be inserted, and hence reduce the overall
117 resident memory size of structure instances. The optimal layout of the
118 above example is::
119
120 struct foo {
121 u32 field2;
122 u16 field1;
123 u8 field3;
124 };
125
126 For a natural alignment scheme, the compiler would only have to add a single
127 byte of padding at the end of the structure. This padding is added in order
128 to satisfy alignment constraints for arrays of these structures.
129
130 Another point worth mentioning is the use of __attribute__((packed)) on a
131 structure type. This GCC-specific attribute tells the compiler never to
132 insert any padding within structures, useful when you want to use a C struct
133 to represent some data that comes in a fixed arrangement 'off the wire'.
134
135 You might be inclined to believe that usage of this attribute can easily
136 lead to unaligned accesses when accessing fields that do not satisfy
137 architectural alignment requirements. However, again, the compiler is aware
138 of the alignment constraints and will generate extra instructions to perform
139 the memory access in a way that does not cause unaligned access. Of course,
140 the extra instructions obviously cause a loss in performance compared to the
141 non-packed case, so the packed attribute should only be used when avoiding
142 structure padding is of importance.
143
144
145 Code that causes unaligned access
146 =================================
147
148 With the above in mind, let's move onto a real life example of a function
149 that can cause an unaligned memory access. The following function taken
150 from include/linux/etherdevice.h is an optimized routine to compare two
151 ethernet MAC addresses for equality::
152
153 bool ether_addr_equal(const u8 *addr1, const u8 *addr2)
154 {
155 #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
156 u32 fold = ((*(const u32 *)addr1) ^ (*(const u32 *)addr2)) |
157 ((*(const u16 *)(addr1 + 4)) ^ (*(const u16 *)(addr2 + 4)));
158
159 return fold == 0;
160 #else
161 const u16 *a = (const u16 *)addr1;
162 const u16 *b = (const u16 *)addr2;
163 return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0;
164 #endif
165 }
166
167 In the above function, when the hardware has efficient unaligned access
168 capability, there is no issue with this code. But when the hardware isn't
169 able to access memory on arbitrary boundaries, the reference to a[0] causes
170 2 bytes (16 bits) to be read from memory starting at address addr1.
171
172 Think about what would happen if addr1 was an odd address such as 0x10003.
173 (Hint: it'd be an unaligned access.)
174
175 Despite the potential unaligned access problems with the above function, it
176 is included in the kernel anyway but is understood to only work normally on
177 16-bit-aligned addresses. It is up to the caller to ensure this alignment or
178 not use this function at all. This alignment-unsafe function is still useful
179 as it is a decent optimization for the cases when you can ensure alignment,
180 which is true almost all of the time in ethernet networking context.
181
182
183 Here is another example of some code that could cause unaligned accesses::
184
185 void myfunc(u8 *data, u32 value)
186 {
187 [...]
188 *((u32 *) data) = cpu_to_le32(value);
189 [...]
190 }
191
192 This code will cause unaligned accesses every time the data parameter points
193 to an address that is not evenly divisible by 4.
194
195 In summary, the 2 main scenarios where you may run into unaligned access
196 problems involve:
197
198 1. Casting variables to types of different lengths
199 2. Pointer arithmetic followed by access to at least 2 bytes of data
200
201
202 Avoiding unaligned accesses
203 ===========================
204
205 The easiest way to avoid unaligned access is to use the get_unaligned() and
206 put_unaligned() macros provided by the <linux/unaligned.h> header file.
207
208 Going back to an earlier example of code that potentially causes unaligned
209 access::
210
211 void myfunc(u8 *data, u32 value)
212 {
213 [...]
214 *((u32 *) data) = cpu_to_le32(value);
215 [...]
216 }
217
218 To avoid the unaligned memory access, you would rewrite it as follows::
219
220 void myfunc(u8 *data, u32 value)
221 {
222 [...]
223 value = cpu_to_le32(value);
224 put_unaligned(value, (u32 *) data);
225 [...]
226 }
227
228 The get_unaligned() macro works similarly. Assuming 'data' is a pointer to
229 memory and you wish to avoid unaligned access, its usage is as follows::
230
231 u32 value = get_unaligned((u32 *) data);
232
233 These macros work for memory accesses of any length (not just 32 bits as
234 in the examples above). Be aware that when compared to standard access of
235 aligned memory, using these macros to access unaligned memory can be costly in
236 terms of performance.
237
238 If use of such macros is not convenient, another option is to use memcpy(),
239 where the source or destination (or both) are of type u8* or unsigned char*.
240 Due to the byte-wise nature of this operation, unaligned accesses are avoided.
241
242
243 Alignment vs. Networking
244 ========================
245
246 On architectures that require aligned loads, networking requires that the IP
247 header is aligned on a four-byte boundary to optimise the IP stack. For
248 regular ethernet hardware, the constant NET_IP_ALIGN is used. On most
249 architectures this constant has the value 2 because the normal ethernet
250 header is 14 bytes long, so in order to get proper alignment one needs to
251 DMA to an address which can be expressed as 4*n + 2. One notable exception
252 here is powerpc which defines NET_IP_ALIGN to 0 because DMA to unaligned
253 addresses can be very expensive and dwarf the cost of unaligned loads.
254
255 For some ethernet hardware that cannot DMA to unaligned addresses like
256 4*n+2 or non-ethernet hardware, this can be a problem, and it is then
257 required to copy the incoming frame into an aligned buffer. Because this is
258 unnecessary on architectures that can do unaligned accesses, the code can be
259 made dependent on CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS like so::
260
261 #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
262 skb = original skb
263 #else
264 skb = copy skb
265 #endif
266

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

정렬되지 않은 memory access 소개

1-18

정렬되지 않은 Memory Access

저자: Daniel Drake <[email protected]>

저자: Johannes Berg <[email protected]>

도움을 준 사람: Alan Cox, Avuton Olrich, Heikki Orsila, Jan Engelhardt, Kyle McMartin, Kyle Moffett, Randy Dunlap, Robert Hancock, Uli Kunitz, Vadim Lobanov

Linux는 memory access 동작이 서로 다른 매우 다양한 architecture에서 실행됩니다. 이 문서는 정렬되지 않은 access가 무엇인지, 그런 access를 일으키지 않는 code를 작성해야 하는 이유와 그 작성 방법을 설명합니다.

정렬되지 않은 access의 정의

19-35

정렬되지 않은 access의 정의

N byte data를 읽으려는 시작 address가 N으로 나누어떨어지지 않을 때, 즉 `addr % N != 0`일 때 unaligned memory access가 발생합니다. 예를 들어 address `0x10004`에서 4 byte를 읽는 것은 괜찮지만, `0x10005`에서 4 byte를 읽으면 unaligned memory access입니다.

Memory access는 여러 방식으로 일어나므로 위 설명이 다소 모호해 보일 수 있습니다. 여기서 말하는 context는 machine code level입니다. 특정 instruction은 memory에서 정해진 byte 수를 읽거나 씁니다. 예를 들어 x86 assembly의 `movb`, `movw`, `movl`이 이에 해당합니다.

여러 byte를 access하는 machine instruction으로 compile될 C statement는 비교적 쉽게 식별할 수 있습니다. 특히 `u16`, `u32`, `u64` 같은 type을 다룰 때 그렇습니다.

Natural alignment

36-51

Natural alignment

위 규칙이 natural alignment를 이룹니다. N byte memory에 access할 때 base memory address는 N으로 나누어떨어져야 합니다. 즉 `addr % N == 0`이어야 합니다.

Code를 작성할 때는 target architecture에 natural alignment requirement가 있다고 가정하십시오.

실제로 모든 memory access 크기에 natural alignment를 요구하는 architecture는 소수뿐입니다. 그러나 지원하는 모든 architecture를 고려해야 하며, natural alignment requirement를 만족하는 code를 작성하는 것이 완전한 portability를 얻는 가장 쉬운 방법입니다.

정렬되지 않은 access가 나쁜 이유

52-75

정렬되지 않은 access가 나쁜 이유

Unaligned memory access의 영향은 architecture마다 다릅니다. 일반적으로 발생하는 상황을 요약하면 다음과 같습니다.

  • 일부 architecture는 unaligned memory access를 투명하게 수행할 수 있지만 대개 상당한 performance cost가 듭니다.
  • 일부 architecture는 unaligned access가 발생하면 processor exception을 일으킵니다. Exception handler가 access를 바로잡을 수 있지만 performance cost가 큽니다.
  • 일부 architecture도 processor exception을 일으키지만 exception에 unaligned access를 바로잡기 위한 정보가 충분하지 않습니다.
  • 일부 architecture는 unaligned memory access를 수행할 수 없으면서 요청과 다른 memory access를 조용히 수행합니다. 그 결과 발견하기 어려운 미묘한 code bug가 생깁니다.

따라서 code가 unaligned memory access를 일으키면 일부 platform에서는 올바르게 작동하지 않고, 다른 platform에서는 performance 문제를 일으킵니다.

정렬되지 않은 access를 일으키지 않는 code

76-144

정렬되지 않은 access를 일으키지 않는 code

처음에는 위 개념을 실제 coding practice와 연결하기 어려워 보일 수 있습니다. 특정 variable의 memory address 등을 programmer가 크게 제어할 수 없기 때문입니다.

다행히 대부분의 경우 compiler가 올바르게 동작하도록 보장하므로 복잡하지 않습니다. 다음 structure를 예로 들겠습니다.

struct foo {
        u16 field1;
        u32 field2;
        u8 field3;
};

이 structure instance가 address `0x10000`에서 시작한다고 가정합니다. 단순하게 생각하면 `field2`가 structure 안의 offset 2 byte, 즉 `0x10002`에 놓여 4로 나누어떨어지지 않으므로 access가 정렬되지 않을 것이라 예상할 수 있습니다. 여기서는 4 byte 값을 읽는다는 점을 기억하십시오.

Compiler는 alignment constraint를 이해하므로 위 경우 `field1`과 `field2` 사이에 padding 2 byte를 넣습니다. 따라서 field를 다른 길이의 type으로 cast하지 않는 한 standard structure type에서는 compiler가 field access를 적절히 정렬하도록 padding을 추가한다고 항상 믿을 수 있습니다.

마찬가지로 compiler는 variable type 크기를 바탕으로 variable과 function parameter를 natural alignment 방식에 맞춰 정렬합니다.

단일 byte인 `u8` 또는 `char` access는 모든 memory address가 1로 나누어떨어지므로 unaligned access를 일으키지 않습니다.

또한 padding이 들어갈 자리에 field를 배치하도록 structure field 순서를 바꾸면 structure instance가 차지하는 전체 memory 크기를 줄일 수 있습니다. 위 예의 최적 layout은 다음과 같습니다.

struct foo {
        u32 field2;
        u16 field1;
        u8 field3;
};

Natural alignment 방식에서 compiler는 structure 끝에 padding 1 byte만 추가하면 됩니다. 이 padding은 이러한 structure의 array에 대한 alignment constraint를 만족시키기 위해 추가됩니다.

Structure type에 `__attribute__((packed))`를 사용하는 점도 언급할 가치가 있습니다. 이 GCC 전용 attribute는 structure 내부에 padding을 절대 넣지 말라고 compiler에 지시합니다. 고정된 배열로 wire를 통해 들어오는 data를 C struct로 표현할 때 유용합니다.

이 attribute를 사용하면 architecture alignment requirement를 만족하지 않는 field에 access할 때 쉽게 unaligned access가 생긴다고 생각할 수 있습니다. 그러나 compiler는 alignment constraint를 알고 있으므로 unaligned access를 일으키지 않는 방식으로 memory에 access하도록 추가 instruction을 생성합니다.

추가 instruction은 non-packed case보다 performance를 떨어뜨리므로 packed attribute는 structure padding을 피하는 것이 중요할 때만 사용해야 합니다.

정렬되지 않은 access를 일으키는 code

145-201

정렬되지 않은 access를 일으키는 code

실제로 unaligned memory access를 일으킬 수 있는 function을 살펴보겠습니다. 다음은 `include/linux/etherdevice.h`에서 가져온 두 ethernet MAC address의 동일 여부를 비교하는 최적화 routine입니다.

bool ether_addr_equal(const u8 *addr1, const u8 *addr2)
{
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
      u32 fold = ((*(const u32 *)addr1) ^ (*(const u32 *)addr2)) |
                 ((*(const u16 *)(addr1 + 4)) ^ (*(const u16 *)(addr2 + 4)));

      return fold == 0;
#else
      const u16 *a = (const u16 *)addr1;
      const u16 *b = (const u16 *)addr2;
      return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0;
#endif
}

Hardware에 효율적인 unaligned access capability가 있다면 이 code에는 문제가 없습니다. 그러나 hardware가 임의의 boundary에서 memory에 access할 수 없다면 `a[0]` reference가 `addr1` address에서 시작해 2 byte, 즉 16 bit를 읽습니다.

`addr1`이 `0x10003` 같은 홀수 address라면 어떤 일이 생길지 생각해 보십시오. 정렬되지 않은 access가 됩니다.

잠재적인 문제에도 이 function은 kernel에 포함되어 있지만 16-bit-aligned address에서만 정상적으로 동작한다고 이해해야 합니다. Caller가 이 alignment를 보장하거나 function을 전혀 사용하지 않아야 합니다.

이 alignment-unsafe function은 alignment를 보장할 수 있을 때 좋은 최적화이므로 여전히 유용하며, ethernet networking context에서는 거의 항상 보장할 수 있습니다.

다음 code 역시 unaligned access를 일으킬 수 있습니다.

void myfunc(u8 *data, u32 value)
{
        [...]
        *((u32 *) data) = cpu_to_le32(value);
        [...]
}

`data` parameter가 4로 나누어떨어지지 않는 address를 가리킬 때마다 이 code는 unaligned access를 일으킵니다.

요약하면 unaligned access 문제를 만나는 두 가지 주요 상황은 다음과 같습니다.

  • Variable을 길이가 다른 type으로 cast하는 경우
  • Pointer arithmetic을 수행한 뒤 최소 2 byte의 data에 access하는 경우

정렬되지 않은 access 피하기

202-242

정렬되지 않은 access 피하기

Unaligned access를 피하는 가장 쉬운 방법은 `<linux/unaligned.h>` header file이 제공하는 `get_unaligned()` 및 `put_unaligned()` macro를 사용하는 것입니다.

앞에서 본 잠재적으로 unaligned access를 일으키는 code는 다음과 같습니다.

void myfunc(u8 *data, u32 value)
{
        [...]
        *((u32 *) data) = cpu_to_le32(value);
        [...]
}

Unaligned memory access를 피하려면 다음처럼 다시 작성합니다.

void myfunc(u8 *data, u32 value)
{
        [...]
        value = cpu_to_le32(value);
        put_unaligned(value, (u32 *) data);
        [...]
}

`get_unaligned()` macro도 비슷하게 동작합니다. `data`가 memory를 가리키는 pointer이고 unaligned access를 피하려면 다음과 같이 사용합니다.

u32 value = get_unaligned((u32 *) data);

이 macro는 위 예의 32 bit뿐 아니라 모든 길이의 memory access에 동작합니다. 다만 aligned memory의 standard access와 비교하면 이 macro로 unaligned memory에 access하는 것은 performance cost가 클 수 있습니다.

이러한 macro 사용이 편리하지 않다면 source 또는 destination, 혹은 둘 다 `u8*` 또는 `unsigned char*` type인 `memcpy()`를 사용할 수도 있습니다. 이 operation은 byte 단위로 수행되므로 unaligned access를 피합니다.

Alignment와 networking

243-265

Alignment와 networking

Aligned load를 요구하는 architecture에서 networking은 IP stack 최적화를 위해 IP header를 4-byte boundary에 정렬해야 합니다. 일반 ethernet hardware에는 constant `NET_IP_ALIGN`을 사용합니다.

일반 ethernet header 길이는 14 byte이므로 올바른 alignment를 얻으려면 `4*n + 2` 형태의 address로 DMA해야 합니다. 따라서 대부분의 architecture에서 `NET_IP_ALIGN` 값은 2입니다.

주목할 예외는 powerpc입니다. 정렬되지 않은 address로 DMA하는 비용이 매우 커 unaligned load 비용을 압도할 수 있으므로 powerpc는 `NET_IP_ALIGN`을 0으로 정의합니다.

`4*n+2`처럼 정렬되지 않은 address로 DMA할 수 없는 일부 ethernet hardware나 non-ethernet hardware에서는 이것이 문제가 될 수 있으며, incoming frame을 aligned buffer로 복사해야 합니다.

Unaligned access가 가능한 architecture에서는 이 복사가 불필요하므로 다음처럼 `CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS`에 따라 code를 구성할 수 있습니다.

#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
        skb = original skb
#else
        skb = copy skb
#endif