-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
331 lines (158 loc) · 671 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>RXE中的定时器</title>
<link href="/202210/rdma-stack-03/"/>
<url>/202210/rdma-stack-03/</url>
<content type="html"><![CDATA[<h2 id="1-RDMA-RXE中结构体的定义和初始化"><a href="#1-RDMA-RXE中结构体的定义和初始化" class="headerlink" title="1. RDMA_RXE中结构体的定义和初始化"></a>1. RDMA_RXE中结构体的定义和初始化</h2><ol><li><p>每个 rxe_qp 包含两个重传定时器,一个重传定时器用于指示未收到对端ACK:retrans_timer,另一个重传计时器指示收到了一个 RNR NAK (收到对端receiver 未准备好的NAK包后,同样要定时重传):rnrnak_timer</p><ol><li>RNR NAK 可参考IB Spec 1.3 Sec. 9.7.5.2.8 RNR NAK</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_verbs.h</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span>{</span><br>...<br> <span class="hljs-comment">/* Timer for retranmitting packet when ACKs have been lost. RC</span><br><span class="hljs-comment"> * only. The requester sets it when it is not already</span><br><span class="hljs-comment"> * started. The responder resets it whenever an ack is</span><br><span class="hljs-comment"> * received.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">timer_list</span> <span class="hljs-title">retrans_timer</span>;</span><br>u64 qp_timeout_jiffies;<br><br><span class="hljs-comment">/* Timer for handling RNR NAKS. */</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">timer_list</span> <span class="hljs-title">rnr_nak_timer</span>;</span><br><br>...<br>};<br></code></pre></td></tr></table></figure></li><li><p>初始化QP的时候,初始化两个定时器</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_qp.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_qp_init_req</span><span class="hljs-params">(struct rxe_dev *rxe, struct rxe_qp *qp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ib_qp_init_attr *init, struct ib_udata *udata,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct rxe_create_qp_resp __user *uresp)</span></span><br><span class="hljs-function"></span>{<br>...<br><br>qp->qp_timeout_jiffies = <span class="hljs-number">0</span>; <span class="hljs-comment">/* Can't be set for UD/UC in modify_qp */</span><br><span class="hljs-keyword">if</span> (init->qp_type == IB_QPT_RC) {<br>timer_setup(&qp->rnr_nak_timer, rnr_nak_timer, <span class="hljs-number">0</span>);<br>timer_setup(&qp->retrans_timer, retransmit_timer, <span class="hljs-number">0</span>);<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><br><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_comp.c</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">retransmit_timer</span><span class="hljs-params">(struct timer_list *t)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> from_timer(qp, t, retrans_timer);<br><br>pr_debug(<span class="hljs-string">"%s: fired for qp#%d\n"</span>, __func__, qp->elem.index);<br><br><span class="hljs-keyword">if</span> (qp->valid) {<br>qp->comp.timeout = <span class="hljs-number">1</span>;<br>rxe_run_task(&qp->comp.task, <span class="hljs-number">1</span>);<br>}<br>}<br><br><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_req.c</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">rnr_nak_timer</span><span class="hljs-params">(struct timer_list *t)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> from_timer(qp, t, rnr_nak_timer);<br><br>pr_debug(<span class="hljs-string">"%s: fired for qp#%d\n"</span>, __func__, qp_num(qp));<br><br><span class="hljs-comment">/* request a send queue retry */</span><br>qp->req.need_retry = <span class="hljs-number">1</span>;<br>qp->req.wait_for_rnr_timer = <span class="hljs-number">0</span>;<br>rxe_run_task(&qp->req.task, <span class="hljs-number">1</span>);<br>}<br><br><span class="hljs-comment">// include/linux/timer.h</span><br><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> from_timer(var, callback_timer, timer_fieldname) \</span><br><span class="hljs-meta">container_of(callback_timer, typeof(*var), timer_fieldname)</span><br></code></pre></td></tr></table></figure><ol><li><p>timer_setup API</p><ul><li><p>这是kernel 4.14之后新引入的一个设置定时器的API</p><p> 参考 <a href="https://lwn.net/Articles/735887/">https://lwn.net/Articles/735887/</a></p><p> 功能就是把 timer的回调函数设为callback,flag设为当前CPU ID</p></li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// include/linux/timer.h</span><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * timer_setup - prepare a timer for first use</span><br><span class="hljs-comment"> * @timer: the timer in question</span><br><span class="hljs-comment"> * @callback: the function to call when timer expires</span><br><span class="hljs-comment"> * @flags: any TIMER_* flags</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * Regular timer initialization should use either DEFINE_TIMER() above,</span><br><span class="hljs-comment"> * or timer_setup(). For timers on the stack, timer_setup_on_stack() must</span><br><span class="hljs-comment"> * be used and must be balanced with a call to destroy_timer_on_stack().</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> timer_setup(timer, callback, flags)\</span><br><span class="hljs-meta">__init_timer((timer), (callback), (flags))</span><br><br><span class="hljs-comment">// include/linux/timer.h</span><br><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> __init_timer(_timer, _fn, _flags)\</span><br><span class="hljs-meta">init_timer_key((_timer), (_fn), (_flags), NULL, NULL)</span><br><br><span class="hljs-comment">// kernel/time/timer.c</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">init_timer_key</span><span class="hljs-params">(struct timer_list *timer,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">void</span> (*func)(struct timer_list *), <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> flags,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *name, struct lock_class_key *key)</span></span><br><span class="hljs-function"></span>{<br>debug_init(timer);<br>do_init_timer(timer, func, flags, name, key);<br>}<br><br><span class="hljs-comment">// kernel/time/timer.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">do_init_timer</span><span class="hljs-params">(struct timer_list *timer,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">void</span> (*func)(struct timer_list *),</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> flags,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *name, struct lock_class_key *key)</span></span><br><span class="hljs-function"></span>{<br>timer->entry.pprev = <span class="hljs-literal">NULL</span>;<br>timer->function = func;<br><span class="hljs-keyword">if</span> (WARN_ON_ONCE(flags & ~TIMER_INIT_FLAGS))<br>flags &= TIMER_INIT_FLAGS;<br>timer->flags = flags | raw_smp_processor_id();<br>lockdep_init_map(&timer->lockdep_map, name, key, <span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure></li><li><p>from_timer 是通过 contianer_of 使用 timer_list* 的地址,获得它所在的结构体基地址(即rxe_qp*)</p></li></ol></li></ol><h2 id="2-retrans-timer重传定时器"><a href="#2-retrans-timer重传定时器" class="headerlink" title="2. retrans_timer重传定时器"></a>2. <code>retrans_timer</code>重传定时器</h2><ol><li><p>设置重传时间:用户 <strong>ibv_modify_qp</strong> 的时候,传入的 timeout 代表重传的超时时间</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_qp.c</span><br><span class="hljs-comment">/* called by the modify qp verb */</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_qp_from_attr</span><span class="hljs-params">(struct rxe_qp *qp, struct ib_qp_attr *attr, <span class="hljs-keyword">int</span> mask,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ib_udata *udata)</span></span><br><span class="hljs-function"></span>{<br>...<br><span class="hljs-keyword">if</span> (mask & IB_QP_TIMEOUT) {<br>qp->attr.timeout = attr->timeout;<br><span class="hljs-keyword">if</span> (attr->timeout == <span class="hljs-number">0</span>) {<br>qp->qp_timeout_jiffies = <span class="hljs-number">0</span>;<br>} <span class="hljs-keyword">else</span> {<br><span class="hljs-comment">/* According to the spec, timeout = 4.096 * 2 ^ attr->timeout [us] */</span><br><span class="hljs-keyword">int</span> j = nsecs_to_jiffies(<span class="hljs-number">4096ULL</span> << attr->timeout);<br><br>qp->qp_timeout_jiffies = j ? j : <span class="hljs-number">1</span>;<br>}<br>}<br>...<br>}<br></code></pre></td></tr></table></figure><ul><li><p><strong>ibv_modify_qp</strong> verbs 参考 <a href="https://www.rdmamojo.com/2013/01/12/ibv_modify_qp/">https://www.rdmamojo.com/2013/01/12/ibv_modify_qp/</a></p></li><li><p>IB spec 1.3 中关于重传时间的定义:Sec. 9.7.6.1.3 DETECTING LOST ACKNOWLEDGE MESSAGES AND TIMEOUTS</p><p> <img src="/img/rdma-stack-03/IBspec-Sec9-7-6-1-3.png" alt="Untitled"></p></li></ul></li><li><p>启动重传定时器</p><p> 每发送一个rxe packet后,启动重传定时器</p><p> mod_timer() 主要修改 timer->expires</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_req.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_requester</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br>... <br>err = rxe_xmit_packet(qp, &pkt, skb);<br>...<br><br>update_state(qp, &pkt);<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">update_state</span><span class="hljs-params">(struct rxe_qp *qp, struct rxe_pkt_info *pkt)</span></span><br><span class="hljs-function"></span>{<br>qp->req.opcode = pkt->opcode;<br><br><span class="hljs-keyword">if</span> (pkt->mask & RXE_END_MASK)<br>qp->req.wqe_index = queue_next_index(qp->sq.<span class="hljs-built_in">queue</span>,<br> qp->req.wqe_index);<br><br>qp->need_req_skb = <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">if</span> (qp->qp_timeout_jiffies && !timer_pending(&qp->retrans_timer))<br>mod_timer(&qp->retrans_timer,<br> jiffies + qp->qp_timeout_jiffies);<br>}<br> <br><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_comp.c</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">retransmit_timer</span><span class="hljs-params">(struct timer_list *t)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> from_timer(qp, t, retrans_timer);<br><br>pr_debug(<span class="hljs-string">"%s: fired for qp#%d\n"</span>, __func__, qp->elem.index);<br><br><span class="hljs-keyword">if</span> (qp->valid) {<br>qp->comp.timeout = <span class="hljs-number">1</span>;<br>rxe_run_task(&qp->comp.task, <span class="hljs-number">1</span>);<br>}<br>}<br></code></pre></td></tr></table></figure><ol><li>超时重传时,rxe_completer FSM中状态的转换<ul><li>COMPST_GET_ACK 状态中取 skb 为空,切到 COMPST_GET_WQE 态</li><li>COMPST_GET_WQE 中看到wqe→status == wqe_state_posted,切到COMPST_EXIT</li><li>COMPST_EXIT 看到 qp->comp.timeout_retry && wqe 满足条件,切到 COMPST_ERROR_RETRY</li><li>COMPST_ERROR_RETRY ,设置 qp->req.need_retry = 1,调用 rxe_req 做重传</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-keyword">enum</span> comp_state <span class="hljs-title">get_wqe</span><span class="hljs-params">(struct rxe_qp *qp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct rxe_pkt_info *pkt,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct rxe_send_wqe **wqe_p)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_send_wqe</span> *<span class="hljs-title">wqe</span>;</span><br><br><span class="hljs-comment">/* we come here whether or not we found a response packet to see if</span><br><span class="hljs-comment"> * there are any posted WQEs</span><br><span class="hljs-comment"> */</span><br>wqe = queue_head(qp->sq.<span class="hljs-built_in">queue</span>, QUEUE_TYPE_FROM_CLIENT);<br>*wqe_p = wqe;<br><br><span class="hljs-comment">/* no WQE or requester has not started it yet */</span><br><span class="hljs-keyword">if</span> (!wqe || wqe->state == wqe_state_posted)<br><span class="hljs-keyword">return</span> pkt ? COMPST_DONE : COMPST_EXIT;<br>...<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_completer</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br>...<br><span class="hljs-keyword">if</span> (qp->comp.timeout) {<br>qp->comp.timeout_retry = <span class="hljs-number">1</span>;<br>qp->comp.timeout = <span class="hljs-number">0</span>;<br>} <span class="hljs-keyword">else</span> {<br>qp->comp.timeout_retry = <span class="hljs-number">0</span>;<br>}<br>state = COMPST_GET_ACK;<br><br><span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>) {<br>pr_debug(<span class="hljs-string">"qp#%d state = %s\n"</span>, qp_num(qp),<br> comp_state_name[state]);<br><span class="hljs-keyword">switch</span> (state) {<br><span class="hljs-keyword">case</span> COMPST_GET_ACK:<br>skb = skb_dequeue(&qp->resp_pkts);<br><span class="hljs-keyword">if</span> (skb) {<br>pkt = SKB_TO_PKT(skb);<br>qp->comp.timeout_retry = <span class="hljs-number">0</span>;<br>}<br>state = COMPST_GET_WQE;<br><span class="hljs-keyword">break</span>;<br><br><span class="hljs-keyword">case</span> COMPST_GET_WQE:<br>state = get_wqe(qp, pkt, &wqe);<br><span class="hljs-keyword">break</span>;<br>...<br><span class="hljs-keyword">case</span> COMPST_EXIT:<br><span class="hljs-keyword">if</span> (qp->comp.timeout_retry && wqe) {<br>state = COMPST_ERROR_RETRY;<br><span class="hljs-keyword">break</span>;<br>}<br><br><span class="hljs-comment">/* re reset the timeout counter if</span><br><span class="hljs-comment"> * (1) QP is type RC</span><br><span class="hljs-comment"> * (2) the QP is alive</span><br><span class="hljs-comment"> * (3) there is a packet sent by the requester that</span><br><span class="hljs-comment"> * might be acked (we still might get spurious</span><br><span class="hljs-comment"> * timeouts but try to keep them as few as possible)</span><br><span class="hljs-comment"> * (4) the timeout parameter is set</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> ((qp_type(qp) == IB_QPT_RC) &&<br> (qp->req.state == QP_STATE_READY) &&<br> (psn_compare(qp->req.psn, qp->comp.psn) > <span class="hljs-number">0</span>) &&<br> qp->qp_timeout_jiffies)<br>mod_timer(&qp->retrans_timer,<br> jiffies + qp->qp_timeout_jiffies);<br><span class="hljs-keyword">goto</span> <span class="hljs-built_in">exit</span>;<br><span class="hljs-keyword">case</span> COMPST_ERROR_RETRY:<br><span class="hljs-comment">/* we come here if the retry timer fired and we did</span><br><span class="hljs-comment"> * not receive a response packet. try to retry the send</span><br><span class="hljs-comment"> * queue if that makes sense and the limits have not</span><br><span class="hljs-comment"> * been exceeded. remember that some timeouts are</span><br><span class="hljs-comment"> * spurious since we do not reset the timer but kick</span><br><span class="hljs-comment"> * it down the road or let it expire</span><br><span class="hljs-comment"> */</span><br><br><span class="hljs-comment">/* there is nothing to retry in this case */</span><br><span class="hljs-keyword">if</span> (!wqe || (wqe->state == wqe_state_posted))<br><span class="hljs-keyword">goto</span> <span class="hljs-built_in">exit</span>;<br><br><span class="hljs-comment">/* if we've started a retry, don't start another</span><br><span class="hljs-comment"> * retry sequence, unless this is a timeout.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (qp->comp.started_retry &&<br> !qp->comp.timeout_retry)<br><span class="hljs-keyword">goto</span> done;<br><br><span class="hljs-keyword">if</span> (qp->comp.retry_cnt > <span class="hljs-number">0</span>) {<br><span class="hljs-keyword">if</span> (qp->comp.retry_cnt != <span class="hljs-number">7</span>)<br>qp->comp.retry_cnt--;<br><br><span class="hljs-comment">/* no point in retrying if we have already</span><br><span class="hljs-comment"> * seen the last ack that the requester could</span><br><span class="hljs-comment"> * have caused</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (psn_compare(qp->req.psn,<br>qp->comp.psn) > <span class="hljs-number">0</span>) {<br><span class="hljs-comment">/* tell the requester to retry the</span><br><span class="hljs-comment"> * send queue next time around</span><br><span class="hljs-comment"> */</span><br>rxe_counter_inc(rxe,<br>RXE_CNT_COMP_RETRY);<br>qp->req.need_retry = <span class="hljs-number">1</span>;<br>qp->comp.started_retry = <span class="hljs-number">1</span>;<br>rxe_run_task(&qp->req.task, <span class="hljs-number">0</span>);<br>}<br><span class="hljs-keyword">goto</span> done;<br><br>} <span class="hljs-keyword">else</span> {<br>rxe_counter_inc(rxe, RXE_CNT_RETRY_EXCEEDED);<br>wqe->status = IB_WC_RETRY_EXC_ERR;<br>state = COMPST_ERROR;<br>}<br><span class="hljs-keyword">break</span>;<br>...<br>}<br></code></pre></td></tr></table></figure> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_req.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_requester</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br>...<br><br><span class="hljs-comment">/* we come here if the retransmit timer has fired</span><br><span class="hljs-comment"> * or if the rnr timer has fired. If the retransmit</span><br><span class="hljs-comment"> * timer fires while we are processing an RNR NAK wait</span><br><span class="hljs-comment"> * until the rnr timer has fired before starting the</span><br><span class="hljs-comment"> * retry flow</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (unlikely(qp->req.need_retry && !qp->req.wait_for_rnr_timer)) {<br>req_retry(qp);<br>qp->req.need_retry = <span class="hljs-number">0</span>;<br>}<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">req_retry</span><span class="hljs-params">(struct rxe_qp *qp)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_send_wqe</span> *<span class="hljs-title">wqe</span>;</span><br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> wqe_index;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> mask;<br><span class="hljs-keyword">int</span> npsn;<br><span class="hljs-keyword">int</span> first = <span class="hljs-number">1</span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_queue</span> *<span class="hljs-title">q</span> =</span> qp->sq.<span class="hljs-built_in">queue</span>;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> cons;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> prod;<br><br>cons = queue_get_consumer(q, QUEUE_TYPE_FROM_CLIENT);<br>prod = queue_get_producer(q, QUEUE_TYPE_FROM_CLIENT);<br><br>qp->req.wqe_index= cons;<br>qp->req.psn= qp->comp.psn;<br>qp->req.opcode= <span class="hljs-number">-1</span>;<br><br><span class="hljs-keyword">for</span> (wqe_index = cons; wqe_index != prod;<br>wqe_index = queue_next_index(q, wqe_index)) {<br>wqe = queue_addr_from_index(qp->sq.<span class="hljs-built_in">queue</span>, wqe_index);<br>mask = wr_opcode_mask(wqe->wr.opcode, qp);<br><br><span class="hljs-keyword">if</span> (wqe->state == wqe_state_posted)<br><span class="hljs-keyword">break</span>;<br><br><span class="hljs-keyword">if</span> (wqe->state == wqe_state_done)<br><span class="hljs-keyword">continue</span>;<br><br>wqe->iova = (mask & WR_ATOMIC_MASK) ?<br> wqe->wr.wr.atomic.remote_addr :<br> (mask & WR_READ_OR_WRITE_MASK) ?<br> wqe->wr.wr.rdma.remote_addr :<br> <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">if</span> (!first || (mask & WR_READ_MASK) == <span class="hljs-number">0</span>) {<br>wqe->dma.resid = wqe->dma.length;<br>wqe->dma.cur_sge = <span class="hljs-number">0</span>;<br>wqe->dma.sge_offset = <span class="hljs-number">0</span>;<br>}<br><br><span class="hljs-keyword">if</span> (first) {<br>first = <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">if</span> (mask & WR_WRITE_OR_SEND_MASK) {<br>npsn = (qp->comp.psn - wqe->first_psn) &<br>BTH_PSN_MASK;<br>retry_first_write_send(qp, wqe, npsn);<br>}<br><br><span class="hljs-keyword">if</span> (mask & WR_READ_MASK) {<br>npsn = (wqe->dma.length - wqe->dma.resid) /<br>qp->mtu;<br>wqe->iova += npsn * qp->mtu;<br>}<br>}<br><br>wqe->state = wqe_state_posted;<br>}<br>}<br></code></pre></td></tr></table></figure><p> req_retry 中根据Send Queue中从 consumer index到 producer index,读WQE</p><p> consumer index 只有当收到ACK包的时候才更新</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * IBA Spec. Section 10.7.3.1 SIGNALED COMPLETIONS</span><br><span class="hljs-comment"> * ---------8<---------8<-------------</span><br><span class="hljs-comment"> * ...Note that if a completion error occurs, a Work Completion</span><br><span class="hljs-comment"> * will always be generated, even if the signaling</span><br><span class="hljs-comment"> * indicator requests an Unsignaled Completion.</span><br><span class="hljs-comment"> * ---------8<---------8<-------------</span><br><span class="hljs-comment"> */</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">do_complete</span><span class="hljs-params">(struct rxe_qp *qp, struct rxe_send_wqe *wqe)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_dev</span> *<span class="hljs-title">rxe</span> =</span> to_rdev(qp->ibqp.device);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_cqe</span> <span class="hljs-title">cqe</span>;</span><br><span class="hljs-keyword">bool</span> post;<br><br><span class="hljs-comment">/* do we need to post a completion */</span><br>post = ((qp->sq_sig_type == IB_SIGNAL_ALL_WR) ||<br>(wqe->wr.send_flags & IB_SEND_SIGNALED) ||<br>wqe->status != IB_WC_SUCCESS);<br><br><span class="hljs-keyword">if</span> (post)<br>make_send_cqe(qp, wqe, &cqe);<br><br>queue_advance_consumer(qp->sq.<span class="hljs-built_in">queue</span>, QUEUE_TYPE_FROM_CLIENT);<br><br>...<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h2 id="3-rnrnak-timer计时器"><a href="#3-rnrnak-timer计时器" class="headerlink" title="3. rnrnak_timer计时器"></a>3. <code>rnrnak_timer</code>计时器</h2><ol><li>收到RNR NAK包后,启动 rnr_nak_timer <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_completer</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br>...<br><span class="hljs-keyword">case</span> COMPST_RNR_RETRY:<br><span class="hljs-comment">/* we come here if we received an RNR NAK */</span><br><span class="hljs-keyword">if</span> (qp->comp.rnr_retry > <span class="hljs-number">0</span>) {<br><span class="hljs-keyword">if</span> (qp->comp.rnr_retry != <span class="hljs-number">7</span>)<br>qp->comp.rnr_retry--;<br><br><span class="hljs-comment">/* don't start a retry flow until the</span><br><span class="hljs-comment"> * rnr timer has fired</span><br><span class="hljs-comment"> */</span><br>qp->req.wait_for_rnr_timer = <span class="hljs-number">1</span>;<br>pr_debug(<span class="hljs-string">"qp#%d set rnr nak timer\n"</span>,<br> qp_num(qp));<br>mod_timer(&qp->rnr_nak_timer,<br> jiffies + rnrnak_jiffies(aeth_syn(pkt)<br>& ~AETH_TYPE_MASK));<br><span class="hljs-keyword">goto</span> <span class="hljs-built_in">exit</span>;<br>} <span class="hljs-keyword">else</span> {<br>rxe_counter_inc(rxe,<br>RXE_CNT_RNR_RETRY_EXCEEDED);<br>wqe->status = IB_WC_RNR_RETRY_EXC_ERR;<br>state = COMPST_ERROR;<br>}<br><span class="hljs-keyword">break</span>;<br>...<br>}<br><br><span class="hljs-comment">// drivers/infiniband/sw/rxe/rxe_req.c</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">rnr_nak_timer</span><span class="hljs-params">(struct timer_list *t)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> from_timer(qp, t, rnr_nak_timer);<br><br>pr_debug(<span class="hljs-string">"%s: fired for qp#%d\n"</span>, __func__, qp_num(qp));<br><br><span class="hljs-comment">/* request a send queue retry */</span><br>qp->req.need_retry = <span class="hljs-number">1</span>;<br>qp->req.wait_for_rnr_timer = <span class="hljs-number">0</span>;<br>rxe_run_task(&qp->req.task, <span class="hljs-number">1</span>);<br>}<br></code></pre></td></tr></table></figure> 之后流程依然是rxe_requester→ req_retry</li></ol>]]></content>
<tags>
<tag>RDMA</tag>
<tag>libRXE</tag>
<tag>Soft-ROCE</tag>
</tags>
</entry>
<entry>
<title>RDMA-RoCEv2 ibv_reg_mr 分析</title>
<link href="/202208/rdma-stack-02/"/>
<url>/202208/rdma-stack-02/</url>
<content type="html"><![CDATA[<h2 id="1-用户程序调用-IBVerbs"><a href="#1-用户程序调用-IBVerbs" class="headerlink" title="1. 用户程序调用 IBVerbs"></a>1. 用户程序调用 IBVerbs</h2><ol><li><p>用户调用的ibv_reg_mr中包含四个参数,如下所示</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function">struct ibv_mr *<span class="hljs-title">ibv_reg_mr</span><span class="hljs-params">(struct ibv_pd *pd, <span class="hljs-keyword">void</span> *addr,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">size_t</span> length, <span class="hljs-keyword">enum</span> ibv_access_flags acces</span></span><br></code></pre></td></tr></table></figure><p> <a href="https://www.rdmamojo.com/2012/09/07/ibv_reg_mr/">https://www.rdmamojo.com/2012/09/07/ibv_reg_mr/</a></p><table><thead><tr><th>Name</th><th>Direction</th><th>Description</th></tr></thead><tbody><tr><td>pd</td><td>in</td><td>Protection Domain that was returned from ibv_alloc_pd()</td></tr><tr><td>addr</td><td>in</td><td>The start address of the virtual contiguous memory block</td></tr><tr><td>length</td><td>in</td><td>Size of the memory block to register, in bytes. This value must be at least 1 and less than dev_cap.max_mr_size</td></tr><tr><td>access</td><td>in</td><td>Requested access permissions for the memory region</td></tr></tbody></table></li><li><p>在当前的rdma-core实现下,会实际调用ibv_reg_mr_iova2,传入的addr/iova两个参数相等</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/verbs.c</span><br><span class="hljs-meta">#<span class="hljs-meta-keyword">undef</span> ibv_reg_mr</span><br>LATEST_SYMVER_FUNC(ibv_reg_mr, <span class="hljs-number">1</span>_1, <span class="hljs-string">"IBVERBS_1.1"</span>,<br> struct ibv_mr *,<br> struct ibv_pd *pd, <span class="hljs-keyword">void</span> *addr,<br> <span class="hljs-keyword">size_t</span> length, <span class="hljs-keyword">int</span> access)<br>{<br><span class="hljs-keyword">return</span> ibv_reg_mr_iova2(pd, addr, length, (<span class="hljs-keyword">uintptr_t</span>)addr, access);<br>}<br><br><span class="hljs-function">struct ibv_mr *<span class="hljs-title">ibv_reg_mr_iova2</span><span class="hljs-params">(struct ibv_pd *pd, <span class="hljs-keyword">void</span> *addr, <span class="hljs-keyword">size_t</span> length,</span></span><br><span class="hljs-params"><span class="hljs-function"><span class="hljs-keyword">uint64_t</span> iova, <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> access)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_device</span> *<span class="hljs-title">device</span> =</span> verbs_get_device(pd->context->device);<br><span class="hljs-keyword">bool</span> odp_mr = access & IBV_ACCESS_ON_DEMAND;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_mr</span> *<span class="hljs-title">mr</span>;</span><br><br><span class="hljs-keyword">if</span> (!(device->core_support & IB_UVERBS_CORE_SUPPORT_OPTIONAL_MR_ACCESS))<br>access &= ~IBV_ACCESS_OPTIONAL_RANGE;<br><br><span class="hljs-keyword">if</span> (!odp_mr && ibv_dontfork_range(addr, length))<br><span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br><br>mr = get_ops(pd->context)->reg_mr(pd, addr, length, iova, access);<br><span class="hljs-keyword">if</span> (mr) {<br>mr->context = pd->context;<br>mr->pd = pd;<br>mr->addr = addr;<br>mr->length = length;<br>} <span class="hljs-keyword">else</span> {<br><span class="hljs-keyword">if</span> (!odp_mr)<br>ibv_dofork_range(addr, length);<br>}<br><br><span class="hljs-keyword">return</span> mr;<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="2-用户态libRXE"><a href="#2-用户态libRXE" class="headerlink" title="2. 用户态libRXE"></a>2. 用户态libRXE</h2><ol><li><p>libRXE中,rxe_reg_mr 函数指针注册为 <code>ops->reg_mr</code></p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_context_ops</span> <span class="hljs-title">rxe_ctx_ops</span> =</span> {<br>...<br>.reg_mr = rxe_reg_mr,<br>.dereg_mr = rxe_dereg_mr,<br>.alloc_mw = rxe_alloc_mw,<br>.dealloc_mw = rxe_dealloc_mw,<br>.bind_mw = rxe_bind_mw,<br>...<br>};<br></code></pre></td></tr></table></figure></li><li><p>rxe_reg_mr 实现</p><ul><li>申请一个verbs_mr结构体空间</li><li>通过ibv_cmd_reg_mr执行系统调用。注意这里 addr 和 hca_va 参数是相等的</li></ul> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> struct ibv_mr *<span class="hljs-title">rxe_reg_mr</span><span class="hljs-params">(struct ibv_pd *pd, <span class="hljs-keyword">void</span> *addr, <span class="hljs-keyword">size_t</span> length,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">uint64_t</span> hca_va, <span class="hljs-keyword">int</span> access)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_mr</span> *<span class="hljs-title">vmr</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_reg_mr</span> <span class="hljs-title">cmd</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_reg_mr_resp</span> <span class="hljs-title">resp</span>;</span><br><span class="hljs-keyword">int</span> ret;<br><br>vmr = <span class="hljs-built_in">calloc</span>(<span class="hljs-number">1</span>, <span class="hljs-built_in"><span class="hljs-keyword">sizeof</span></span>(*vmr));<br><span class="hljs-keyword">if</span> (!vmr)<br><span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br><br>ret = <span class="hljs-built_in">ibv_cmd_reg_mr</span>(pd, addr, length, hca_va, access, vmr, &cmd,<br> <span class="hljs-built_in"><span class="hljs-keyword">sizeof</span></span>(cmd), &resp, <span class="hljs-built_in"><span class="hljs-keyword">sizeof</span></span>(resp));<br><span class="hljs-keyword">if</span> (ret) {<br><span class="hljs-built_in">free</span>(vmr);<br><span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br>}<br><br><span class="hljs-keyword">return</span> &vmr->ibv_mr;<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="3-发起write系统调用"><a href="#3-发起write系统调用" class="headerlink" title="3. 发起write系统调用"></a>3. 发起write系统调用</h2><ol><li>ibv_cmd_reg_mr 实现<ul><li>将传入参数写入到cmd结构体中</li><li>发起write系统调用, 发起的cmd名称为 IB_USER_VERBS_CMD_REG_MR</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/cmd.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">ibv_cmd_reg_mr</span><span class="hljs-params">(struct ibv_pd *pd, <span class="hljs-keyword">void</span> *addr, <span class="hljs-keyword">size_t</span> length,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">uint64_t</span> hca_va, <span class="hljs-keyword">int</span> access,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct verbs_mr *vmr, struct ibv_reg_mr *cmd,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">size_t</span> cmd_size,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ib_uverbs_reg_mr_resp *resp, <span class="hljs-keyword">size_t</span> resp_size)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> ret;<br><br>cmd->start = (<span class="hljs-keyword">uintptr_t</span>) addr;<br>cmd->length = length;<br><span class="hljs-comment">/* On demand access and entire address space means implicit.</span><br><span class="hljs-comment"> * In that case set the value in the command to what kernel expects.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (access & IBV_ACCESS_ON_DEMAND) {<br><span class="hljs-keyword">if</span> (length == SIZE_MAX && addr) {<br>errno = EINVAL;<br><span class="hljs-keyword">return</span> EINVAL;<br>}<br><span class="hljs-keyword">if</span> (length == SIZE_MAX)<br>cmd->length = UINT64_MAX;<br>}<br><br>cmd->hca_va = hca_va;<br>cmd->pd_handle = pd->handle;<br>cmd->access_flags = access;<br><br>ret = execute_cmd_write(pd->context, IB_USER_VERBS_CMD_REG_MR, cmd,<br>cmd_size, resp, resp_size);<br><span class="hljs-keyword">if</span> (ret)<br><span class="hljs-keyword">return</span> ret;<br><br>vmr->ibv_mr.handle = resp->mr_handle;<br>vmr->ibv_mr.lkey = resp->lkey;<br>vmr->ibv_mr.rkey = resp->rkey;<br>vmr->ibv_mr.context = pd->context;<br>vmr->mr_type = IBV_MR_TYPE_MR;<br>vmr->access = access;<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><ul><li>这里补充下 ib_uverbs_reg_mr 系统调用传入的结构体,以及 ib_uverbs_reg_mr_resp 传回结构体</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_reg_mr</span> {</span><br>__aligned_u64 response;<br>__aligned_u64 start;<br>__aligned_u64 length;<br>__aligned_u64 hca_va;<br>__u32 pd_handle;<br>__u32 access_flags;<br>__aligned_u64 driver_data[<span class="hljs-number">0</span>];<br>};<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_reg_mr_resp</span> {</span><br>__u32 mr_handle;<br>__u32 lkey;<br>__u32 rkey;<br>__u32 driver_data[<span class="hljs-number">0</span>];<br>};<br></code></pre></td></tr></table></figure></li></ol><h2 id="4-内核中rdma-core处理系统调用"><a href="#4-内核中rdma-core处理系统调用" class="headerlink" title="4. 内核中rdma-core处理系统调用"></a>4. 内核中rdma-core处理系统调用</h2><ol><li><p>IB_USER_VERBS_CMD_REG_MR 方法的write系统调用,由 ib_uverbs_reg_mr 函数处理</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/core/uverbs_cmd.c</span><br>DECLARE_UVERBS_WRITE(<br>IB_USER_VERBS_CMD_REG_MR,<br>ib_uverbs_reg_mr,<br>UAPI_DEF_WRITE_UDATA_IO(struct ib_uverbs_reg_mr,<br>struct ib_uverbs_reg_mr_resp),<br>UAPI_DEF_METHOD_NEEDS_FN(reg_user_mr)),<br></code></pre></td></tr></table></figure></li><li><p>ib_uverbs_reg_mr 实现</p><ul><li>取出传入的ib_uverbs_reg_mr结构体,这里检查 cmd.start 和 cmd.hca_va 两个地址是否相等</li><li>执行实际的 reg_user_mr 函数</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/core/uverbs_cmd.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ib_uverbs_reg_mr</span><span class="hljs-params">(struct uverbs_attr_bundle *attrs)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_reg_mr_resp</span> <span class="hljs-title">resp</span> =</span> {};<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_reg_mr</span> <span class="hljs-title">cmd</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uobject</span> *<span class="hljs-title">uobj</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_pd</span> *<span class="hljs-title">pd</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_mr</span> *<span class="hljs-title">mr</span>;</span><br><span class="hljs-keyword">int</span> ret;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_device</span> *<span class="hljs-title">ib_dev</span>;</span><br><br>ret = uverbs_request(attrs, &cmd, <span class="hljs-keyword">sizeof</span>(cmd));<br><span class="hljs-keyword">if</span> (ret)<br><span class="hljs-keyword">return</span> ret;<br><br><span class="hljs-keyword">if</span> ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))<br><span class="hljs-keyword">return</span> -EINVAL;<br><br>uobj = uobj_alloc(UVERBS_OBJECT_MR, attrs, &ib_dev);<br><span class="hljs-keyword">if</span> (IS_ERR(uobj))<br><span class="hljs-keyword">return</span> PTR_ERR(uobj);<br><br>ret = ib_check_mr_access(ib_dev, cmd.access_flags);<br><span class="hljs-keyword">if</span> (ret)<br><span class="hljs-keyword">goto</span> err_free;<br><br>pd = uobj_get_obj_read(pd, UVERBS_OBJECT_PD, cmd.pd_handle, attrs);<br><span class="hljs-keyword">if</span> (!pd) {<br>ret = -EINVAL;<br><span class="hljs-keyword">goto</span> err_free;<br>}<br><br>mr = pd->device->ops.reg_user_mr(pd, cmd.start, cmd.length, cmd.hca_va,<br> cmd.access_flags,<br> &attrs->driver_udata);<br><span class="hljs-keyword">if</span> (IS_ERR(mr)) {<br>ret = PTR_ERR(mr);<br><span class="hljs-keyword">goto</span> err_put;<br>}<br><br>mr->device = pd->device;<br>mr->pd = pd;<br>mr->type = IB_MR_TYPE_USER;<br>mr->dm = <span class="hljs-literal">NULL</span>;<br>mr->sig_attrs = <span class="hljs-literal">NULL</span>;<br>mr->uobject = uobj;<br>atomic_inc(&pd->usecnt);<br>mr->iova = cmd.hca_va;<br><br>rdma_restrack_new(&mr->res, RDMA_RESTRACK_MR);<br>rdma_restrack_set_name(&mr->res, <span class="hljs-literal">NULL</span>);<br>rdma_restrack_add(&mr->res);<br><br>uobj->object = mr;<br>uobj_put_obj_read(pd);<br>uobj_finalize_uobj_create(uobj, attrs);<br><br>resp.lkey = mr->lkey;<br>resp.rkey = mr->rkey;<br>resp.mr_handle = uobj->id;<br><span class="hljs-keyword">return</span> uverbs_response(attrs, &resp, <span class="hljs-keyword">sizeof</span>(resp));<br><br>err_put:<br>uobj_put_obj_read(pd);<br>err_free:<br>uobj_alloc_abort(uobj, attrs);<br><span class="hljs-keyword">return</span> ret;<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="5-RXE内核模块中进行实际的Memory-Region注册"><a href="#5-RXE内核模块中进行实际的Memory-Region注册" class="headerlink" title="5. RXE内核模块中进行实际的Memory Region注册"></a>5. RXE内核模块中进行实际的Memory Region注册</h2><ol><li><p>reg_user_mr 函数的注册</p><ul><li>由此可知,RXE内核模块中实际上调用了 rxe_reg_user_mr 进行MR的注册</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_verbs.c</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_device_ops</span> <span class="hljs-title">rxe_dev_ops</span> =</span> {<br>.owner = THIS_MODULE,<br>.driver_id = RDMA_DRIVER_RXE,<br>.uverbs_abi_ver = RXE_UVERBS_ABI_VERSION,<br>...<br>.alloc_mr = rxe_alloc_mr,<br>.alloc_mw = rxe_alloc_mw,<br>...<br>.dealloc_mw = rxe_dealloc_mw,<br>...<br>.dereg_mr = rxe_dereg_mr,<br>...<br>.get_dma_mr = rxe_get_dma_mr,<br>...<br>.map_mr_sg = rxe_map_mr_sg,<br>...<br>.reg_user_mr = rxe_reg_user_mr,<br>...<br>};<br></code></pre></td></tr></table></figure></li><li><p>rxe_reg_user_mr 的实现</p><ul><li>参数 start/iova 即为 Memory region的起始地址,length 为其长度</li><li><code>rxe_alloc</code> 在<code>rxe->mr_pool</code>中申请一个rxe_mr struct 空间</li><li>调用 <code>rxe_mr_init_user</code>, 传入的 mr 结构体指针刚申请空间,无实际内容</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_verbs.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> struct ib_mr *<span class="hljs-title">rxe_reg_user_mr</span><span class="hljs-params">(struct ib_pd *ibpd,</span></span><br><span class="hljs-params"><span class="hljs-function"> u64 start,</span></span><br><span class="hljs-params"><span class="hljs-function"> u64 length,</span></span><br><span class="hljs-params"><span class="hljs-function"> u64 iova,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">int</span> access, struct ib_udata *udata)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> err;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_dev</span> *<span class="hljs-title">rxe</span> =</span> to_rdev(ibpd->device);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_pd</span> *<span class="hljs-title">pd</span> =</span> to_rpd(ibpd);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_mr</span> *<span class="hljs-title">mr</span>;</span><br><br>mr = rxe_alloc(&rxe->mr_pool);<br><span class="hljs-keyword">if</span> (!mr) {<br>err = -ENOMEM;<br><span class="hljs-keyword">goto</span> err2;<br>}<br><br>rxe_get(pd);<br><br>err = rxe_mr_init_user(pd, start, length, iova, access, mr);<br><span class="hljs-keyword">if</span> (err)<br><span class="hljs-keyword">goto</span> err3;<br><br><span class="hljs-keyword">return</span> &mr->ibmr;<br><br>err3:<br>rxe_put(pd);<br>rxe_put(mr);<br>err2:<br><span class="hljs-keyword">return</span> ERR_PTR(err);<br>}<br></code></pre></td></tr></table></figure></li><li><p>rxe_mr_init_user的实现</p><ol><li>调用 ib_umem_get 来完成内存页面pin操作 + 将pin住的内存页面组织为DMA SG List</li><li>将 ib_umem 中的DMA SG List复制为 rxe_map_set</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_mr.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_mr_init_user</span><span class="hljs-params">(struct rxe_pd *pd, u64 start, u64 length, u64 iova,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">int</span> access, struct rxe_mr *mr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_map_set</span>*<span class="hljs-title">set</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_map</span>**<span class="hljs-title">map</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_phys_buf</span>*<span class="hljs-title">buf</span> =</span> <span class="hljs-literal">NULL</span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_umem</span>*<span class="hljs-title">umem</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sg_page_iter</span><span class="hljs-title">sg_iter</span>;</span><br><span class="hljs-keyword">int</span>num_buf;<br><span class="hljs-keyword">void</span>*vaddr;<br><span class="hljs-keyword">int</span> err;<br><br>umem = ib_umem_get(pd->ibpd.device, start, length, access);<br><span class="hljs-keyword">if</span> (IS_ERR(umem)) {<br>pr_warn(<span class="hljs-string">"%s: Unable to pin memory region err = %d\n"</span>,<br>__func__, (<span class="hljs-keyword">int</span>)PTR_ERR(umem));<br>err = PTR_ERR(umem);<br><span class="hljs-keyword">goto</span> err_out;<br>}<br><br>num_buf = ib_umem_num_pages(umem);<br><br>rxe_mr_init(access, mr);<br><br>err = rxe_mr_alloc(mr, num_buf, <span class="hljs-number">0</span>);<br><span class="hljs-keyword">if</span> (err) {<br>pr_warn(<span class="hljs-string">"%s: Unable to allocate memory for map\n"</span>,<br>__func__);<br><span class="hljs-keyword">goto</span> err_release_umem;<br>}<br><br><span class="hljs-built_in">set</span> = mr->cur_map_set;<br><span class="hljs-built_in">set</span>->page_shift = PAGE_SHIFT;<br><span class="hljs-built_in">set</span>->page_mask = PAGE_SIZE - <span class="hljs-number">1</span>;<br><br>num_buf = <span class="hljs-number">0</span>;<br><span class="hljs-built_in">map</span> = <span class="hljs-built_in">set</span>-><span class="hljs-built_in">map</span>;<br><br><span class="hljs-keyword">if</span> (length > <span class="hljs-number">0</span>) {<br>buf = <span class="hljs-built_in">map</span>[<span class="hljs-number">0</span>]->buf;<br><br>for_each_sgtable_page (&umem->sgt_append.sgt, &sg_iter, <span class="hljs-number">0</span>) {<br><span class="hljs-keyword">if</span> (num_buf >= RXE_BUF_PER_MAP) {<br><span class="hljs-built_in">map</span>++;<br>buf = <span class="hljs-built_in">map</span>[<span class="hljs-number">0</span>]->buf;<br>num_buf = <span class="hljs-number">0</span>;<br>}<br><br>vaddr = page_address(sg_page_iter_page(&sg_iter));<br><span class="hljs-keyword">if</span> (!vaddr) {<br>pr_warn(<span class="hljs-string">"%s: Unable to get virtual address\n"</span>,<br>__func__);<br>err = -ENOMEM;<br><span class="hljs-keyword">goto</span> err_release_umem;<br>}<br><br>buf->addr = (<span class="hljs-keyword">uintptr_t</span>)vaddr;<br>buf->size = PAGE_SIZE;<br>num_buf++;<br>buf++;<br>}<br>}<br><br>mr->ibmr.pd = &pd->ibpd;<br>mr->umem = umem;<br>mr->access = access;<br>mr->state = RXE_MR_STATE_VALID;<br>mr->type = IB_MR_TYPE_USER;<br><br><span class="hljs-built_in">set</span>->length = length;<br><span class="hljs-built_in">set</span>->iova = iova;<br><span class="hljs-built_in">set</span>->va = start;<br><span class="hljs-built_in">set</span>->offset = ib_umem_offset(umem);<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br><br>err_release_umem:<br>ib_umem_release(umem);<br>err_out:<br><span class="hljs-keyword">return</span> err;<br>}<br></code></pre></td></tr></table></figure></li><li><p>ib_umem_get的实现</p><ul><li>ib_umem 结构体记录传入的起始地址/长度,以及当前用户线程的 mm_struct</li><li>将 addr 开始的 npages 个页面pin在内存中</li><li><code>pin_user_pages_fast</code><ul><li>参考: <a href="https://lwn.net/Articles/807108/">Explicit pinning of user-space pages</a></li></ul></li><li>将被pin住的n个页面,组织为DMA SG List,保存在 umem ->sgt_append 中<ul><li>调用 sg_alloc_append_table_from_pages 来实现这个目的</li></ul></li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/core/umem.c</span><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * ib_umem_get - Pin and DMA map userspace memory.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * @device: IB device to connect UMEM</span><br><span class="hljs-comment"> * @addr: userspace virtual address to start at</span><br><span class="hljs-comment"> * @size: length of region to pin</span><br><span class="hljs-comment"> * @access: IB_ACCESS_xxx flags for memory being pinned</span><br><span class="hljs-comment"> */</span><br><span class="hljs-function">struct ib_umem *<span class="hljs-title">ib_umem_get</span><span class="hljs-params">(struct ib_device *device, <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> addr,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">size_t</span> size, <span class="hljs-keyword">int</span> access)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_umem</span> *<span class="hljs-title">umem</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">page</span> **<span class="hljs-title">page_list</span>;</span><br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> lock_limit;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> new_pinned;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> cur_base;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> dma_attr = <span class="hljs-number">0</span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mm_struct</span> *<span class="hljs-title">mm</span>;</span><br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> npages;<br><span class="hljs-keyword">int</span> pinned, ret;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> gup_flags = FOLL_WRITE;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * If the combination of the addr and size requested for this memory</span><br><span class="hljs-comment"> * region causes an integer overflow, return error.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (((addr + size) < addr) ||<br> PAGE_ALIGN(addr + size) < (addr + size))<br><span class="hljs-keyword">return</span> ERR_PTR(-EINVAL);<br><br><span class="hljs-keyword">if</span> (!can_do_mlock())<br><span class="hljs-keyword">return</span> ERR_PTR(-EPERM);<br><br><span class="hljs-keyword">if</span> (access & IB_ACCESS_ON_DEMAND)<br><span class="hljs-keyword">return</span> ERR_PTR(-EOPNOTSUPP);<br><br>umem = kzalloc(<span class="hljs-keyword">sizeof</span>(*umem), GFP_KERNEL);<br><span class="hljs-keyword">if</span> (!umem)<br><span class="hljs-keyword">return</span> ERR_PTR(-ENOMEM);<br>umem->ibdev = device;<br>umem->length = size;<br>umem->address = addr;<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Drivers should call ib_umem_find_best_pgsz() to set the iova</span><br><span class="hljs-comment"> * correctly.</span><br><span class="hljs-comment"> */</span><br>umem->iova = addr;<br>umem->writable = ib_access_writable(access);<br>umem->owning_mm = mm = current->mm;<br>mmgrab(mm);<br><br>page_list = (struct page **) __get_free_page(GFP_KERNEL);<br><span class="hljs-keyword">if</span> (!page_list) {<br>ret = -ENOMEM;<br><span class="hljs-keyword">goto</span> umem_kfree;<br>}<br><br>npages = ib_umem_num_pages(umem);<br><span class="hljs-keyword">if</span> (npages == <span class="hljs-number">0</span> || npages > UINT_MAX) {<br>ret = -EINVAL;<br><span class="hljs-keyword">goto</span> out;<br>}<br><br>lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;<br><br>new_pinned = atomic64_add_return(npages, &mm->pinned_vm);<br><span class="hljs-keyword">if</span> (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {<br>atomic64_sub(npages, &mm->pinned_vm);<br>ret = -ENOMEM;<br><span class="hljs-keyword">goto</span> out;<br>}<br><br>cur_base = addr & PAGE_MASK;<br><br><span class="hljs-keyword">if</span> (!umem->writable)<br>gup_flags |= FOLL_FORCE;<br><br><span class="hljs-keyword">while</span> (npages) {<br>cond_resched();<br>pinned = pin_user_pages_fast(cur_base,<br> <span class="hljs-keyword">min_t</span>(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span>, npages,<br>PAGE_SIZE /<br><span class="hljs-keyword">sizeof</span>(struct page *)),<br> gup_flags | FOLL_LONGTERM, page_list);<br><span class="hljs-keyword">if</span> (pinned < <span class="hljs-number">0</span>) {<br>ret = pinned;<br><span class="hljs-keyword">goto</span> umem_release;<br>}<br><br>cur_base += pinned * PAGE_SIZE;<br>npages -= pinned;<br>ret = sg_alloc_append_table_from_pages(<br>&umem->sgt_append, page_list, pinned, <span class="hljs-number">0</span>,<br>pinned << PAGE_SHIFT, ib_dma_max_seg_size(device),<br>npages, GFP_KERNEL);<br><span class="hljs-keyword">if</span> (ret) {<br>unpin_user_pages_dirty_lock(page_list, pinned, <span class="hljs-number">0</span>);<br><span class="hljs-keyword">goto</span> umem_release;<br>}<br>}<br><br><span class="hljs-keyword">if</span> (access & IB_ACCESS_RELAXED_ORDERING)<br>dma_attr |= DMA_ATTR_WEAK_ORDERING;<br><br>ret = ib_dma_map_sgtable_attrs(device, &umem->sgt_append.sgt,<br> DMA_BIDIRECTIONAL, dma_attr);<br><span class="hljs-keyword">if</span> (ret)<br><span class="hljs-keyword">goto</span> umem_release;<br><span class="hljs-keyword">goto</span> out;<br><br>umem_release:<br>__ib_umem_release(device, umem, <span class="hljs-number">0</span>);<br>atomic64_sub(ib_umem_num_pages(umem), &mm->pinned_vm);<br>out:<br>free_page((<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span>) page_list);<br>umem_kfree:<br><span class="hljs-keyword">if</span> (ret) {<br>mmdrop(umem->owning_mm);<br>kfree(umem);<br>}<br><span class="hljs-keyword">return</span> ret ? ERR_PTR(ret) : umem;<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="6-Kernel-中关于pin-memory页面相关流程"><a href="#6-Kernel-中关于pin-memory页面相关流程" class="headerlink" title="6. Kernel 中关于pin memory页面相关流程"></a>6. Kernel 中关于pin memory页面相关流程</h2><ol><li><p>整体思路</p><ul><li>保证N个虚拟地址连续的页面被分配,将每个物理页面的引用计数增大到 GUP_PIN_COUNTING_BIAS</li><li>page->refcount 被置为 GUP_PIN_COUNTING_BIAS 表示该页因为DMA的原因而被PIN住。(bit 10位为1)</li></ul></li><li><p>入口函数:pin_user_pages_fast</p><p> 从start开始的虚拟地址,pin住 nr_pages 个数的页面,每个页面的指针保存在 pages 数组中传回</p><p> 内核相关文档: <a href="https://www.kernel.org/doc/html/latest/core-api/pin_user_pages.html"><strong><strong>pin_user_pages() and related calls</strong></strong></a></p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/mm/gup.c</span><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * pin_user_pages_fast() - pin user pages in memory without taking locks</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * @start: starting user address</span><br><span class="hljs-comment"> * @nr_pages: number of pages from start to pin</span><br><span class="hljs-comment"> * @gup_flags: flags modifying pin behaviour</span><br><span class="hljs-comment"> * @pages: array that receives pointers to the pages pinned.</span><br><span class="hljs-comment"> * Should be at least nr_pages long.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * Nearly the same as get_user_pages_fast(), except that FOLL_PIN is set. See</span><br><span class="hljs-comment"> * get_user_pages_fast() for documentation on the function arguments, because</span><br><span class="hljs-comment"> * the arguments here are identical.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * FOLL_PIN means that the pages must be released via unpin_user_page(). Please</span><br><span class="hljs-comment"> * see Documentation/core-api/pin_user_pages.rst for further details.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">pin_user_pages_fast</span><span class="hljs-params">(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> start, <span class="hljs-keyword">int</span> nr_pages,</span></span><br><span class="hljs-params"><span class="hljs-function"><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> gup_flags, struct page **pages)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-comment">/* FOLL_GET and FOLL_PIN are mutually exclusive. */</span><br><span class="hljs-keyword">if</span> (WARN_ON_ONCE(gup_flags & FOLL_GET))<br><span class="hljs-keyword">return</span> -EINVAL;<br><br><span class="hljs-keyword">if</span> (WARN_ON_ONCE(!pages))<br><span class="hljs-keyword">return</span> -EINVAL;<br><br>gup_flags |= FOLL_PIN;<br><span class="hljs-keyword">return</span> internal_get_user_pages_fast(start, nr_pages, gup_flags, pages);<br>}<br>EXPORT_SYMBOL_GPL(pin_user_pages_fast);<br></code></pre></td></tr></table></figure></li><li><p>internal_get_user_pages_fast 函数分析</p><ul><li><p><code>current->mm->flags</code> 增加一个bit: MMF_HAS_PINNED</p></li><li><p>快速路径:页面已经分配出来</p><ul><li><p>模拟TLB miss情况,通过wake page table 最终分析出PTE entry中是否对应物理页,调用过程如下:</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs c">lockless_pages_from_mm<br>-- gup_pgd_range<br> -- gup_pud_range<br> -- gup_pud_range<br> -- gup_pmd_range<br> -- gup_pte_range<br> -- try_grab_folio(page, <span class="hljs-number">1</span>, flags); <span class="hljs-comment">// 这里会对page refcount + GUP_PIN_COUNTING_BIAS - 1</span><br></code></pre></td></tr></table></figure></li></ul></li><li><p>慢速路径:可能存在一些未建立映射的页面</p><ul><li>__get_user_pages</li></ul></li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">internal_get_user_pages_fast</span><span class="hljs-params">(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> start,</span></span><br><span class="hljs-params"><span class="hljs-function"><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> nr_pages,</span></span><br><span class="hljs-params"><span class="hljs-function"><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> gup_flags,</span></span><br><span class="hljs-params"><span class="hljs-function">struct page **pages)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> len, end;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> nr_pinned;<br><span class="hljs-keyword">int</span> ret;<br><br><span class="hljs-keyword">if</span> (WARN_ON_ONCE(gup_flags & ~(FOLL_WRITE | FOLL_LONGTERM |<br> FOLL_FORCE | FOLL_PIN | FOLL_GET |<br> FOLL_FAST_ONLY | FOLL_NOFAULT)))<br><span class="hljs-keyword">return</span> -EINVAL;<br><br><span class="hljs-keyword">if</span> (gup_flags & FOLL_PIN)<br>mm_set_has_pinned_flag(&current->mm->flags);<br><br><span class="hljs-keyword">if</span> (!(gup_flags & FOLL_FAST_ONLY))<br>might_lock_read(&current->mm->mmap_lock);<br><br>start = untagged_addr(start) & PAGE_MASK;<br>len = nr_pages << PAGE_SHIFT;<br><span class="hljs-keyword">if</span> (check_add_overflow(start, len, &end))<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br><span class="hljs-keyword">if</span> (unlikely(!access_ok((<span class="hljs-keyword">void</span> __user *)start, len)))<br><span class="hljs-keyword">return</span> -EFAULT;<br><br>nr_pinned = lockless_pages_from_mm(start, end, gup_flags, pages);<br><span class="hljs-keyword">if</span> (nr_pinned == nr_pages || gup_flags & FOLL_FAST_ONLY)<br><span class="hljs-keyword">return</span> nr_pinned;<br><br><span class="hljs-comment">/* Slow path: try to get the remaining pages with get_user_pages */</span><br>start += nr_pinned << PAGE_SHIFT;<br>pages += nr_pinned;<br>ret = __gup_longterm_unlocked(start, nr_pages - nr_pinned, gup_flags,<br> pages);<br><span class="hljs-keyword">if</span> (ret < <span class="hljs-number">0</span>) {<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * The caller has to unpin the pages we already pinned so</span><br><span class="hljs-comment"> * returning -errno is not an option</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (nr_pinned)<br><span class="hljs-keyword">return</span> nr_pinned;<br><span class="hljs-keyword">return</span> ret;<br>}<br><span class="hljs-keyword">return</span> ret + nr_pinned;<br>}<br></code></pre></td></tr></table></figure></li><li><p>快速路径:软件模拟TLB走页,把遍历到的每一个页面的page->refcount 增加 GUP_PIN_COUNTING_BIAS - 1</p><ul><li>五级页表下模拟TLB功能</li><li>五级页表相关参考<ul><li><strong><strong><a href="https://tinylab.org/lwn-717293/">LWN 717293: 五级页表</a></strong></strong></li><li><strong><a href="https://lwn.net/Articles/717293/">Five-level page tables</a></strong></li></ul></li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> <span class="hljs-title">lockless_pages_from_mm</span><span class="hljs-params">(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> start,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> end,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> gup_flags,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct page **pages)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> flags;<br><span class="hljs-keyword">int</span> nr_pinned = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">unsigned</span> seq;<br><br><span class="hljs-keyword">if</span> (!IS_ENABLED(CONFIG_HAVE_FAST_GUP) ||<br> !gup_fast_permitted(start, end))<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">if</span> (gup_flags & FOLL_PIN) {<br>seq = raw_read_seqcount(&current->mm->write_protect_seq);<br><span class="hljs-keyword">if</span> (seq & <span class="hljs-number">1</span>)<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Disable interrupts. The nested form is used, in order to allow full,</span><br><span class="hljs-comment"> * general purpose use of this routine.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * With interrupts disabled, we block page table pages from being freed</span><br><span class="hljs-comment"> * from under us. See struct mmu_table_batch comments in</span><br><span class="hljs-comment"> * include/asm-generic/tlb.h for more details.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * We do not adopt an rcu_read_lock() here as we also want to block IPIs</span><br><span class="hljs-comment"> * that come from THPs splitting.</span><br><span class="hljs-comment"> */</span><br>local_irq_save(flags);<br>gup_pgd_range(start, end, gup_flags, pages, &nr_pinned);<br>local_irq_restore(flags);<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * When pinning pages for DMA there could be a concurrent write protect</span><br><span class="hljs-comment"> * from fork() via copy_page_range(), in this case always fail fast GUP.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (gup_flags & FOLL_PIN) {<br><span class="hljs-keyword">if</span> (read_seqcount_retry(&current->mm->write_protect_seq, seq)) {<br>unpin_user_pages_lockless(pages, nr_pinned);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>} <span class="hljs-keyword">else</span> {<br>sanity_check_pinned_pages(pages, nr_pinned);<br>}<br>}<br><span class="hljs-keyword">return</span> nr_pinned;<br>}<br></code></pre></td></tr></table></figure> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">gup_pgd_range</span><span class="hljs-params">(<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> addr, <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> end,</span></span><br><span class="hljs-params"><span class="hljs-function"><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> flags, struct page **pages, <span class="hljs-keyword">int</span> *nr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> next;<br><span class="hljs-keyword">pgd_t</span> *pgdp;<br><br>pgdp = pgd_offset(current->mm, addr);<br><span class="hljs-keyword">do</span> {<br><span class="hljs-keyword">pgd_t</span> pgd = READ_ONCE(*pgdp);<br><br>next = pgd_addr_end(addr, end);<br><span class="hljs-keyword">if</span> (pgd_none(pgd))<br><span class="hljs-keyword">return</span>;<br><span class="hljs-keyword">if</span> (unlikely(pgd_huge(pgd))) {<br><span class="hljs-keyword">if</span> (!gup_huge_pgd(pgd, pgdp, addr, next, flags,<br> pages, nr))<br><span class="hljs-keyword">return</span>;<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (unlikely(is_hugepd(__hugepd(pgd_val(pgd))))) {<br><span class="hljs-keyword">if</span> (!gup_huge_pd(__hugepd(pgd_val(pgd)), addr,<br> PGDIR_SHIFT, next, flags, pages, nr))<br><span class="hljs-keyword">return</span>;<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!gup_p4d_range(pgdp, pgd, addr, next, flags, pages, nr))<br><span class="hljs-keyword">return</span>;<br>} <span class="hljs-keyword">while</span> (pgdp++, addr = next, addr != end);<br>}<br></code></pre></td></tr></table></figure></li><li><p>慢速路径:</p><ol><li><p>__get_user_pages:start对应的虚拟地址连续分配nr_pages个物理内存,核心处理思想就是强制显示手动触发page fault流程为其分配物理page,根据其start所属的vma类型处理主要分几种情况处理:</p></li><li><p><a href="https://zhikunhuo.blog.csdn.net/article/details/121620769"> 逻辑图示:</a></p><p> <img src="/img/rdma-stack-02/get_user_pages.png" alt="get_user_pages function"></p></li><li><p>流程讲解:这里参考了 <a href="https://zhikunhuo.blog.csdn.net/article/details/121620769">linux那些事之pin memory(get_user_pages())</a></p><ul><li>__get_user_pages()函数首先调用find_extend_vma()函数查找start所属的vma,find_extend_vma()相比find_vma多了一种功能就是如果查找到的vma不能包括start地址,则可以将vma进行扩展以使查找的vma能够包括start地址。</li><li>接下来要根据查找到的vma几种情况进行处理:<ul><li>vma为空即start地址查找不到对应的所属的vma,且该start地址属于gate are区域则 调用get_gate_page()<ul><li>找到用户空间address对应的struct page。</li><li>调用try_grab_page对page-> refcount 进行引用计数增加,这里增加了 GUP_PIN_COUNTING_BIAS</li></ul></li><li>vma为空 也不属于gate area则说明在该进程空间内addr地址还未分配,直接返回。(一般调用__get_user_pages()之前,start所属的vm空间都提前已经申请好)</li><li>vma不为空,且vma属于huge page类型,则触发follow_hugetlb_page()为其申请一个huge page物理页,并刷新MMU</li><li>vma不为空,且vma属于系统默认page大小类型,则调用follow_page_mask()查看page table,查看vma是否分配了物理内存:<ul><li>如果没有分配,则调用faultin_page ()触发page fault流程,为其申请一个物理页</li><li>如果已经分配物理内存,则直接查看是否需要分配下一页</li></ul></li></ul></li><li>由于通过page fault流程一次只能分配一个物理页,如果nr_pages需要分配多个物理页,则需要重新循环重新依次进入上面流程。</li><li>特别需要注意的是,由于触发page fault流程非常耗时,特别是当nr_pages比较大时,整个锁定物理页过程非常耗时。为了在长时间循环中防止各种意外发生,函数调用了fatal_signal_pending()和cond_resched(),分别可以获取KILL signal处理和 cpu放权触发更高优先级任务处理。</li></ul></li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * __get_user_pages() - pin user pages in memory</span><br><span class="hljs-comment"> * @mm:mm_struct of target mm</span><br><span class="hljs-comment"> * @start:starting user address</span><br><span class="hljs-comment"> * @nr_pages:number of pages from start to pin</span><br><span class="hljs-comment"> * @gup_flags:flags modifying pin behaviour</span><br><span class="hljs-comment"> * @pages:array that receives pointers to the pages pinned.</span><br><span class="hljs-comment"> *Should be at least nr_pages long. Or NULL, if caller</span><br><span class="hljs-comment"> *only intends to ensure the pages are faulted in.</span><br><span class="hljs-comment"> * @vmas:array of pointers to vmas corresponding to each page.</span><br><span class="hljs-comment"> *Or NULL if the caller does not require them.</span><br><span class="hljs-comment"> * @locked: whether we're still with the mmap_lock held</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * Returns either number of pages pinned (which may be less than the</span><br><span class="hljs-comment"> * number requested), or an error. Details about the return value:</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * -- If nr_pages is 0, returns 0.</span><br><span class="hljs-comment"> * -- If nr_pages is >0, but no pages were pinned, returns -errno.</span><br><span class="hljs-comment"> * -- If nr_pages is >0, and some pages were pinned, returns the number of</span><br><span class="hljs-comment"> * pages pinned. Again, this may be less than nr_pages.</span><br><span class="hljs-comment"> * -- 0 return value is possible when the fault would need to be retried.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * The caller is responsible for releasing returned @pages, via put_page().</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * @vmas are valid only as long as mmap_lock is held.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * Must be called with mmap_lock held. It may be released. See below.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * __get_user_pages walks a process's page tables and takes a reference to</span><br><span class="hljs-comment"> * each struct page that each user address corresponds to at a given</span><br><span class="hljs-comment"> * instant. That is, it takes the page that would be accessed if a user</span><br><span class="hljs-comment"> * thread accesses the given user virtual address at that instant.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * This does not guarantee that the page exists in the user mappings when</span><br><span class="hljs-comment"> * __get_user_pages returns, and there may even be a completely different</span><br><span class="hljs-comment"> * page there in some cases (eg. if mmapped pagecache has been invalidated</span><br><span class="hljs-comment"> * and subsequently re faulted). However it does guarantee that the page</span><br><span class="hljs-comment"> * won't be freed completely. And mostly callers simply care that the page</span><br><span class="hljs-comment"> * contains data that was valid *at some point in time*. Typically, an IO</span><br><span class="hljs-comment"> * or similar operation cannot guarantee anything stronger anyway because</span><br><span class="hljs-comment"> * locks can't be held over the syscall boundary.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * If @gup_flags & FOLL_WRITE == 0, the page must not be written to. If</span><br><span class="hljs-comment"> * the page is written to, set_page_dirty (or set_page_dirty_lock, as</span><br><span class="hljs-comment"> * appropriate) must be called after the page is finished with, and</span><br><span class="hljs-comment"> * before put_page is called.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * If @locked != NULL, *@locked will be set to 0 when mmap_lock is</span><br><span class="hljs-comment"> * released by an up_read(). That can happen if @gup_flags does not</span><br><span class="hljs-comment"> * have FOLL_NOWAIT.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * A caller using such a combination of @locked and @gup_flags</span><br><span class="hljs-comment"> * must therefore hold the mmap_lock for reading only, and recognize</span><br><span class="hljs-comment"> * when it's been released. Otherwise, it must be held for either</span><br><span class="hljs-comment"> * reading or writing and will not be released.</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * In most cases, get_user_pages or get_user_pages_fast should be used</span><br><span class="hljs-comment"> * instead of __get_user_pages. __get_user_pages should be used only if</span><br><span class="hljs-comment"> * you need some special @gup_flags.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">static</span> <span class="hljs-keyword">long</span> __get_user_pages(struct mm_struct *mm,<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> start, <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> nr_pages,<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> gup_flags, struct page **pages,<br>struct vm_area_struct **vmas, <span class="hljs-keyword">int</span> *locked)<br>{<br><span class="hljs-keyword">long</span> ret = <span class="hljs-number">0</span>, i = <span class="hljs-number">0</span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">vm_area_struct</span> *<span class="hljs-title">vma</span> =</span> <span class="hljs-literal">NULL</span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">follow_page_context</span> <span class="hljs-title">ctx</span> =</span> { <span class="hljs-literal">NULL</span> };<br><br><span class="hljs-keyword">if</span> (!nr_pages)<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br><br>start = untagged_addr(start);<br><br>VM_BUG_ON(!!pages != !!(gup_flags & (FOLL_GET | FOLL_PIN)));<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * If FOLL_FORCE is set then do not force a full fault as the hinting</span><br><span class="hljs-comment"> * fault information is unrelated to the reference behaviour of a task</span><br><span class="hljs-comment"> * using the address space</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (!(gup_flags & FOLL_FORCE))<br>gup_flags |= FOLL_NUMA;<br><br><span class="hljs-keyword">do</span> {<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">page</span> *<span class="hljs-title">page</span>;</span><br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> foll_flags = gup_flags;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> page_increm;<br><br><span class="hljs-comment">/* first iteration or cross vma bound */</span><br><span class="hljs-keyword">if</span> (!vma || start >= vma->vm_end) {<br>vma = find_extend_vma(mm, start);<br><span class="hljs-keyword">if</span> (!vma && in_gate_area(mm, start)) {<br>ret = get_gate_page(mm, start & PAGE_MASK,<br>gup_flags, &vma,<br>pages ? &pages[i] : <span class="hljs-literal">NULL</span>);<br><span class="hljs-keyword">if</span> (ret)<br><span class="hljs-keyword">goto</span> out;<br>ctx.page_mask = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">goto</span> next_page;<br>}<br><br><span class="hljs-keyword">if</span> (!vma) {<br>ret = -EFAULT;<br><span class="hljs-keyword">goto</span> out;<br>}<br>ret = check_vma_flags(vma, gup_flags);<br><span class="hljs-keyword">if</span> (ret)<br><span class="hljs-keyword">goto</span> out;<br><br><span class="hljs-keyword">if</span> (is_vm_hugetlb_page(vma)) {<br>i = follow_hugetlb_page(mm, vma, pages, vmas,<br>&start, &nr_pages, i,<br>gup_flags, locked);<br><span class="hljs-keyword">if</span> (locked && *locked == <span class="hljs-number">0</span>) {<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * We've got a VM_FAULT_RETRY</span><br><span class="hljs-comment"> * and we've lost mmap_lock.</span><br><span class="hljs-comment"> * We must stop here.</span><br><span class="hljs-comment"> */</span><br>BUG_ON(gup_flags & FOLL_NOWAIT);<br><span class="hljs-keyword">goto</span> out;<br>}<br><span class="hljs-keyword">continue</span>;<br>}<br>}<br>retry:<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * If we have a pending SIGKILL, don't keep faulting pages and</span><br><span class="hljs-comment"> * potentially allocating memory.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (fatal_signal_pending(current)) {<br>ret = -EINTR;<br><span class="hljs-keyword">goto</span> out;<br>}<br>cond_resched();<br><br>page = follow_page_mask(vma, start, foll_flags, &ctx);<br><span class="hljs-keyword">if</span> (!page || PTR_ERR(page) == -EMLINK) {<br>ret = faultin_page(vma, start, &foll_flags,<br> PTR_ERR(page) == -EMLINK, locked);<br><span class="hljs-keyword">switch</span> (ret) {<br><span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:<br><span class="hljs-keyword">goto</span> retry;<br><span class="hljs-keyword">case</span> -EBUSY:<br>ret = <span class="hljs-number">0</span>;<br>fallthrough;<br><span class="hljs-keyword">case</span> -EFAULT:<br><span class="hljs-keyword">case</span> -ENOMEM:<br><span class="hljs-keyword">case</span> -EHWPOISON:<br><span class="hljs-keyword">goto</span> out;<br>}<br>BUG();<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (PTR_ERR(page) == -EEXIST) {<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Proper page table entry exists, but no corresponding</span><br><span class="hljs-comment"> * struct page. If the caller expects **pages to be</span><br><span class="hljs-comment"> * filled in, bail out now, because that can't be done</span><br><span class="hljs-comment"> * for this page.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">if</span> (pages) {<br>ret = PTR_ERR(page);<br><span class="hljs-keyword">goto</span> out;<br>}<br><br><span class="hljs-keyword">goto</span> next_page;<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (IS_ERR(page)) {<br>ret = PTR_ERR(page);<br><span class="hljs-keyword">goto</span> out;<br>}<br><span class="hljs-keyword">if</span> (pages) {<br>pages[i] = page;<br>flush_anon_page(vma, page, start);<br>flush_dcache_page(page);<br>ctx.page_mask = <span class="hljs-number">0</span>;<br>}<br>next_page:<br><span class="hljs-keyword">if</span> (vmas) {<br>vmas[i] = vma;<br>ctx.page_mask = <span class="hljs-number">0</span>;<br>}<br>page_increm = <span class="hljs-number">1</span> + (~(start >> PAGE_SHIFT) & ctx.page_mask);<br><span class="hljs-keyword">if</span> (page_increm > nr_pages)<br>page_increm = nr_pages;<br>i += page_increm;<br>start += page_increm * PAGE_SIZE;<br>nr_pages -= page_increm;<br>} <span class="hljs-keyword">while</span> (nr_pages);<br>out:<br><span class="hljs-keyword">if</span> (ctx.pgmap)<br>put_dev_pagemap(ctx.pgmap);<br><span class="hljs-keyword">return</span> i ? i : ret;<br>}<br></code></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag>RDMA</tag>
<tag>libRXE</tag>
<tag>Soft-ROCE</tag>
<tag>libibverbs</tag>
</tags>
</entry>
<entry>
<title>RDMA Stack实现分析</title>
<link href="/202207/rdma-stack-01/"/>
<url>/202207/rdma-stack-01/</url>
<content type="html"><![CDATA[<p>希望通过这篇文章来梳理</p><ol><li>libibverbs提供的用户态Verbs API是如何进行设备操作/数据通信的,以Soft-RXE实现为例。</li><li>基于kernel: 5.19-rc1</li><li>基于rdma-core: v34.0</li></ol><h2 id="RDMA-Stack的内核支持"><a href="#RDMA-Stack的内核支持" class="headerlink" title="RDMA Stack的内核支持"></a>RDMA Stack的内核支持</h2><ol><li><p>参考:<a href="https://www.kernel.org/doc/html/latest/translations/zh_CN/infiniband/user_verbs.html">https://www.kernel.org/doc/html/latest/translations/zh_CN/infiniband/user_verbs.html</a></p></li><li><p>RDMA Stack分为控制面和数据面,图1中给出了Mellanox MLX5 RDMA stack 涉及到的用户态库以及内核模块。图二中给出了一个控制面API在用户态libibverbs与内核之间的调用关系。</p><ol><li>控制面:<ol><li><code>ib_uverbs.ko</code> 内核模块会对每个IB设备生成一个字符设备文件。通常的形式为 <code>/dev/infiniband/uverbs%d</code> ,用户态程序通过ioctl对字符设备进行属性的读写。</li><li>libibverbs 中的控制面相关Verbs API利用了<code>ib_uverbs.ko</code> 暴露出来的字符设备对RDMA设备操作。</li></ol></li><li>数据面<ol><li>通常是通过直接写入<code>mmap()</code>到用户空间的硬件寄存器来完成 的,没有系统调用或上下文切换到内核。</li></ol></li></ol><p> <img src="/img/rdma-stack-01/mlx_ofed_stack.png" alt="Mellanox OFED stack arch."><br> <a href="https://docs.nvidia.com/networking/display/OFEDv501000/Introduction">图1: Mellanox OFED stack arch.</a>)</p><p> <img src="/img/rdma-stack-01/ibv_create_qp.png"><br> 图2: libibverbs中的控制面API与内核的调用关系: 以ibv_create_qp为例</p></li></ol><h2 id="1-ibv-open-device"><a href="#1-ibv-open-device" class="headerlink" title="1. ibv_open_device"></a>1. ibv_open_device</h2><p>用户态打开IB设备的API</p><ol><li>参考:<a href="https://www.rdmamojo.com/2012/06/29/ibv_open_device/">RDMAmojo: ibv_open_device</a></li></ol><h3 id="1-1-RXE调用栈分析"><a href="#1-1-RXE调用栈分析" class="headerlink" title="1.1 RXE调用栈分析"></a>1.1 RXE调用栈分析</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs c">ibv_open_device<br>|-- verbs_open_device<br>|-- open_cdev<br>|-- rxe_alloc_context<br>|-- verbs_init_and_alloc_context<br>|-- verbs_init_context<br>|-- ibv_cmd_get_context<br>|-- verbs_set_ops<br></code></pre></td></tr></table></figure><ol><li><p>open_cdev</p><p> 会打开uverbs字符设备,文件描述符保存在cmd_fd中</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/device.c</span><br>cmd_fd = open_cdev(verbs_device->sysfs->sysfs_name,<br> verbs_device->sysfs->sysfs_cdev);<br><br><span class="hljs-comment">// rdma-core/util/open_cdev.c</span><br><span class="hljs-comment">// in open_cdev funcion</span><br>fd = open(<span class="hljs-string">"/dev/infiniband/uverbs%d"</span>, O_RDWR | O_CLOEXEC)<br></code></pre></td></tr></table></figure></li><li><p>rxe_alloc_context</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/device.c</span><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * cmd_fd ownership is transferred into alloc_context, if it fails</span><br><span class="hljs-comment"> * then it closes cmd_fd and returns NULL</span><br><span class="hljs-comment"> */</span><br>context_ex = verbs_device->ops->alloc_context(device, cmd_fd, private_data);<br><br><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> struct verbs_context *<span class="hljs-title">rxe_alloc_context</span><span class="hljs-params">(struct ibv_device *ibdev,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">int</span> cmd_fd,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">void</span> *private_data)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_context</span> *<span class="hljs-title">context</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_get_context</span> <span class="hljs-title">cmd</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_get_context_resp</span> <span class="hljs-title">resp</span>;</span><br><br>context = verbs_init_and_alloc_context(ibdev, cmd_fd, context, ibv_ctx,<br> RDMA_DRIVER_RXE);<br>...<br>ibv_cmd_get_context(&context->ibv_ctx, &cmd, <span class="hljs-keyword">sizeof</span>(cmd),<br>&resp, <span class="hljs-keyword">sizeof</span>(resp))<br>...<br><br>verbs_set_ops(&context->ibv_ctx, &rxe_ctx_ops);<br>...<br>}<br><br></code></pre></td></tr></table></figure></li><li><p>verbs_init_and_alloc_context</p><ol><li><code>&((typeof(context))((void *)0))->ibv_ctx</code>, 类似于<code>offset_of</code> 宏,计算<code>ibv_ctx</code>在<code>rxe_context</code> 结构体内偏移</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.h</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_context</span> {</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_context</span><span class="hljs-title">ibv_ctx</span>;</span><br>};<br><br><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-comment">// expand macro</span><br>context = ((typeof(context))_verbs_init_and_alloc_context(<br>ibdev, <br>cmd_fd, <br><span class="hljs-keyword">sizeof</span>(*context),<br>&((typeof(context))((<span class="hljs-keyword">void</span> *)<span class="hljs-number">0</span>))->ibv_ctx, <br>(RDMA_DRIVER_RXE)))<br><br><span class="hljs-comment">// rdma-core/libibverbs/device.c</span><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Allocate and initialize a context structure. This is called to create the</span><br><span class="hljs-comment"> * driver wrapper, and context_offset is the number of bytes into the wrapper</span><br><span class="hljs-comment"> * structure where the verbs_context starts.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">void</span> *_verbs_init_and_alloc_context(struct ibv_device *device, <span class="hljs-keyword">int</span> cmd_fd,<br> <span class="hljs-keyword">size_t</span> alloc_size,<br> struct verbs_context *context_offset,<br> <span class="hljs-keyword">uint32_t</span> driver_id)<br>{<br><span class="hljs-keyword">void</span> *drv_context;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_context</span> *<span class="hljs-title">context</span>;</span><br><br>drv_context = <span class="hljs-built_in">calloc</span>(<span class="hljs-number">1</span>, alloc_size);<br>...<br>context = drv_context + (<span class="hljs-keyword">uintptr_t</span>)context_offset;<br>verbs_init_context(context, device, cmd_fd, driver_id)<br>...<br><span class="hljs-keyword">return</span> drv_context;<br>}<br></code></pre></td></tr></table></figure></li><li><p>verbs_init_context</p><ol><li><code>context->cmd_fd = cmd_fd;</code> 记录uverbs设备文件的描述符</li><li>用<code>verbs_dummy_ops</code>初始化 <code>ibv_context->ibv_context_ops</code> 回调函数结构体</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/device.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">verbs_init_context</span><span class="hljs-params">(struct verbs_context *context_ex,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_device *device, <span class="hljs-keyword">int</span> cmd_fd,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">uint32_t</span> driver_id)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_context</span> *<span class="hljs-title">context</span> =</span> &context_ex->context;<br><br>ibverbs_device_hold(device);<br><br>context->device = device;<br>context->cmd_fd = cmd_fd;<br>context->async_fd = <span class="hljs-number">-1</span>;<br>pthread_mutex_init(&context->mutex, <span class="hljs-literal">NULL</span>);<br><br>context_ex->context.abi_compat = __VERBS_ABI_IS_EXTENDED;<br>context_ex->sz = <span class="hljs-keyword">sizeof</span>(*context_ex);<br><br>context_ex->priv = <span class="hljs-built_in">calloc</span>(<span class="hljs-number">1</span>, <span class="hljs-keyword">sizeof</span>(*context_ex->priv));<br>...<br>context_ex->priv->driver_id = driver_id;<br>verbs_set_ops(context_ex, &verbs_dummy_ops);<br>context_ex->priv->use_ioctl_write = has_ioctl_write(context);<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>ibv_cmd_get_context</p><ol><li>ioctl 传递给kernel的参数都会封装在ibv_command_buffer 结构体中,这里声明arg buffer</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// in rdma-core/providers/rxe/rxe.c, call function</span><br>ibv_cmd_get_context(&context->ibv_ctx, &cmd, <span class="hljs-keyword">sizeof</span>(cmd),<br>&resp, <span class="hljs-keyword">sizeof</span>(resp));<br><br><span class="hljs-comment">// rdma-core/libibverbs/cmd_device.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">ibv_cmd_get_context</span><span class="hljs-params">(struct verbs_context *context_ex,</span></span><br><span class="hljs-params"><span class="hljs-function">struct ibv_get_context *cmd, <span class="hljs-keyword">size_t</span> cmd_size,</span></span><br><span class="hljs-params"><span class="hljs-function">struct ib_uverbs_get_context_resp *resp,</span></span><br><span class="hljs-params"><span class="hljs-function"><span class="hljs-keyword">size_t</span> resp_size)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-comment">//DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_DEVICE,</span><br><span class="hljs-comment">// UVERBS_METHOD_GET_CONTEXT, cmd, cmd_size,</span><br><span class="hljs-comment">// resp, resp_size);</span><br><span class="hljs-comment">// expands macro</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_command_buffer</span> <span class="hljs-title">cmdb</span>[((<span class="hljs-title">sizeof</span>(<span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_command_buffer</span>) +</span><br><span class="hljs-class"> <span class="hljs-title">sizeof</span>(<span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_attr</span>) * (__<span class="hljs-title">cmdbtotal</span>) +</span><br><span class="hljs-class"> <span class="hljs-title">sizeof</span>(<span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_command_buffer</span>) - 1) /</span><br><span class="hljs-class"><span class="hljs-title">sizeof</span>(<span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_command_buffer</span>))];</span><br><span class="hljs-keyword">return</span> cmd_get_context(context_ex, cmdb);<br>}<br><br></code></pre></td></tr></table></figure></li><li><p>verbs_set_ops </p><ol><li>将<code>verbs_context</code> 结构体中的<code>verbs_context</code> 中的回调函数,设置为rxe声明的回调函数(默认为dummy_ops)</li><li>这里不太清楚为什么<code>verbs_context</code>内部的<code>verbs_ex_private</code> 结构体又保存了一份回调函数</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// in rdma-core/providers/rxe/rxe.c, call function</span><br>verbs_set_ops(&context->ibv_ctx, &rxe_ctx_ops);<br><br><span class="hljs-comment">// rdma-core/libibverbs/dummy_ops.c</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">verbs_set_ops</span><span class="hljs-params">(struct verbs_context *vctx,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> struct verbs_context_ops *ops)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_ex_private</span> *<span class="hljs-title">priv</span> =</span> vctx->priv;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_context_ops</span> *<span class="hljs-title">ctx</span> =</span> &vctx->context.ops;<br>...<br><span class="hljs-comment">// expand macro</span><br><span class="hljs-keyword">if</span> (ops->alloc_dm) {<br>priv->ops.alloc_dm = ops->alloc_dm;<br>(vctx)->alloc_dm = ops->alloc_dm;<br>}<br>... <br><br></code></pre></td></tr></table></figure></li><li><p>总结ibv_open_device</p><ol><li>分配context结构体,打开uverbs字符设备</li><li>设置用于传递ioctl参数的 arg buffer</li><li>设置回调函数,这些回调函数会被ibv_xxx Verbs调用</li></ol></li></ol><h2 id="2-ibv-create-qp"><a href="#2-ibv-create-qp" class="headerlink" title="2. ibv_create_qp"></a>2. ibv_create_qp</h2><p>Verbs</p><ol><li><a href="https://www.rdmamojo.com/2012/12/21/ibv_create_qp/">RDMAmojo: ibv_create_qp</a></li></ol><h3 id="2-1-RXE调用栈分析"><a href="#2-1-RXE调用栈分析" class="headerlink" title="2.1 RXE调用栈分析"></a>2.1 RXE调用栈分析</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs c">ibv_create_qp<br>|-- rxe_create_qp<br> |-- ibv_cmd_create_qp<br>|-- ibv_icmd_create_qp<br>|-- execute_ioctl_fallback<br>|-- execute_ioctl<br>|-- kernel: rxe_create_qp<br>|-- map_queue_pair<br></code></pre></td></tr></table></figure><ol><li><p>ibv_create_qp</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/verbs.c</span><br>LATEST_SYMVER_FUNC(ibv_create_qp, <span class="hljs-number">1</span>_1, <span class="hljs-string">"IBVERBS_1.1"</span>,<br> struct ibv_qp *, <span class="hljs-comment">// return type</span><br> struct ibv_pd *pd, <span class="hljs-comment">// arg1</span><br> struct ibv_qp_init_attr *qp_init_attr) <span class="hljs-comment">// arg2</span><br>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_qp</span> *<span class="hljs-title">qp</span> =</span> get_ops(pd->context)->create_qp(pd, qp_init_attr);<br><br><span class="hljs-keyword">return</span> qp;<br>}<br></code></pre></td></tr></table></figure></li><li><p>rxe_create_qp</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> struct ibv_qp *<span class="hljs-title">rxe_create_qp</span><span class="hljs-params">(struct ibv_pd *ibpd,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_qp_init_attr *attr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_create_qp</span> <span class="hljs-title">cmd</span> =</span> {};<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">urxe_create_qp_resp</span> <span class="hljs-title">resp</span> =</span> {};<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span>;</span><br><span class="hljs-keyword">int</span> ret;<br><br>...<br>ret = ibv_cmd_create_qp(ibpd, &qp->vqp.qp, attr, &cmd, <span class="hljs-keyword">sizeof</span>(cmd),<br>&resp.ibv_resp, <span class="hljs-keyword">sizeof</span>(resp));<br>...<br>ret = map_queue_pair(ibpd->context->cmd_fd, qp, attr,<br> &resp.drv_payload);<br><br>...<br>}<br></code></pre></td></tr></table></figure><ol><li>其中 ibv_create_qp/urxe_create_qp_resp 这两个结构体由宏声明,分别将二者展开后 <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/cmake-build-debug/include/infiniband/kern-abi.h </span><br>DECLARE_CMD(IB_USER_VERBS_CMD_CREATE_CQ, ibv_create_cq, ib_uverbs_create_cq);<br><br><span class="hljs-comment">// expands macro</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_create_qp</span> {</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_cmd_hdr</span> <span class="hljs-title">hdr</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">union</span> {</span><br>_STRUCT_ib_uverbs_create_qp;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_create_qp</span> <span class="hljs-title">core_payload</span>;</span><br>};<br>};<br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_create_qp</span> _<span class="hljs-title">ABI_REQ_STRUCT_IB_USER_VERBS_CMD_CREATE_QP</span>;</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_create_qp</span> _<span class="hljs-title">KABI_REQ_STRUCT_IB_USER_VERBS_CMD_CREATE_QP</span>;</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_create_qp_resp</span></span><br><span class="hljs-class">_<span class="hljs-title">KABI_RESP_STRUCT_IB_USER_VERBS_CMD_CREATE_QP</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">enum</span> {</span> _ABI_ALIGN_IB_USER_VERBS_CMD_CREATE_QP = <span class="hljs-number">4</span> };<br></code></pre></td></tr></table></figure> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs c"><br><span class="hljs-comment">// rdma-core/providers/rxe/rxe-abi.h</span><br>DECLARE_DRV_CMD(urxe_create_qp, IB_USER_VERBS_CMD_CREATE_QP,<br>empty, rxe_create_qp_resp);<br><br><span class="hljs-comment">// expands macro</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">urxe_create_qp</span> {</span><br>_ABI_REQ_STRUCT_IB_USER_VERBS_CMD_CREATE_QP ibv_cmd;<br><span class="hljs-class"><span class="hljs-keyword">union</span> {</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> {</span>};<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">empty</span> <span class="hljs-title">drv_payload</span>;</span><br>};<br>};<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">urxe_create_qp_resp</span> {</span><br>_KABI_RESP_STRUCT_IB_USER_VERBS_CMD_CREATE_QP ibv_resp;<br><span class="hljs-class"><span class="hljs-keyword">union</span> {</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> {</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mminfo</span> <span class="hljs-title">rq_mi</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mminfo</span> <span class="hljs-title">sq_mi</span>;</span><br>};<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_create_qp_resp</span> <span class="hljs-title">drv_payload</span>;</span><br>};<br>};<br></code></pre></td></tr></table></figure></li></ol></li><li><p>ibv_cmd_create_qp</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/cmd_qp.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">ibv_cmd_create_qp</span><span class="hljs-params">(struct ibv_pd *pd,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_qp *qp, struct ibv_qp_init_attr *attr,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_create_qp *cmd, <span class="hljs-keyword">size_t</span> cmd_size,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ib_uverbs_create_qp_resp *resp, <span class="hljs-keyword">size_t</span> resp_size)</span></span><br><span class="hljs-function"></span>{<br>DECLARE_CMD_BUFFER_COMPAT(cmdb, UVERBS_OBJECT_QP,<br> UVERBS_METHOD_QP_CREATE, cmd, cmd_size, resp,<br> resp_size);<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_qp_init_attr_ex</span> <span class="hljs-title">attr_ex</span> =</span> {};<br><span class="hljs-keyword">int</span> ret;<br><br>attr_ex.qp_context = attr->qp_context;<br>attr_ex.send_cq = attr->send_cq;<br>attr_ex.recv_cq = attr->recv_cq;<br>attr_ex.srq = attr->srq;<br>attr_ex.cap = attr->cap;<br>attr_ex.qp_type = attr->qp_type;<br>attr_ex.sq_sig_all = attr->sq_sig_all;<br>attr_ex.comp_mask = IBV_QP_INIT_ATTR_PD;<br>attr_ex.pd = pd;<br>ret = ibv_icmd_create_qp(pd->context, <span class="hljs-literal">NULL</span>, qp, &attr_ex, cmdb);<br><span class="hljs-keyword">if</span> (!ret)<br><span class="hljs-built_in">memcpy</span>(&attr->cap, &attr_ex.cap, <span class="hljs-keyword">sizeof</span>(attr_ex.cap));<br><br><span class="hljs-keyword">return</span> ret;<br>}<br></code></pre></td></tr></table></figure></li><li><p>ibv_icmd_create_qp</p><ol><li>libibverbs使用了两套与内核通信的方式:传统的write方式,以及ioctl方式。</li><li>如果当前的ib device context不支持ioctl方式与内核暴露的字符设备通信的话,则需要fallback回write方式<ol><li>可以参考之前版本的libibverbs对应实现:<a href="https://github.com/hustcat/rdma-core/blob/v15/libibverbs/cmd.c#L1063">https://github.com/hustcat/rdma-core/blob/v15/libibverbs/cmd.c#L1063</a></li></ol></li><li>_CMD_BIT(cmd_name) 宏作用:把verbs_context_ops看成bitmap,每个回调函数占一个bit,计算传入的cmd_name占哪个bit<ol><li>sizeof(void*) 由编译时target platform决定,amd64下值等于8</li></ol></li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/cmd_qp.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ibv_icmd_create_qp</span><span class="hljs-params">(struct ibv_context *context,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct verbs_qp *vqp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_qp *qp_in,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_qp_init_attr_ex *attr_ex,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_command_buffer *link)</span> </span>{<br>...<br>execute_ioctl_fallback(context, create_qp, cmdb, &ret)<br>...<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">write_fallback</span> _<span class="hljs-title">execute_ioctl_fallback</span>(<span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_context</span> *<span class="hljs-title">ctx</span>,</span><br><span class="hljs-class"> <span class="hljs-title">unsigned</span> <span class="hljs-title">int</span> <span class="hljs-title">cmd_bit</span>,</span><br><span class="hljs-class"> <span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_command_buffer</span> *<span class="hljs-title">cmdb</span>,</span><br><span class="hljs-class"> <span class="hljs-title">int</span> *<span class="hljs-title">ret</span>);</span><br><br><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> execute_ioctl_fallback(ctx, cmd_name, cmdb, ret) \</span><br><span class="hljs-meta">_execute_ioctl_fallback(ctx, _CMD_BIT(cmd_name), cmdb, ret)</span><br><br><span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> _CMD_BIT(cmd_name) \</span><br><span class="hljs-meta">(offsetof(struct verbs_context_ops, cmd_name) / sizeof(void *))</span><br><br></code></pre></td></tr></table></figure></li><li><p>_execute_ioctl_fallback</p><ol><li>检查当前设备是否可以使用ioctl方式进行操作,是的话直接ioctl传给内核响应命令,否则fallback回调用函数</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/libibverbs/cmd_fallback.c</span><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Used to support callers that have a fallback to the old write ABI</span><br><span class="hljs-comment"> * interface.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">write_fallback</span> _<span class="hljs-title">execute_ioctl_fallback</span>(<span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_context</span> *<span class="hljs-title">ctx</span>,</span><br><span class="hljs-class"> <span class="hljs-title">unsigned</span> <span class="hljs-title">int</span> <span class="hljs-title">cmd_bit</span>,</span><br><span class="hljs-class"> <span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_command_buffer</span> *<span class="hljs-title">cmdb</span>,</span><br><span class="hljs-class"> <span class="hljs-title">int</span> *<span class="hljs-title">ret</span>)</span><br><span class="hljs-class">{</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">verbs_ex_private</span> *<span class="hljs-title">priv</span> =</span> get_priv(ctx);<br><br><span class="hljs-keyword">if</span> (bitmap_test_bit(priv->unsupported_ioctls, cmd_bit))<br><span class="hljs-keyword">return</span> _check_legacy(cmdb, ret);<br><br>*ret = execute_ioctl(ctx, cmdb);<br>...<br>}<br></code></pre></td></tr></table></figure></li><li><p>kernel: rxe_create_qp</p><ol><li><p>在内核新建一个QP:根据QPN号hash后得到对应的src port,用于填充要发送的UDP报文</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// linux-kernel/rust-kernel/drivers/infiniband/sw/rxe/rxe_verbs.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_create_qp</span><span class="hljs-params">(struct ib_qp *ibqp, struct ib_qp_init_attr *init,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ib_udata *udata)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> err;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_dev</span> *<span class="hljs-title">rxe</span> =</span> to_rdev(ibqp->device);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_pd</span> *<span class="hljs-title">pd</span> =</span> to_rpd(ibqp->pd);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> to_rqp(ibqp);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_create_qp_resp</span> __<span class="hljs-title">user</span> *<span class="hljs-title">uresp</span> =</span> <span class="hljs-literal">NULL</span>;<br><span class="hljs-keyword">if</span> (udata) {<br><span class="hljs-keyword">if</span> (udata->outlen < <span class="hljs-keyword">sizeof</span>(*uresp))<br><span class="hljs-keyword">return</span> -EINVAL;<br>uresp = udata->outbuf;<br>}<br>err = rxe_qp_chk_init(rxe, init);<br>...<br>qp->is_user = <span class="hljs-literal">true</span>;<br>...<br>err = rxe_add_to_pool(&rxe->qp_pool, qp);<br>...<br>err = rxe_qp_from_init(rxe, qp, pd, init, uresp, ibqp->pd, udata);<br>...<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>rxe_qp_init_req </p><ul><li>在 rxe_queue_init 函数中申请一段连续的虚拟地址空间,用作SQ buffer</li><li>将SQ buffer的地址+大小设置为QP中RQ的mmap info</li><li>根据QPN号hash后得到对应的src port,用于填充UDP报文</li></ul></li><li><p>rxe_qp_init_resp</p><ul><li>在 rxe_queue_init 函数中申请一段连续的虚拟地址空间,用作RQ buffer</li><li>将RQ buffer的地址+大小设置为QP中RQ的mmap info</li></ul></li></ol></li><li><p>kernel: rxe_create_mmap_info</p><ol><li>将QP中记录的SQ/RQ的mmap info,传回userspace,userspace根据内核指定的offset+size做mmap之后,使得kernel与userspace可以共享QP内存空间。</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// linux-kernel/rust-kernel/drivers/infiniband/sw/rxe/rxe_mmap.c</span><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Allocate information for rxe_mmap</span><br><span class="hljs-comment"> */</span><br><span class="hljs-function">struct rxe_mmap_info *<span class="hljs-title">rxe_create_mmap_info</span><span class="hljs-params">(struct rxe_dev *rxe, u32 size,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ib_udata *udata, <span class="hljs-keyword">void</span> *obj)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_mmap_info</span> *<span class="hljs-title">ip</span>;</span><br>ip = kmalloc(<span class="hljs-keyword">sizeof</span>(*ip), GFP_KERNEL);<br>...<br>ip->info.offset = rxe->mmap_offset;<br>ip->info.size = size;<br>ip->context =<br>container_of(udata, struct uverbs_attr_bundle, driver_udata)<br>->context;<br>...<br>ip->obj = obj;<br>kref_init(&ip->ref);<br><span class="hljs-keyword">return</span> ip;<br>}<br></code></pre></td></tr></table></figure><p> 最后<code>ip->info</code> 会被传给<code>userspace</code>,作为<code>rxe_create_qp_resp</code> </p></li><li><p>map_queue_pair</p><ol><li><code>map_queue_pair</code> 中第一个参数是<code>ibpd->context->cmd_fd</code>, 字符设备的文件描述符。</li><li>mmap 的offset + size 是由kernel传到的用户空间的resp设置</li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// in rdma-core/providers/rxe/rxe.c, call function</span><br>ret = map_queue_pair(ibpd->context->cmd_fd, qp, attr,<br> &resp.drv_payload);<br><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">map_queue_pair</span><span class="hljs-params">(<span class="hljs-keyword">int</span> cmd_fd, struct rxe_qp *qp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_qp_init_attr *attr,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct rxe_create_qp_resp *resp)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">if</span> (attr->srq) {<br>qp->rq.max_sge = <span class="hljs-number">0</span>;<br>qp->rq.<span class="hljs-built_in">queue</span> = <span class="hljs-literal">NULL</span>;<br>qp->rq_mmap_info.size = <span class="hljs-number">0</span>;<br>} <span class="hljs-keyword">else</span> {<br>qp->rq.max_sge = attr->cap.max_recv_sge;<br>qp->rq.<span class="hljs-built_in">queue</span> = mmap(<span class="hljs-literal">NULL</span>, resp->rq_mi.size, PROT_READ | PROT_WRITE,<br> MAP_SHARED,<br> cmd_fd, resp->rq_mi.offset);<br><span class="hljs-keyword">if</span> ((<span class="hljs-keyword">void</span> *)qp->rq.<span class="hljs-built_in">queue</span> == MAP_FAILED)<br><span class="hljs-keyword">return</span> errno;<br><br>qp->rq_mmap_info = resp->rq_mi;<br>pthread_spin_init(&qp->rq.lock, PTHREAD_PROCESS_PRIVATE);<br>}<br><br>qp->sq.max_sge = attr->cap.max_send_sge;<br>qp->sq.max_inline = attr->cap.max_inline_data;<br>qp->sq.<span class="hljs-built_in">queue</span> = mmap(<span class="hljs-literal">NULL</span>, resp->sq_mi.size, PROT_READ | PROT_WRITE,<br> MAP_SHARED,<br> cmd_fd, resp->sq_mi.offset);<br>...<br>qp->sq_mmap_info = resp->sq_mi;<br>pthread_spin_init(&qp->sq.lock, PTHREAD_PROCESS_PRIVATE);<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br><br></code></pre></td></tr></table></figure></li><li><p>总结ibv_create_qp</p><ol><li>libRXE部分通过ioctl/write的方式请求kernel</li><li>kernel创建SQ/RQ两端缓冲区,将地址+大小传回用户态</li><li>libRXE根据传回信息,映射相同的地址到用户态,使得内核与用户态共享Queue Pair的地址空间。</li></ol></li></ol><h2 id="3-ibv-post-send"><a href="#3-ibv-post-send" class="headerlink" title="3. ibv_post_send"></a>3. ibv_post_send</h2><p>Verbs</p><ol><li><a href="https://www.rdmamojo.com/2013/01/26/ibv_post_send/">RDMAmojo: ibv_post_send</a></li><li>将一组链式的ibv_send_wr发送到QP中的Send Queue中</li></ol><h3 id="3-1-RXE调用栈分析"><a href="#3-1-RXE调用栈分析" class="headerlink" title="3.1 RXE调用栈分析"></a>3.1 RXE调用栈分析</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs c">ibv_post_send<br>|-- rxe_post_send<br>|-- post_one_send<br>|-- post_send_db<br>|-- rxe_post_send<br>|-- rxe_requester<br></code></pre></td></tr></table></figure><ol><li><p>rxe_post_send</p><ul><li>将wr_list链表中的work request依次放到QP send queue buffer中</li><li>如果有失败的,则将bad_wr指向第一个失败的work request,bad_wr会返回给应用程序</li><li>所有WR放到Send Queue之后,敲一下door bell通知kernel</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_post_send</span><span class="hljs-params">(struct ibv_qp *ibqp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_send_wr *wr_list,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_send_wr **bad_wr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> rc = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">int</span> err;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> to_rqp(ibqp);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_wq</span> *<span class="hljs-title">sq</span> =</span> &qp->sq;<br><br><span class="hljs-keyword">if</span> (!bad_wr)<br><span class="hljs-keyword">return</span> EINVAL;<br><br>*bad_wr = <span class="hljs-literal">NULL</span>;<br>...<br>pthread_spin_lock(&sq->lock);<br><br><span class="hljs-keyword">while</span> (wr_list) {<br>rc = post_one_send(qp, sq, wr_list);<br><span class="hljs-keyword">if</span> (rc) {<br>*bad_wr = wr_list;<br><span class="hljs-keyword">break</span>;<br>}<br><br>wr_list = wr_list->next;<br>}<br><br>pthread_spin_unlock(&sq->lock);<br><br>err = post_send_db(ibqp);<br><span class="hljs-keyword">return</span> err ? err : rc;<br>}<br></code></pre></td></tr></table></figure></li><li><p>post_one_send</p><ul><li>对于RXE而言,libRXE中的<code>post_one_send</code>作为生产者,向Send Queue中放置Work request。</li><li>读当前Send queue空闲区域起始地址需要原子操作(避免使用锁)</li><li>更新 <code>producer_index</code> 指针需要原子操作</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">post_one_send</span><span class="hljs-params">(struct rxe_qp *qp, struct rxe_wq *sq,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_send_wr *ibwr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> err;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_send_wqe</span> *<span class="hljs-title">wqe</span>;</span><br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> length = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">int</span> i;<br><br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < ibwr->num_sge; i++)<br>length += ibwr->sg_list[i].length;<br><br>err = validate_send_wr(qp, ibwr, length);<br><span class="hljs-keyword">if</span> (err) {<br>verbs_err(verbs_get_ctx(qp->vqp.qp.context),<br> <span class="hljs-string">"validate send failed\n"</span>);<br><span class="hljs-keyword">return</span> err;<br>}<br><br>wqe = (struct rxe_send_wqe *)producer_addr(sq-><span class="hljs-built_in">queue</span>);<br><br>err = init_send_wqe(qp, sq, ibwr, length, wqe);<br><span class="hljs-keyword">if</span> (err)<br><span class="hljs-keyword">return</span> err;<br><br><span class="hljs-keyword">if</span> (queue_full(sq-><span class="hljs-built_in">queue</span>))<br><span class="hljs-keyword">return</span> -ENOMEM;<br><br>advance_producer(sq-><span class="hljs-built_in">queue</span>);<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>init_send_wqe</p><ul><li>这个函数比较容易理解,就是将一个WR中所有<code>sg_list</code> 拷贝到wqe buffer中,kernel再去根据SGList读内存。(RXE这里没有DMA操作)</li><li>如果设置了 IBV_SEND_INLINE,则在这里直接拷贝数据本身到wqe buffer中</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">init_send_wqe</span><span class="hljs-params">(struct rxe_qp *qp, struct rxe_wq *sq,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_send_wr *ibwr, <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> length,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct rxe_send_wqe *wqe)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> num_sge = ibwr->num_sge;<br><span class="hljs-keyword">int</span> i;<br><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> opcode = ibwr->opcode;<br><br>convert_send_wr(qp, &wqe->wr, ibwr);<br><br><span class="hljs-keyword">if</span> (qp_type(qp) == IBV_QPT_UD) {<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_ah</span> *<span class="hljs-title">ah</span> =</span> to_rah(ibwr->wr.ud.ah);<br><br><span class="hljs-keyword">if</span> (!ah->ah_num)<br><span class="hljs-comment">/* old kernels only */</span><br><span class="hljs-built_in">memcpy</span>(&wqe->wr.wr.ud.av, &ah->av, <span class="hljs-keyword">sizeof</span>(struct rxe_av));<br>}<br><br><span class="hljs-keyword">if</span> (ibwr->send_flags & IBV_SEND_INLINE) {<br><span class="hljs-keyword">uint8_t</span> *inline_data = wqe->dma.inline_data;<br><br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < num_sge; i++) {<br><span class="hljs-built_in">memcpy</span>(inline_data,<br> (<span class="hljs-keyword">uint8_t</span> *)(<span class="hljs-keyword">long</span>)ibwr->sg_list[i].addr,<br> ibwr->sg_list[i].length);<br>inline_data += ibwr->sg_list[i].length;<br>}<br>} <span class="hljs-keyword">else</span><br><span class="hljs-built_in">memcpy</span>(wqe->dma.sge, ibwr->sg_list,<br> num_sge*<span class="hljs-keyword">sizeof</span>(struct ibv_sge));<br><br><span class="hljs-keyword">if</span> ((opcode == IBV_WR_ATOMIC_CMP_AND_SWP)<br> || (opcode == IBV_WR_ATOMIC_FETCH_AND_ADD))<br>wqe->iova= ibwr->wr.atomic.remote_addr;<br><span class="hljs-keyword">else</span><br>wqe->iova= ibwr->wr.rdma.remote_addr;<br><br>wqe->dma.length= length;<br>wqe->dma.resid= length;<br>wqe->dma.num_sge= num_sge;<br>wqe->dma.cur_sge= <span class="hljs-number">0</span>;<br>wqe->dma.sge_offset= <span class="hljs-number">0</span>;<br>wqe->state= <span class="hljs-number">0</span>;<br>wqe->ssn= qp->ssn++;<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>post_send_db</p><ul><li>敲doorbell 在RXE这里变为使用write的方式通知kernel</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-comment">/* send a null post send as a doorbell */</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">post_send_db</span><span class="hljs-params">(struct ibv_qp *ibqp)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ibv_post_send</span> <span class="hljs-title">cmd</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ib_uverbs_post_send_resp</span> <span class="hljs-title">resp</span>;</span><br><br>cmd.hdr.command= IB_USER_VERBS_CMD_POST_SEND;<br>cmd.hdr.in_words = <span class="hljs-keyword">sizeof</span>(cmd) / <span class="hljs-number">4</span>;<br>cmd.hdr.out_words = <span class="hljs-keyword">sizeof</span>(resp) / <span class="hljs-number">4</span>;<br>cmd.response= (<span class="hljs-keyword">uintptr_t</span>)&resp;<br>cmd.qp_handle= ibqp->handle;<br>cmd.wr_count= <span class="hljs-number">0</span>;<br>cmd.sge_count= <span class="hljs-number">0</span>;<br>cmd.wqe_size= <span class="hljs-keyword">sizeof</span>(struct ibv_send_wr);<br><br><span class="hljs-keyword">if</span> (write(ibqp->context->cmd_fd, &cmd, <span class="hljs-keyword">sizeof</span>(cmd)) != <span class="hljs-keyword">sizeof</span>(cmd))<br><span class="hljs-keyword">return</span> errno;<br><br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>kernel: rxe_post_send</p><ol><li>libRXE将传递给内核的命令设置为 IB_USER_VERBS_CMD_POST_SEND</li><li><code>ib_uverbs_post_send</code> -> <code>ib_device->post_send</code> -> <code>rxe_post_send</code> -> <code>rxe_requester</code> -> <code>ip_local_out</code></li><li>kernel在初始化Send Queue的时候,将 qp->req.task 回调函数注册为<code>rxe_requester</code></li></ol> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_verbs.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_post_send</span><span class="hljs-params">(struct ib_qp *ibqp, <span class="hljs-keyword">const</span> struct ib_send_wr *wr,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> struct ib_send_wr **bad_wr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> to_rqp(ibqp);<br>...<br><span class="hljs-keyword">if</span> (qp->is_user) {<br><span class="hljs-comment">/* Utilize process context to do protocol processing */</span><br>rxe_run_task(&qp->req.task, <span class="hljs-number">0</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>...<br>}<br><br><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_qp.c</span><br><span class="hljs-comment">// in func rxe_qp_init_req</span><br>rxe_init_task(rxe, &qp->req.task, qp,<br> rxe_requester, <span class="hljs-string">"req"</span>);<br>rxe_init_task(rxe, &qp->comp.task, qp,<br> rxe_completer, <span class="hljs-string">"comp"</span>);<br></code></pre></td></tr></table></figure></li><li><p>kernel: rxe_requester</p><ul><li>rxe_requester从rxe_qp队列取出wqe,生成对应的skb_buff,然后下发给对应的rxe_dev设备后发送IP包</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_req.c</span><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_requester</span><span class="hljs-params">(<span class="hljs-keyword">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> (struct rxe_qp *)arg;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_pkt_info</span> <span class="hljs-title">pkt</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sk_buff</span> *<span class="hljs-title">skb</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_send_wqe</span> *<span class="hljs-title">wqe</span>;</span><br>...<br>wqe = req_next_wqe(qp); <br>opcode = next_opcode(qp, wqe, wqe->wr.opcode);<br>mask = rxe_opcode[opcode].mask;<br><span class="hljs-comment">// read payload from mem or inline</span><br>...<br><span class="hljs-comment">// set ib packet</span><br>pkt.rxe = rxe;<br>pkt.opcode = opcode;<br>pkt.qp = qp;<br>pkt.psn = qp->req.psn;<br>pkt.mask = rxe_opcode[opcode].mask;<br>pkt.wqe = wqe;<br>av = rxe_get_av(&pkt, &ah);<br>...<br><span class="hljs-comment">// set sk buffer</span><br>skb = init_req_packet(qp, wqe, opcode, payload, &pkt);<br>...<br>ret = finish_packet(qp, av, wqe, &pkt, skb, payload);<br>ret = rxe_xmit_packet(qp, &pkt, skb);<br>...<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rxe_xmit_packet</span><span class="hljs-params">(struct rxe_qp *qp, struct rxe_pkt_info *pkt,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct sk_buff *skb)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> err;<br><span class="hljs-keyword">int</span> is_request = pkt->mask & RXE_REQ_MASK;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_dev</span> *<span class="hljs-title">rxe</span> =</span> to_rdev(qp->ibqp.device);<br>...<br>rxe_icrc_generate(skb, pkt);<br><br><span class="hljs-keyword">if</span> (pkt->mask & RXE_LOOPBACK_MASK)<br>err = rxe_loopback(skb, pkt);<br><span class="hljs-keyword">else</span><br>err = rxe_send(skb, pkt);<br>...<br><span class="hljs-keyword">if</span> ((qp_type(qp) != IB_QPT_RC) &&<br> (pkt->mask & RXE_END_MASK)) {<br>pkt->wqe->state = wqe_state_done;<br>rxe_run_task(&qp->comp.task, <span class="hljs-number">1</span>);<br>}<br><br>rxe_counter_inc(rxe, RXE_CNT_SENT_PKTS);<br>...<br>}<br><br></code></pre></td></tr></table></figure></li></ol><h3 id="4-2-MLX5调用栈分析"><a href="#4-2-MLX5调用栈分析" class="headerlink" title="4.2 MLX5调用栈分析"></a>4.2 MLX5调用栈分析</h3><ol><li>将Work Request链表写入mmap到用户态地址空间的QP send queue buffer中<ul><li>这里通过MMIO的方式访问位于PCI上的内存区间</li></ul></li><li>全部Work Request写完后,敲一下硬件的doorbell<ul><li><p>将一个ctrl结构体的地址通过MMIO的方式写入硬件的doorbell寄存器</p><p> // rdma-core/providers/mlx5/qp.c</p><p> <code>mmio_write64_be(bf->reg + bf->offset, *(__be64 *)ctrl);</code></p></li></ul></li></ol><h2 id="4-ibv-post-recv"><a href="#4-ibv-post-recv" class="headerlink" title="4. ibv_post_recv"></a>4. ibv_post_recv</h2><h3 id="4-1-RXE调用栈分析"><a href="#4-1-RXE调用栈分析" class="headerlink" title="4.1 RXE调用栈分析"></a>4.1 RXE调用栈分析</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c">ibv_post_recv<br>|-- rxe_post_recv<br>|-- rxe_post_one_recv<br></code></pre></td></tr></table></figure><ol><li><p>rxe_post_recv</p><ul><li>和post_send大同小异,向Recv Queue中写入recv work request</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_post_recv</span><span class="hljs-params">(struct ibv_qp *ibqp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_recv_wr *recv_wr,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct ibv_recv_wr **bad_wr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> rc = <span class="hljs-number">0</span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_qp</span> *<span class="hljs-title">qp</span> =</span> to_rqp(ibqp);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_wq</span> *<span class="hljs-title">rq</span> =</span> &qp->rq;<br>...<br>pthread_spin_lock(&rq->lock);<br><br><span class="hljs-keyword">while</span> (recv_wr) {<br>rc = rxe_post_one_recv(rq, recv_wr);<br><span class="hljs-keyword">if</span> (rc) {<br>*bad_wr = recv_wr;<br><span class="hljs-keyword">break</span>;<br>}<br><br>recv_wr = recv_wr->next;<br>}<br><br>pthread_spin_unlock(&rq->lock);<br><br><span class="hljs-keyword">return</span> rc;<br>}<br></code></pre></td></tr></table></figure></li><li><p>rxe_post_one_recv</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_post_one_recv</span><span class="hljs-params">(struct rxe_wq *rq, struct ibv_recv_wr *recv_wr)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> i;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_recv_wqe</span> *<span class="hljs-title">wqe</span>;</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_queue_buf</span> *<span class="hljs-title">q</span> =</span> rq-><span class="hljs-built_in">queue</span>;<br><span class="hljs-keyword">int</span> length = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">int</span> rc = <span class="hljs-number">0</span>;<br>...<br>wqe = (struct rxe_recv_wqe *)producer_addr(q);<br><br>wqe->wr_id = recv_wr->wr_id;<br>wqe->num_sge = recv_wr->num_sge;<br><br><span class="hljs-built_in">memcpy</span>(wqe->dma.sge, recv_wr->sg_list,<br> wqe->num_sge*<span class="hljs-keyword">sizeof</span>(*wqe->dma.sge));<br><br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < wqe->num_sge; i++)<br>length += wqe->dma.sge[i].length;<br><br>wqe->dma.length = length;<br>wqe->dma.resid = length;<br>wqe->dma.cur_sge = <span class="hljs-number">0</span>;<br>wqe->dma.num_sge = wqe->num_sge;<br>wqe->dma.sge_offset = <span class="hljs-number">0</span>;<br><br>advance_producer(q);<br><br>out:<br><span class="hljs-keyword">return</span> rc;<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="5-ibv-poll-cq"><a href="#5-ibv-poll-cq" class="headerlink" title="5. ibv_poll_cq"></a>5. ibv_poll_cq</h2><h3 id="5-1-RXE调用栈分析"><a href="#5-1-RXE调用栈分析" class="headerlink" title="5.1 RXE调用栈分析"></a>5.1 RXE调用栈分析</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c">ibv_poll_cq<br>|-- rxe_poll_cq<br></code></pre></td></tr></table></figure><ol><li><p>rxe_poll_cq</p><ul><li>从CQ buffer中读取多个Work Completions,写入到应用程序传递下来的 wc buffer中</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// rdma-core/providers/rxe/rxe.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">rxe_poll_cq</span><span class="hljs-params">(struct ibv_cq *ibcq, <span class="hljs-keyword">int</span> ne, struct ibv_wc *wc)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_cq</span> *<span class="hljs-title">cq</span> =</span> to_rcq(ibcq);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_queue_buf</span> *<span class="hljs-title">q</span>;</span><br><span class="hljs-keyword">int</span> npolled;<br><span class="hljs-keyword">uint8_t</span> *src;<br><br>pthread_spin_lock(&cq->lock);<br>q = cq-><span class="hljs-built_in">queue</span>;<br><br><span class="hljs-keyword">for</span> (npolled = <span class="hljs-number">0</span>; npolled < ne; ++npolled, ++wc) {<br><span class="hljs-keyword">if</span> (queue_empty(q))<br><span class="hljs-keyword">break</span>;<br><br>src = consumer_addr(q);<br><span class="hljs-built_in">memcpy</span>(wc, src, <span class="hljs-keyword">sizeof</span>(*wc));<br>advance_consumer(q);<br>}<br><br>pthread_spin_unlock(&cq->lock);<br><span class="hljs-keyword">return</span> npolled;<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="6-RXE对于收包的处理"><a href="#6-RXE对于收包的处理" class="headerlink" title="6. RXE对于收包的处理"></a>6. RXE对于收包的处理</h2><ol><li><p>UDP隧道建立时设置回调函数</p><ul><li>rdma_rxe module在初始化的时候,会创建一个UDP tunnel,其回调函数被设置为rxe_udp_encap_rec</li></ul> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_net.c</span><br><span class="hljs-function"><span class="hljs-keyword">static</span> struct socket *<span class="hljs-title">rxe_setup_udp_tunnel</span><span class="hljs-params">(struct net *net, __be16 port,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">bool</span> ipv6)</span></span><br><span class="hljs-function"></span>{<br>...<br>udp_cfg.local_udp_port = port;<br><br><span class="hljs-comment">/* Create UDP socket */</span><br>err = udp_sock_create(net, &udp_cfg, &sock);<br>...<br>tnl_cfg.encap_type = <span class="hljs-number">1</span>;<br>tnl_cfg.encap_rcv = rxe_udp_encap_recv;<br><br><span class="hljs-comment">/* Setup UDP tunnel */</span><br>setup_udp_tunnel_sock(net, sock, &tnl_cfg);<br><br><span class="hljs-keyword">return</span> sock;<br>}<br></code></pre></td></tr></table></figure></li><li><p>rxe_udp_encap_recv → rxe_rcv</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_recv.c</span><br><span class="hljs-comment">/* rxe_rcv is called from the interface driver */</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">rxe_rcv</span><span class="hljs-params">(struct sk_buff *skb)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">int</span> err;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_pkt_info</span> *<span class="hljs-title">pkt</span> =</span> SKB_TO_PKT(skb);<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">rxe_dev</span> *<span class="hljs-title">rxe</span> =</span> pkt->rxe;<br>...<br>pkt->opcode = bth_opcode(pkt);<br>pkt->psn = bth_psn(pkt);<br>pkt->qp = <span class="hljs-literal">NULL</span>;<br>pkt->mask |= rxe_opcode[pkt->opcode].mask;<br>...<br>err = rxe_icrc_check(skb, pkt);<br>...<br>rxe_counter_inc(rxe, RXE_CNT_RCVD_PKTS);<br><br><span class="hljs-keyword">if</span> (unlikely(bth_qpn(pkt) == IB_MULTICAST_QPN))<br>rxe_rcv_mcast_pkt(rxe, skb);<br><span class="hljs-keyword">else</span><br>rxe_rcv_pkt(pkt, skb);<br><br><span class="hljs-keyword">return</span>;<br>...<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-keyword">void</span> <span class="hljs-title">rxe_rcv_pkt</span><span class="hljs-params">(struct rxe_pkt_info *pkt, struct sk_buff *skb)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">if</span> (pkt->mask & RXE_REQ_MASK)<br>rxe_resp_queue_pkt(pkt->qp, skb);<br><span class="hljs-keyword">else</span><br>rxe_comp_queue_pkt(pkt->qp, skb);<br>}<br></code></pre></td></tr></table></figure></li><li><p>rxe_responder</p><ul><li><p>如果收到的包是RDMA请求的话(RDMA_READ/RDMA_WRITE/RDMA_SEND_REQ)</p></li><li><p>如果收到包的状态是RESPST_COMPLETE的话,说明一次RDMA通信完成,RXE kernel模块会在CQ中写入一个WR</p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-comment">// kernel/drivers/infiniband/sw/rxe/rxe_resp.c </span><br><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">enum</span> resp_states <span class="hljs-title">do_complete</span><span class="hljs-params">(struct rxe_qp *qp,</span></span><br><span class="hljs-params"><span class="hljs-function"> struct rxe_pkt_info *pkt)</span></span><br><span class="hljs-function"></span>{...}<br></code></pre></td></tr></table></figure></li></ul></li></ol>]]></content>
<tags>
<tag>RDMA</tag>
<tag>libRXE</tag>
<tag>Soft-ROCE</tag>
<tag>libibverbs</tag>
</tags>
</entry>
<entry>
<title>体验rust-for-linux</title>
<link href="/202206/rust-for-linux-01/"/>
<url>/202206/rust-for-linux-01/</url>
<content type="html"><![CDATA[<h2 id="编译x86-kernel-构建文件系统"><a href="#编译x86-kernel-构建文件系统" class="headerlink" title="编译x86 kernel + 构建文件系统"></a>编译x86 kernel + 构建文件系统</h2><ol><li><p>Rust依赖安装</p><ol><li>参考:<a href="https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/quick-start.rst">Rust for linux: Quick Start</a></li><li>使用用cargo/rustup安装rustc + rust-src + <strong><strong>bindgen</strong></strong></li><li>安装LLVM-14<ol><li>参考<a href="https://apt.llvm.org/">https://apt.llvm.org/</a></li><li><code>bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"</code></li></ol></li></ol></li><li><p>x86生成配置+编译内核</p><ol><li><p>需要注意的选项</p><ol><li>CONFIG_SYSTEM_TRUSTED_KEYS 设置为空</li><li>CONFIG_SYSTEM_REVOCATION_KEYS 设置为空</li><li>MODVERSIONS + GCC_PLUGINS 这两个配置默认是y,需要关闭才能打开CONFIG_RUST</li></ol></li><li><p>编译内核+模块</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">make LLVM=-14 O=../rust-kernel-build/amd64 -j 64<br></code></pre></td></tr></table></figure></li><li><p>安装内核模块</p><p> INSTALL_MOD_PATH 是相对于内核编译路径设置的,不是当前内核源码路径</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo make LLVM=-14 O=../rust-kernel-build/amd64 INSTALL_MOD_PATH=../../../debian11-amd64-rootfs modules_install<br></code></pre></td></tr></table></figure></li></ol></li><li><p>编译独立的内核模块</p><ol><li><p><a href="https://github.com/Rust-for-Linux/rust-out-of-tree-module">rust-out-of-tree-module repo</a></p></li><li><p>改一下Makefile,加上modules_install 内容</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">❯ cat Makefile<br><span class="hljs-comment"># SPDX-License-Identifier: GPL-2.0</span><br><br>KDIR ?= /lib/modules/`uname -r`/build<br>INSTALL_MOD_PATH ?= /<br>default:<br>$(MAKE) -C $(KDIR) M=$<span class="hljs-variable">$PWD</span><br>modules_install:<br>$(MAKE) -C $(KDIR) M=$<span class="hljs-variable">$PWD</span> modules_install<br></code></pre></td></tr></table></figure></li><li><p>编译独立的模块,并安装</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">make LLVM=-14 KDIR=../rust-kernel-build/amd64<br>make LLVM=-14 KDIR=../rust-kernel-build/amd64 INSTALL_MOD_PATH=../../../debian11-arm64-rootfs modules_install<br></code></pre></td></tr></table></figure></li></ol></li><li><p>构建文件系统</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs bash">dd <span class="hljs-keyword">if</span>=/dev/zero of=./debian11-amd64-rootfs.img bs=1M count=10240<br>mkfs -t ext4 ./debian11-amd64-rootfs.img<br>sudo mkdir debian11-amd64-rootfs<br>sudo mount debian11-amd64-rootfs.img debian11-amd64-rootfs<br><span class="hljs-comment"># 下载debian 11 rootfs</span><br>sudo debootstrap --arch amd64 stable debian11-amd64-rootfs http://ftp.debian.org/debian<br><span class="hljs-comment"># 根据自己习惯配置一下rootfs</span><br>sudo chroot debian11-amd64-rootfs<br><br>apt-get update<br>apt-get install sudo vim bash-completion -y<br>apt-get install net-tools ethtool ifupdown network-manager iputils-ping -y<br>apt-get install rsyslog resolvconf udev -y<br>apt-get install vim net-tools openssh-server curl git build-essential -y<br><span class="hljs-built_in">echo</span> <span class="hljs-string">"debian"</span> >/etc/hostname<br><span class="hljs-built_in">echo</span> <span class="hljs-string">"127.0.0.1 localhost"</span> >/etc/hosts<br><br>dpkg-reconfigure resolvconf<br>dpkg-reconfigure tzdata<br><span class="hljs-built_in">exit</span><br></code></pre></td></tr></table></figure></li><li><p>Qemu启动脚本</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br><br>KRNL_IMGE=<kernel-build-dir>/amd64/arch/x86_64/boot/bzImage<br>ROOT_FILE=debian11-amd64-rootfs.img<br>QEMU_BINY=./x86_64-qemu/qemu-system-x86_64<br><br>sudo <span class="hljs-variable">${QEMU_BINY}</span> \<br> -name <span class="hljs-string">"rust-for-linux-VM"</span> \<br> -enable-kvm \<br> -cpu host \<br> -smp 4 \<br> -m 16G \<br> -kernel <span class="hljs-variable">${KRNL_IMGE}</span> \<br> -hda <span class="hljs-variable">${ROOT_FILE}</span> -append <span class="hljs-string">"console=ttyS0 root=/dev/sda rw"</span> \<br> -serial mon:stdio \<br> -net user,hostfwd=tcp::38080-:22 \<br> -net nic,model=virtio \<br> -nographic \<br> -qmp unix:./qmp-amd64-sock,server,nowait<br></code></pre></td></tr></table></figure></li><li><p>测试独立的模块</p><p> 用新编译的内核+文件系统启动qemu</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs bash">root@debian:~<span class="hljs-comment"># modprobe rust_out_of_tree</span><br>[ 149.540799] rust_out_of_tree: loading out-of-tree module taints kernel.<br>[ 149.548570] rust_out_of_tree: Rust out-of-tree sample (init)<br>root@debian:~<span class="hljs-comment"># modinfo rust_out_of_tree</span><br>filename: /lib/modules/5.19.0-rc1-156671-gabd32d188969/extra/rust_out_of_tree.ko<br>author: Rust <span class="hljs-keyword">for</span> Linux Contributors<br>description: Rust out-of-tree sample<br>license: GPL v2<br>vermagic: 5.19.0-rc1-156671-gabd32d188969 SMP preempt mod_unload aarch64<br>name: rust_out_of_tree<br>depends:<br>root@debian:~<span class="hljs-comment"># rmmod rust_out_of_tree</span><br>[ 210.345721] rust_out_of_tree: My message is on the heap!<br>[ 210.345961] rust_out_of_tree: Rust out-of-tree sample (<span class="hljs-built_in">exit</span>)<br></code></pre></td></tr></table></figure></li></ol><h2 id="交叉编译Arm64-kernel"><a href="#交叉编译Arm64-kernel" class="headerlink" title="交叉编译Arm64 kernel"></a>交叉编译Arm64 kernel</h2><p>对于LLVM交叉编译而言,不需要像gcc一样安装不同架构的工具链,仅是需要给<code>clang</code>传递<code>-target=<triple></code> 参数,所以执行编译内核时,设置<code>ARCH</code>变量即可,不需要设置<code>CROSS_COMPILE</code> 变量,详细的LLVM编译内核文档可以参考:<a href="https://docs.kernel.org/kbuild/llvm.html">Building Linux with Clang/LLVM</a></p><ol><li><p>config中打开 CONFIG_RUST 相关选项</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">make LLVM=-14 ARCH=arm64 O=../rust-kernel-build/arm64 menuconfig<br></code></pre></td></tr></table></figure></li><li><p>编译内核/内核模块</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">make LLVM=-14 ARCH=arm64 O=../rust-kernel-build/arm64 -j 64<br></code></pre></td></tr></table></figure></li><li><p>安装内核模块,需要指定根文件目录</p><p> 安装完成后,内核模块会被安装到 <code>../../debian11-arm64-rootfs/lib/modules/</code> 目录下</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo make LLVM=-14 ARCH=arm64 O=../rust-kernel-build/arm64 INSTALL_MOD_PATH=../../../debian11-arm64-rootfs modules_install<br></code></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag>Rust</tag>
<tag>kernel</tag>
<tag>Rust-for-linux</tag>
</tags>
</entry>
<entry>
<title>RDMA入门简介</title>
<link href="/202205/rdma-ofa-training-1/"/>
<url>/202205/rdma-ofa-training-1/</url>
<content type="html"><![CDATA[<p>本文是对 <a href="https://www.openfabrics.org/training/">OFA Training</a> 第一部分的总结。</p><p>这是一份很好的学习RDMA的入门材料,从原理讲起,更容易理解。</p><h2 id="关键术语"><a href="#关键术语" class="headerlink" title="关键术语"></a>关键术语</h2><h3 id="1-RDMA-–-Remote-Direct-Memory-Acces"><a href="#1-RDMA-–-Remote-Direct-Memory-Acces" class="headerlink" title="1. RDMA – Remote Direct Memory Acces"></a>1. <strong>RDMA – Remote Direct Memory Acces</strong></h3><p>一种应用程序之间的通信机制</p><ol><li>Remote:说明通信双方处于远端</li><li>Direct:说明通信不需要“更高权限者”的参与</li><li>Memory:说明数据需要从应用A的虚拟地址转移到应用B的虚拟地址</li></ol><p><img src="/img/rdma_ofa_training_part1/Untitled.png"></p><h3 id="2-Verbs"><a href="#2-Verbs" class="headerlink" title="2. Verbs"></a>2. Verbs</h3><p>应用程序用来控制、执行RDMA操作的API</p><p><img src="/img/rdma_ofa_training_part1/Untitled%201.png"></p><h3 id="3-Channel-Adapter-CA"><a href="#3-Channel-Adapter-CA" class="headerlink" title="3. Channel Adapter - CA"></a>3. <strong>Channel Adapter - CA</strong></h3><p>允许应用程序直接进行RDMA操作的I/O设备</p><p>CA的概念中包含了硬件和软件两个部分</p><p>CA目前有三种类型</p><ol><li>iWARP</li><li>InfiniBand</li><li>RocE</li></ol><h3 id="4-Requester-Responder"><a href="#4-Requester-Responder" class="headerlink" title="4. Requester, Responder"></a>4. <strong>Requester, Responder</strong></h3><p><img src="/img/rdma_ofa_training_part1/Untitled%202.png"></p><ol><li>RDMA请求发送方可能发送一个“READ”、“WRITE”请求到RDMA接收方</li><li>WRITE请求是将发送方写数据到接收方</li><li>READ请求是发送方读数据到接收方</li></ol><h2 id="RDMA简介"><a href="#RDMA简介" class="headerlink" title="RDMA简介"></a>RDMA简介</h2><h3 id="回顾现有的I-x2F-O机制"><a href="#回顾现有的I-x2F-O机制" class="headerlink" title="回顾现有的I/O机制"></a>回顾现有的I/O机制</h3><ol><li>按照冯诺依曼体系结构来看,现有的主机通常包括:CPU、MEM、IO子系统</li><li>所有的硬件资源被OS所管理:应用程序想要使用IO资源需要通过OS申请、访问、释放。<ol><li><p>应用程序使用的是虚拟地址</p></li><li><p>IO设备使用的是物理地址</p></li><li><p>地址的转换完全由OS来管理</p><blockquote><p>注:IO设备也可能用虚地址访存,参考IOMMU、SMMU</p></blockquote></li></ol></li><li>应用程序几乎所有的IO操作,都需要发起系统调用,由OS完成<ol><li>应用程序通常IO操作包括三类:存储、网络、IPC</li></ol></li></ol><h3 id="一种新的IO机制:Channel-IO"><a href="#一种新的IO机制:Channel-IO" class="headerlink" title="一种新的IO机制:Channel IO"></a>一种新的IO机制:Channel IO</h3><ol><li><p>IO Channel可以在两个应用程序间建立直接的IO通道</p><ol><li>OS负责创建IO通道,但OS不负责IO传输</li><li>逻辑上看如下图</li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%203.png" alt="Untitled"> </p></li><li><p>IO通道物理结构如下图</p><ol><li>一个IO通道可以在两个Channel Adapters上建立</li><li>IO通道对上层应用提供API进行控制、传输,称为<em>channel interface</em></li><li>channel interface是运行在用户态的</li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%204.png" alt="Untitled"></p></li></ol><h2 id="消息传递"><a href="#消息传递" class="headerlink" title="消息传递"></a>消息传递</h2><h2 id="地址翻译"><a href="#地址翻译" class="headerlink" title="地址翻译"></a>地址翻译</h2><h3 id="1-三次地址翻译概述"><a href="#1-三次地址翻译概述" class="headerlink" title="1. 三次地址翻译概述"></a>1. 三次地址翻译概述</h3><p>远程通信需要进行三次地址翻译,如下图所示</p><p><img src="/img/rdma_ofa_training_part1/Untitled%205.png"></p><h3 id="2-三次地址翻译实现方式"><a href="#2-三次地址翻译实现方式" class="headerlink" title="2. 三次地址翻译实现方式"></a>2. 三次地址翻译实现方式</h3><ol><li><p>基于OS实现三次地址翻译</p><ol><li>OS来完成虚拟→物理/物理→虚拟地址的翻译</li><li>OS实现的地址翻译可以保证,每个应用程序对应的虚拟地址都是独立的。</li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%206.png"> </p></li><li><p>基于通道适配器CA来实现三次地址翻译</p><ol><li><p>通道适配器来完成地址翻译的话则不需要OS介入,如下图所示</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%207.png"></p></li><li><p>地址映射表保存在通道适配器CA中,但是由OS创建;OS不介入到地址翻译的过程中</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%208.png"></p></li></ol></li></ol><h3 id="3-远端虚拟地址直接访问"><a href="#3-远端虚拟地址直接访问" class="headerlink" title="3. 远端虚拟地址直接访问"></a>3. 远端虚拟地址直接访问</h3><ol><li><p>一个IO通道可以暴露给应用程序A一段本机不存在的虚拟地址</p><ol><li>换言之,这段虚拟地址被映射到了远端的应用程序。</li><li>RDMA技术就是在解决:应用程序A如何直接访问远端应用程序的虚拟地址?</li><li></li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%209.png" alt="**Virtual-to-virtual transfers**"></p></li></ol><h2 id="RDMA通信原理简介"><a href="#RDMA通信原理简介" class="headerlink" title="RDMA通信原理简介"></a>RDMA通信原理简介</h2><h3 id="回顾socket通信原理"><a href="#回顾socket通信原理" class="headerlink" title="回顾socket通信原理"></a>回顾socket通信原理</h3><p><img src="/img/rdma_ofa_training_part1/Untitled%2010.png"></p><ol><li>socket应用程序会将应用程序要发送的数据,从应用缓冲区拷贝到内核缓冲区中。</li><li>OS中的TCP栈负责将内核缓冲区的字节传输到远端。</li><li>OS中的TCP栈只负责传输字节(这里没有消息的概念),是字节流</li></ol><h3 id="RDMA通信架构"><a href="#RDMA通信架构" class="headerlink" title="RDMA通信架构"></a>RDMA通信架构</h3><ol><li><p>大概可以分为三层:软件层接口、RDMA协议层、网络传输层</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2011.png"></p></li></ol><h3 id="软件层接口"><a href="#软件层接口" class="headerlink" title="软件层接口"></a>软件层接口</h3><ol><li><p>软件层接口</p><ol><li>提供一个应用程序可以访问的队列接口,用来将应用程序发来的请求保序</li><li>是一个面向消息的架构(区别于socket是面向字节流的)</li></ol></li><li><p>队列接口</p><ol><li>包含两种队列<ol><li>QP:等待被执行的任务队列(Queue Pair)</li><li>CQ:已经完成的任务队列(Completion queue)</li></ol></li><li>CQ中的一个标识可能标志多个QP中的任务已完成</li></ol></li><li><p>Queue Pair</p><ol><li><p>如下图所示</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2012.png"></p></li><li><p>QP总是成对出现的,包含一个发送队列、一个接受队列</p></li><li><p>一个应用程序可以创建多个QP,一个QP只对应一个应用程序</p></li></ol></li><li><p>Work requests 任务请求</p><ol><li><p>一个 Work requests ,WR 描述了一个等待被执行的任务,可能是一个发送任务,也可能是一个接受任务</p></li><li><p>应用程序发起一个任务请求,任务请求会被保存到队列中,称为Post a WR</p><ol><li>Work Queue中的一个Work reqeuest称为Work Queue Element (WQE – pronounced wookie)</li><li>completion queue entry 称为 CQEs ,pronounced cookie</li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%2013.png"></p></li></ol></li><li><p>Queue接口是异步的</p><ol><li>应用程序发起请求后,不等待任务完成</li><li>一旦一个或多个任务被完成,CQ中会放入CQE来通知应用程序</li></ol></li><li><p>QP队列中的WRE序列问题</p><ol><li>一个WR(QP中的一个队列)中的所有发送请求是严格保序的</li><li>一个WR中的所有接受请求是严格保序的</li><li>发送队列和接受队列中的元素没有序列要求</li><li>QP之间没有序列要求</li></ol></li><li><p>QP的连接</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2014.png"></p><ol><li>一旦两个应用程序通过设备相连,两个应用程序对应的QP也就连接起来了</li><li>应用A向发送队列写入的WR,会发送到通道的另一端。</li><li>再一次强调,一个QP只能用于一个应用程序</li></ol></li><li><p>软件层接口的使用</p><ol><li>用户创建一个用来收/发消息的 Work Request</li><li>为了发送消息,用户使用 <em><strong>Post Send Request , or Post Receive Request Verbs(API)</strong></em> 来将这个WR填充到到某个QP的发送队列或者接受队列</li></ol></li></ol><h3 id="RDMA协议层"><a href="#RDMA协议层" class="headerlink" title="RDMA协议层"></a>RDMA协议层</h3><ol><li><p>RDMA协议层主要用于地址的转换</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2015.png"></p></li><li><p>RDMA的消息种类</p><ol><li>SEND/RECEIVE: 进行通道IO<ol><li><p>如下图所示</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2016.png"></p></li><li><p>流程如下所示</p><ol><li>请求方Post一个SEND WR到对应的发送队列中<ol><li>SEND WQE中描述了发送端buffer的信息,以及目的端口号DLID、目的QP号</li></ol></li><li>相应方Post一个RECEIVE WR到对应的接受队列中<ol><li>RECEIVE WR描述了下一个接受的信息可以用的内存地址</li></ol></li><li>接受端buffer信息一定要先于发送端信息被post</li></ol></li></ol></li><li>RDMA READ/WRITE :进行远端内存操作<ol><li><p>响应方的buffer由“虚拟地址 + RDMA Key”组成的描述符所描述</p><ol><li>响应方需要将本端的buffer描述符通过SEND 消息传递给请求方</li><li>请求方使用收到的描述符,对远端内存进行READ/WRITE操作,之后响应方对buffer不可读写</li><li>直到请求方通知响应方WR/RD完成,响应方的应用程序才能读写buffer</li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%2017.png"> </p></li><li><p>RDMA Write</p><p> 响应方的CA处理dst VA → dst PA 的地址翻译</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2018.png"></p></li><li><p>RDMA READ</p><p> 左侧是请求方,右侧是响应方</p><p> 请求方的CA处理src VA到src PA的地址转换(读回的数据写入到哪里的问题)</p><p> <img src="/img/rdma_ofa_training_part1/Untitled%2019.png"></p></li></ol></li></ol></li><li><p>RDMA 协议层总结</p><ol><li>SEND/RECEIVE 请求是一个端到端的操作</li><li>一个SEND操作,会消耗一个请求方QP的WQE,也会消耗一个响应方QP的WQE</li><li>RECEIVE WQE 一定要在 SEND WQE 之前发出</li><li>RDMA READ/WRITE 提供了应用程序可以直接访问远端内存缓冲的方法</li></ol></li></ol><h3 id="网络传输层"><a href="#网络传输层" class="headerlink" title="网络传输层"></a>网络传输层</h3><ol><li>传输层必须对上提供可靠的、面向连接的服务</li><li>“可靠”的含义:<strong>数据必须按序交付给接收方,并且不能出现重复和错误</strong><ol><li>不能出现错误:如果出现错误,可靠的服务要么可以从字节序列中恢复错误(比如编码方式可以容错),要么报告给应用程序</li></ol></li><li>“连接”的含义<ol><li>连接指的是两个主机的两队QP进行连接</li></ol></li></ol><h2 id="Verbs-简介"><a href="#Verbs-简介" class="headerlink" title="Verbs 简介"></a>Verbs 简介</h2><p>Verbs API是应用程序进行RDMA消息传递的编程接口,如下图所示</p><p>可以想像成底层为两端的QP对/两端的CQ对建立了虚拟的IO通道</p><p><img src="/img/rdma_ofa_training_part1/Untitled%2020.png"></p><h3 id="应用程序使用channel-IO-进行通信的流程"><a href="#应用程序使用channel-IO-进行通信的流程" class="headerlink" title="应用程序使用channel IO 进行通信的流程"></a>应用程序使用channel IO 进行通信的流程</h3><ol><li>打开一个HCA(Host CA)</li><li>创建一个Protection Domain</li><li>创建一个Queue Pair</li><li>创建一个/或一组 Completion Queue</li><li>注册内存区域Memory Regions</li><li>发起任务请求Work reqeust</li><li>等待CQ的完成任务通知</li></ol><h3 id="Verbs的含义"><a href="#Verbs的含义" class="headerlink" title="Verbs的含义"></a>Verbs的含义</h3><ol><li>Verb一次最早由InfiniBand Architecture Specification提出,大约在1999年</li><li>一个Verb更多地是在描述一个动作“语义”,并没有描述编程接口,也与各个OS特性无关的</li><li>不同厂商的RDMA实现:RoCE、IB、IWARP,均对用户提供统一的Verbs API接口</li></ol><h2 id="RDMA的隔离和保护机制"><a href="#RDMA的隔离和保护机制" class="headerlink" title="RDMA的隔离和保护机制"></a>RDMA的隔离和保护机制</h2><ol><li>Protection Domain的隔离与保护<ol><li><p>每个PD和下列组件绑定在一起</p><ol><li>一个或者多个QPs</li><li>一段内存区域</li><li>一个应用程序</li><li></li></ol><p> <img src="/img/rdma_ofa_training_part1/Untitled%2021.png"> </p></li><li><p>PD由Verbs调用而分配出来</p></li><li><p>PD确保只有注册到该虚拟内存的QP才能访问它。</p></li></ol></li><li>Memory Registration<ol><li>应用程序可以向HCA注册一段内存区域</li><li>HCA返回这段内存的描述符<ol><li>Virtual Address</li><li>RKEY</li><li>LKEY</li></ol></li><li>LKEY是用来让应用程序访问这段内存的</li><li>RKEY是用来让远端应用程序访问这段内存的(将会传给远端的调用者,此时本地是响应者)</li></ol></li><li>Channel<ol><li>用户通过Memory Registration 方法来创建本进程绑定的WQ和CQ,<ol><li>创建出来的通道位于本进程的虚拟地址范围内。</li><li>读写访问不涉及内核态切换</li></ol></li><li>channel之间的隔离依靠OS提供的进程隔离机制</li></ol></li></ol>]]></content>
<tags>
<tag>RDMA</tag>
<tag>OFA</tag>
</tags>
</entry>
<entry>
<title>Rust标准库涉及的Trait</title>
<link href="/202205/rust-traits/"/>
<url>/202205/rust-traits/</url>
<content type="html"><![CDATA[<p>本篇文章是对下面文章的阅读笔记,不保证可靠性,希望大家还是以Rust标准库源码/英文原文为准</p><p><a href="https://github.com/pretzelhammer/rust-blog/blob/master/posts/tour-of-rusts-standard-library-traits.md">Tour of Rust’s Standard Library Traits</a></p><p>本文亦参考了下面这篇文章,但该文章排版有混乱的地方<br><a href="https://rustmagazine.github.io/rust_magazine_2021/chapter_7/rusts-standard-library-traits.html">Tour of Rust’s Standard Library Traits中译</a></p><h2 id="Trait基础"><a href="#Trait基础" class="headerlink" title="Trait基础"></a>Trait基础</h2><h3 id="1-Self指代实现的类型"><a href="#1-Self指代实现的类型" class="headerlink" title="1. Self指代实现的类型"></a>1. Self指代实现的类型</h3><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span> {<br> <span class="hljs-comment">// always returns i32</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">returns_num</span></span>() -> <span class="hljs-built_in">i32</span>;<br><br> <span class="hljs-comment">// returns implementing type</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">returns_self</span></span>() -> <span class="hljs-keyword">Self</span>;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OtherType</span></span>;<br><br><span class="hljs-keyword">impl</span> Trait <span class="hljs-keyword">for</span> SomeType {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">returns_num</span></span>() -> <span class="hljs-built_in">i32</span> {<br> <span class="hljs-number">5</span><br> }<br><br> <span class="hljs-comment">// Self == SomeType</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">returns_self</span></span>() -> <span class="hljs-keyword">Self</span> {<br> SomeType<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="2-Function"><a href="#2-Function" class="headerlink" title="2. Function"></a>2. Function</h3><ol><li>指代第一个参数不是self的任意函数,类似其他语言的静态函数</li><li>只能通过 Typename::func() 方式调用。这里的type实现了trait</li></ol><h3 id="3-Method"><a href="#3-Method" class="headerlink" title="3. Method"></a>3. Method</h3><ol><li><p>指的是第一个参数是self的函数。其类型是 <code>Self</code> , <code>&Self</code>或 <code>&mut Self</code>。也可以被Box,Rc,Arc或Pin来包装。</p></li><li><p>通过 TraitName::func() 方式调用,或者 <实现trait类型的实例>.func() 调用</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> five = <span class="hljs-built_in">ToString</span>::to_string(&<span class="hljs-number">5</span>);<br> <span class="hljs-keyword">let</span> five = <span class="hljs-built_in">i32</span>::to_string(&<span class="hljs-number">5</span>);<br>}<br><span class="hljs-comment">// 5这里是i32类型,i32类型实现了ToString trait</span><br><span class="hljs-comment">// https://doc.rust-lang.org/std/string/trait.ToString.html</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> five = <span class="hljs-number">5</span>.to_string();<br>}<br><br></code></pre></td></tr></table></figure></li></ol><h3 id="4-Associated-Types-关联类型"><a href="#4-Associated-Types-关联类型" class="headerlink" title="4. Associated Types 关联类型"></a>4. Associated Types 关联类型</h3><ol><li><p>每个类型对这个trait的实现中,可以由实现者指定func中某个参数的类型</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">AssociatedType</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span>(arg: Self::AssociatedType);<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OtherType</span></span>;<br><br><span class="hljs-comment">// any type implementing Trait can</span><br><span class="hljs-comment">// choose the type of AssociatedType</span><br><br><span class="hljs-keyword">impl</span> Trait <span class="hljs-keyword">for</span> SomeType {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">AssociatedType</span></span> = <span class="hljs-built_in">i8</span>; <span class="hljs-comment">// chooses i8</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span>(arg: Self::AssociatedType) {}<br>}<br><br><span class="hljs-keyword">impl</span> Trait <span class="hljs-keyword">for</span> OtherType {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">AssociatedType</span></span> = <span class="hljs-built_in">u8</span>; <span class="hljs-comment">// chooses u8</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span>(arg: Self::AssociatedType) {}<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> SomeType::func(-<span class="hljs-number">1_i8</span>); <span class="hljs-comment">// can only call func with i8 on SomeType</span><br> OtherType::func(<span class="hljs-number">1_u8</span>); <span class="hljs-comment">// can only call func with u8 on OtherType</span><br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="5-Generic-Parameters-泛型参数"><a href="#5-Generic-Parameters-泛型参数" class="headerlink" title="5. Generic Parameters 泛型参数"></a>5. Generic Parameters 泛型参数</h3><ol><li><p>“泛型参数”泛指</p><ol><li>泛型类型参数(generic type parameters)</li><li>泛型生命周期参数(generic lifetime parameters)</li><li>泛型常量参数(generic const parameters)</li></ol></li><li><p>因为这些说起来比较拗口,所以人们通常把它们简称为 “泛型类型(generic type)”、“生命周期(lifetime)”和 “泛型常量(generic const)”。</p></li><li><p>本文不讨论泛型常量</p></li><li><p>一个泛型类型 + 生命周期的例子,可以通过<T = i32> 的方式赋默认类型</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// trait declaration generalized with lifetime & type parameters</span><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span><<span class="hljs-symbol">'a</span>, T> {<br> <span class="hljs-comment">// signature uses generic type</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func1</span></span>(arg: T);<br><br> <span class="hljs-comment">// signature uses lifetime</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func2</span></span>(arg: &<span class="hljs-symbol">'a</span> <span class="hljs-built_in">i32</span>);<br><br> <span class="hljs-comment">// signature uses generic type & lifetime</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func3</span></span>(arg: &<span class="hljs-symbol">'a</span> T);<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br><br><span class="hljs-keyword">impl</span><<span class="hljs-symbol">'a</span>> Trait<<span class="hljs-symbol">'a</span>, <span class="hljs-built_in">i8</span>> <span class="hljs-keyword">for</span> SomeType {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func1</span></span>(arg: <span class="hljs-built_in">i8</span>) {}<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func2</span></span>(arg: &<span class="hljs-symbol">'a</span> <span class="hljs-built_in">i32</span>) {}<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func3</span></span>(arg: &<span class="hljs-symbol">'a</span> <span class="hljs-built_in">i8</span>) {}<br>}<br><br><span class="hljs-keyword">impl</span><<span class="hljs-symbol">'b</span>> Trait<<span class="hljs-symbol">'b</span>, <span class="hljs-built_in">u8</span>> <span class="hljs-keyword">for</span> SomeType {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func1</span></span>(arg: <span class="hljs-built_in">u8</span>) {}<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func2</span></span>(arg: &<span class="hljs-symbol">'b</span> <span class="hljs-built_in">i32</span>) {}<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func3</span></span>(arg: &<span class="hljs-symbol">'b</span> <span class="hljs-built_in">u8</span>) {}<br>}<br><br><span class="hljs-comment">// any type can be used as the default</span><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait2</span></span><T = <span class="hljs-built_in">i32</span>> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func2</span></span>(t: T) {}<br>}<br></code></pre></td></tr></table></figure></li><li><p>也可以只对trait中的单个函数/方法泛型化</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span><<span class="hljs-symbol">'a</span>, T>(t: &<span class="hljs-symbol">'a</span> T);<br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="6-泛型-vs-关联类型"><a href="#6-泛型-vs-关联类型" class="headerlink" title="6. 泛型 vs 关联类型"></a>6. 泛型 vs 关联类型</h3><p>相同点:</p><ul><li>都把在 trait 的函数和方法中使用哪种具体类型的决定权交给了实现者</li></ul><p>不同点:</p><ul><li>当每个类型只应该有 trait 的一个实现时,使用关联类型。</li><li>当每个类型可能会有 trait 的多个实现时,使用泛型类型。</li></ul><ol><li>一个例子 Point 类型实现了Add这个trait,目前实现采用了关联类型,因此只能实现对一个类型的add func 实现Point类型对i32类型的add 方法会报错 <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Add</span></span> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Rhs</span></span>;<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add</span></span>(<span class="hljs-keyword">self</span>, rhs: Self::Rhs) -> Self::Output;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Add <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Rhs</span></span> = Point;<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = Point;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add</span></span>(<span class="hljs-keyword">self</span>, rhs: Point) -> Point {<br> Point {<br> x: <span class="hljs-keyword">self</span>.x + rhs.x,<br> y: <span class="hljs-keyword">self</span>.y + rhs.y,<br> }<br> }<br>}<br><br><span class="hljs-keyword">impl</span> Add <span class="hljs-keyword">for</span> Point { <span class="hljs-comment">// ❌</span><br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Rhs</span></span> = <span class="hljs-built_in">i32</span>;<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = Point;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add</span></span>(<span class="hljs-keyword">self</span>, rhs: <span class="hljs-built_in">i32</span>) -> Point {<br> Point {<br> x: <span class="hljs-keyword">self</span>.x + rhs,<br> y: <span class="hljs-keyword">self</span>.y + rhs,<br> }<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> p1 = Point { x: <span class="hljs-number">1</span>, y: <span class="hljs-number">1</span> };<br> <span class="hljs-keyword">let</span> p2 = Point { x: <span class="hljs-number">2</span>, y: <span class="hljs-number">2</span> };<br> <span class="hljs-keyword">let</span> p3 = p1.add(p2);<br> <span class="hljs-built_in">assert_eq!</span>(p3.x, <span class="hljs-number">3</span>);<br> <span class="hljs-built_in">assert_eq!</span>(p3.y, <span class="hljs-number">3</span>);<br><br> <span class="hljs-keyword">let</span> p1 = Point { x: <span class="hljs-number">1</span>, y: <span class="hljs-number">1</span> };<br> <span class="hljs-keyword">let</span> int2 = <span class="hljs-number">2</span>;<br> <span class="hljs-keyword">let</span> p3 = p1.add(int2); <span class="hljs-comment">// ❌</span><br> <span class="hljs-built_in">assert_eq!</span>(p3.x, <span class="hljs-number">3</span>);<br> <span class="hljs-built_in">assert_eq!</span>(p3.y, <span class="hljs-number">3</span>);<br>}<br></code></pre></td></tr></table></figure> 所以这里需要使用泛型来支持“多个类型在trait中的func会有多个实现“ 这里output也是关联类型,因此所有func实现的Output只能是一个类型 如果实现“点 + 点 = 线段”这种其他Output类型,则会报错。解决方法就是把Output也换成泛型 <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Add</span></span><Rhs> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add</span></span>(<span class="hljs-keyword">self</span>, rhs: Rhs) -> Self::Output;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Add<Point> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = <span class="hljs-keyword">Self</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add</span></span>(<span class="hljs-keyword">self</span>, rhs: Point) -> Self::Output {<br> Point {<br> x: <span class="hljs-keyword">self</span>.x + rhs.x,<br> y: <span class="hljs-keyword">self</span>.y + rhs.y,<br> }<br> }<br>}<br><br><span class="hljs-keyword">impl</span> Add<<span class="hljs-built_in">i32</span>> <span class="hljs-keyword">for</span> Point { <span class="hljs-comment">// ✅</span><br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = <span class="hljs-keyword">Self</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add</span></span>(<span class="hljs-keyword">self</span>, rhs: <span class="hljs-built_in">i32</span>) -> Self::Output {<br> Point {<br> x: <span class="hljs-keyword">self</span>.x + rhs,<br> y: <span class="hljs-keyword">self</span>.y + rhs,<br> }<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> p1 = Point { x: <span class="hljs-number">1</span>, y: <span class="hljs-number">1</span> };<br> <span class="hljs-keyword">let</span> p2 = Point { x: <span class="hljs-number">2</span>, y: <span class="hljs-number">2</span> };<br> <span class="hljs-keyword">let</span> p3 = p1.add(p2);<br> <span class="hljs-built_in">assert_eq!</span>(p3.x, <span class="hljs-number">3</span>);<br> <span class="hljs-built_in">assert_eq!</span>(p3.y, <span class="hljs-number">3</span>);<br><br> <span class="hljs-keyword">let</span> p1 = Point { x: <span class="hljs-number">1</span>, y: <span class="hljs-number">1</span> };<br> <span class="hljs-keyword">let</span> int2 = <span class="hljs-number">2</span>;<br> <span class="hljs-keyword">let</span> p3 = p1.add(int2); <span class="hljs-comment">// ✅</span><br> <span class="hljs-built_in">assert_eq!</span>(p3.x, <span class="hljs-number">3</span>);<br> <span class="hljs-built_in">assert_eq!</span>(p3.y, <span class="hljs-number">3</span>);<br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="7-作用域"><a href="#7-作用域" class="headerlink" title="7. 作用域"></a>7. 作用域</h3><ol><li><p>Trait 只有被显式地导入,才能使用其中定义的func</p><p> 比如Read trait就需要导入才能用</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::fs::File;<br><span class="hljs-keyword">use</span> std::io;<br><span class="hljs-keyword">use</span> std::io::Read; <span class="hljs-comment">// ✅</span><br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -> <span class="hljs-built_in">Result</span><(), io::Error> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> file = File::open(<span class="hljs-string">"Cargo.toml"</span>)?;<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buffer = <span class="hljs-built_in">String</span>::new();<br> file.read_to_string(&<span class="hljs-keyword">mut</span> buffer)?; <span class="hljs-comment">// ✅</span><br> <span class="hljs-literal">Ok</span>(())<br>}<br></code></pre></td></tr></table></figure></li><li><p>标准库预置是个例外,总是被隐式导入。(The standard library prelude)是标准库中的一个模块,也就是说,<code>std::prelude::v1</code>,它在每个其他模块的顶部被自动导入,即<code>use std::prelude::v1::*</code>。这样的话,下面这些 trait 就总会在作用域中,我们不需要自己显式地导入它们,因为它们是预置的一部分。</p><ul><li>AsMut</li><li>AsRef</li><li>Clone</li><li>Copy</li><li>Default</li><li>Drop</li><li>Eq</li><li>Fn</li><li>FnMut</li><li>FnOnce</li><li>From</li><li>Into</li><li>ToOwned</li><li>IntoIterator</li><li>Iterator</li><li>PartialEq</li><li>PartialOrd</li><li>Send</li><li>Sized</li><li>Sync</li><li>ToString</li><li>Ord</li></ul></li></ol><h3 id="8-派生宏(Derive-Macros)"><a href="#8-派生宏(Derive-Macros)" class="headerlink" title="8. 派生宏(Derive Macros)"></a>8. 派生宏(Derive Macros)</h3><p>标准库导出了一小部分派生宏,这么派生宏可以让我们可以便捷地在一个类型上实现 trait,前提是该类型的所有成员都实现了这个 trait。派生宏以它们所实现的 trait 来命名。</p><ul><li>Clone</li><li>Copy</li><li>Debug</li><li>Default</li><li>Eq</li><li>Hash</li><li>Ord</li><li>PartialEq</li><li>PartialOrd</li></ul><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// macro derives Copy & Clone impl for SomeType</span><br><span class="hljs-meta">#[derive(Copy, Clone)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br></code></pre></td></tr></table></figure><h3 id="9-Default-impls-默认实现"><a href="#9-Default-impls-默认实现" class="headerlink" title="9. Default impls 默认实现"></a>9. Default impls 默认实现</h3><ol><li><p>trait可以为定义的函数/方法提供默认实现</p><p> 标准库中的很多 trait 为很多它们的方法提供了默认实现。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">method</span></span>(&<span class="hljs-keyword">self</span>) {<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"default impl"</span>);<br> }<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OtherType</span></span>;<br><br><span class="hljs-comment">// use default impl for Trait::method</span><br><span class="hljs-keyword">impl</span> Trait <span class="hljs-keyword">for</span> SomeType {}<br><br><span class="hljs-keyword">impl</span> Trait <span class="hljs-keyword">for</span> OtherType {<br> <span class="hljs-comment">// use our own impl for Trait::method</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">method</span></span>(&<span class="hljs-keyword">self</span>) {<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"OtherType impl"</span>);<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> SomeType.method(); <span class="hljs-comment">// prints "default impl"</span><br> OtherType.method(); <span class="hljs-comment">// prints "OtherType impl"</span><br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="10-Generic-Blanket-Impls-泛型覆盖实现"><a href="#10-Generic-Blanket-Impls-泛型覆盖实现" class="headerlink" title="10. Generic Blanket Impls 泛型覆盖实现"></a>10. <strong><a href="https://rustmagazine.github.io/rust_magazine_2021/chapter_7/rusts-standard-library-traits.html#%E6%B3%9B%E5%9E%8B%E8%A6%86%E7%9B%96%E5%AE%9E%E7%8E%B0generic-blanket-impls">Generic Blanket Impls</a> 泛型覆盖实现</strong></h3><ol><li>trait中的方法/函数可以定义一个泛型实现<ol><li><p>一个例子</p><ol><li>类型T满足<code>Rem<Output = T> + PartialEq<T> + Sized,</code> 且类型u8可以转换为类型T</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#![allow(unused)]</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br><span class="hljs-keyword">use</span> std::fmt::<span class="hljs-built_in">Debug</span>;<br><span class="hljs-keyword">use</span> std::convert::TryInto;<br><span class="hljs-keyword">use</span> std::ops::Rem;<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Even</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_even</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">bool</span>;<br>}<br><br><span class="hljs-comment">// generic blanket impl</span><br><span class="hljs-keyword">impl</span><T> Even <span class="hljs-keyword">for</span> T<br><span class="hljs-keyword">where</span><br> T: Rem<Output = T> + <span class="hljs-built_in">PartialEq</span><T> + <span class="hljs-built_in">Sized</span>,<br> <span class="hljs-built_in">u8</span>: TryInto<T>,<br> <<span class="hljs-built_in">u8</span> <span class="hljs-keyword">as</span> TryInto<T>>::Error: <span class="hljs-built_in">Debug</span>,<br>{<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_even</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">bool</span> {<br> <span class="hljs-comment">// these unwraps will never panic</span><br> <span class="hljs-keyword">self</span> % <span class="hljs-number">2</span>.try_into().unwrap() == <span class="hljs-number">0</span>.try_into().unwrap()<br> }<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_is_even</span></span>() {<br> <span class="hljs-built_in">assert!</span>(<span class="hljs-number">2_i8</span>.is_even());<br> <span class="hljs-built_in">assert!</span>(<span class="hljs-number">4_u8</span>.is_even());<br> <span class="hljs-built_in">assert!</span>(<span class="hljs-number">6_i16</span>.is_even());<br> <span class="hljs-comment">// etc</span><br>}<br>}<br></code></pre></td></tr></table></figure></li><li><p>泛型覆盖实现提供了方法的实现,所以它们不能被重写。</p><p> trait 一致性是指,对于任意给定的类型,最多存在某一 trait 的一个实现。Rust 用来强制执行特质一致性的规则,这些规则的含义,以及针对这些含义的变通方案都不在本文的讨论范围之内。</p></li></ol></li></ol><h3 id="11-Subtraits-amp-Supertraits"><a href="#11-Subtraits-amp-Supertraits" class="headerlink" title="11. Subtraits & Supertraits"></a>11. <strong><a href="https://rustmagazine.github.io/rust_magazine_2021/chapter_7/rusts-standard-library-traits.html#subtraits--supertraits">Subtraits & Supertraits</a></strong></h3><p>子集、超集</p><ol><li><p>考虑如下的例子</p><ol><li>所有实现了Subtrait的类型是所有实现了Supertrait的类型的子集,或者反过来讲:所有实现了Supertrait的类型是所有实现了Subtrait类型的子集。</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Subtrait</span></span>: Supertrait {}<br></code></pre></td></tr></table></figure><p> 它等于</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Subtrait</span></span> <span class="hljs-keyword">where</span> <span class="hljs-keyword">Self</span>: Supertrait {}<br></code></pre></td></tr></table></figure></li><li><p>子集和超集说的是的是实现trait的类型:</p><ol><li>也就是说约束在<code>Self</code>上:实现<code>Subtrait</code>的类型</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Supertrait</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">method</span></span>(&<span class="hljs-keyword">self</span>) {<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"in supertrait"</span>);<br> }<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Subtrait</span></span>: Supertrait {<br> <span class="hljs-comment">// this looks like it might impl or</span><br> <span class="hljs-comment">// override Supertrait::method but it</span><br> <span class="hljs-comment">// does not</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">method</span></span>(&<span class="hljs-keyword">self</span>) {<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"in subtrait"</span>)<br> }<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br><br><span class="hljs-comment">// adds Supertrait::method to SomeType</span><br><span class="hljs-keyword">impl</span> Supertrait <span class="hljs-keyword">for</span> SomeType {}<br><br><span class="hljs-comment">// adds Subtrait::method to SomeType</span><br><span class="hljs-keyword">impl</span> Subtrait <span class="hljs-keyword">for</span> SomeType {}<br><br><span class="hljs-comment">// both methods exist on SomeType simultaneously</span><br><span class="hljs-comment">// neither overriding or shadowing the other</span><br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> SomeType.method(); <span class="hljs-comment">// ❌ ambiguous method call</span><br> <span class="hljs-comment">// must disambiguate using fully-qualified syntax</span><br> <SomeType <span class="hljs-keyword">as</span> Supertrait>::method(&st); <span class="hljs-comment">// ✅ prints "in supertrait"</span><br> <SomeType <span class="hljs-keyword">as</span> Subtrait>::method(&st); <span class="hljs-comment">// ✅ prints "in subtrait"</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>需要区分:对泛型进行约束</p><ol><li><p>下面例子中,t的类型T一定实现了Clone Trait,或者说“类型T依赖Clone trait”,称为:泛型依赖于 trait 约束。</p><ol><li>在function中,t.clone() 可以被调用(定义在Clone trait的func都可以被调用)</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">function</span></span><T: <span class="hljs-built_in">Clone</span>>(t: T) {<br> <span class="hljs-comment">// impl</span><br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>子集和超集</p><ol><li><p>Copy Trait 的例子</p><ol><li><p>Copy是Clone的子集,Copy并不依赖Clone</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Copy</span></span>: <span class="hljs-built_in">Clone</span> {}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>可以这么理解:subtrait 细化(refine)了它们的 supertrait。</p><p> “细化(Refinement)”刻意保持一定的模糊性,因为它们在不同的上下文环境中会有不同的含义:</p><ul><li>subtrait 可能会使得 supertrait 的方法实现更为具体,快速,占用更少的内存,例如,<code>Copy:Clone</code>;</li><li>subtrait 可能会对 supertrait 的方法实现增加额外的保证,例如:<code>Eq: PartialEq</code>,<code>Ord: PartialOrd</code>,<code>ExactSizeIterator: Iterator</code>;</li><li>subtrait 可能会使得 supertrait 的方法更为灵活和易于调用,例如:<code>FnMut: FnOnce</code>,<code>Fn: FnMut</code>;</li><li>subtrait 可能会扩展 supertrait 并添加新的方法,例如:<code>DoubleEndedIterator: Iterator</code>,<code>ExactSizeIterator: Iterator</code>。</li></ul></li></ol><blockquote><p>感觉这里的“细化”可以理解为“特例化” zelin 220513</p></blockquote></li></ol><h3 id="12-Trait对象"><a href="#12-Trait对象" class="headerlink" title="12. Trait对象"></a>12. Trait对象</h3><ol><li><p>泛型可以实现编译期多态,Trait对象可以实现运行时多态</p></li><li><p>一个使用trait对象是函数动态返回不同类型的例子</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span>(condition: <span class="hljs-built_in">bool</span>, vec: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">i32</span>>) -> <span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> <span class="hljs-built_in">Iterator</span><Item = <span class="hljs-built_in">i32</span>>> {<br> <span class="hljs-keyword">let</span> iter = vec.into_iter();<br> <span class="hljs-keyword">if</span> condition {<br> <span class="hljs-comment">// Has type:</span><br> <span class="hljs-comment">// Box<Map<IntoIter<i32>, Fn(i32) -> i32>></span><br> <span class="hljs-comment">// But is cast to:</span><br> <span class="hljs-comment">// Box<dyn Iterator<Item = i32>></span><br> <span class="hljs-built_in">Box</span>::new(iter.map(|n| n * <span class="hljs-number">2</span>))<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// Has type:</span><br> <span class="hljs-comment">// Box<Filter<IntoIter<i32>, Fn(&i32) -> bool>></span><br> <span class="hljs-comment">// But is cast to:</span><br> <span class="hljs-comment">// Box<dyn Iterator<Item = i32>></span><br> <span class="hljs-built_in">Box</span>::new(iter.filter(|&n| n >= <span class="hljs-number">2</span>))<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>一个使用trait对象在集合中存储不同类型的例子</p><p> 这些类型都实现了同一个trait</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#![allow(unused)]</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br><span class="hljs-keyword">use</span> std::<span class="hljs-built_in">f64</span>::consts::PI;<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Circle</span></span> {<br> radius: <span class="hljs-built_in">f64</span>,<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Square</span></span> {<br> side: <span class="hljs-built_in">f64</span><br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Shape</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">area</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">f64</span>;<br>}<br><br><span class="hljs-keyword">impl</span> Shape <span class="hljs-keyword">for</span> Circle {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">area</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">f64</span> {<br> PI * <span class="hljs-keyword">self</span>.radius * <span class="hljs-keyword">self</span>.radius<br> }<br>}<br><br><span class="hljs-keyword">impl</span> Shape <span class="hljs-keyword">for</span> Square {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">area</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">f64</span> {<br> <span class="hljs-keyword">self</span>.side * <span class="hljs-keyword">self</span>.side<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_total_area</span></span>(shapes: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> Shape>>) -> <span class="hljs-built_in">f64</span> {<br> shapes.into_iter().map(|s| s.area()).sum()<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span>() {<br> <span class="hljs-keyword">let</span> shapes: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> Shape>> = <span class="hljs-built_in">vec!</span>[<br> <span class="hljs-built_in">Box</span>::new(Circle { radius: <span class="hljs-number">1.0</span> }), <span class="hljs-comment">// Box<Circle> cast to Box<dyn Shape></span><br> <span class="hljs-built_in">Box</span>::new(Square { side: <span class="hljs-number">1.0</span> }), <span class="hljs-comment">// Box<Square> cast to Box<dyn Shape></span><br> ];<br> <span class="hljs-built_in">assert_eq!</span>(PI + <span class="hljs-number">1.0</span>, get_total_area(shapes)); <span class="hljs-comment">// ✅</span><br>}<br>}<br></code></pre></td></tr></table></figure></li><li><p>Trait对象是没有大小的,所以必须用智能指针包裹trait对象</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Struct</span></span>;<br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span> {}<br><br><span class="hljs-comment">// regular struct</span><br>&Struct<br><span class="hljs-built_in">Box</span><Struct><br>Rc<Struct><br>Arc<Struct><br><br><span class="hljs-comment">// trait objects</span><br>&<span class="hljs-keyword">dyn</span> Trait<br><span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> Trait><br>Rc<<span class="hljs-keyword">dyn</span> Trait><br>Arc<<span class="hljs-keyword">dyn</span> Trait><br></code></pre></td></tr></table></figure></li><li><p>不是所有的 trait 都可以被转成 trait 对象。当且仅当一个 trait 满足下面这些要求时,它才是对象安全的(object-safe):</p><ul><li>trait 不要求<code>Self:Sized</code></li><li>trait 的所有方法都是对象安全的</li></ul><p> 当一个 trait 方法满足下面的要求时,该方法是对象安全的:</p><ul><li>方法要求<code>Self:Sized</code> 或者</li><li>方法在其接收者位置仅使用一个<code>Self</code>类型</li></ul></li></ol><h3 id="13-Maker-Traits"><a href="#13-Maker-Traits" class="headerlink" title="13. Maker Traits"></a>13. Maker Traits</h3><p>指的是没有trait item(项)的trait(即没有任何函数/方法)</p><p>仅仅是“标记”功能:类型具有“某种属性”</p><p>Eq就是一个标记Trait,在PartialEq基础上标记了“额外具有反射性”</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// Impling PartialEq for a type promises</span><br><span class="hljs-comment">// that equality for the type has these properties:</span><br><span class="hljs-comment">// - symmetry: a == b implies b == a, and</span><br><span class="hljs-comment">// - transitivity: a == b && b == c implies a == c</span><br><span class="hljs-comment">// But DOES NOT promise this property:</span><br><span class="hljs-comment">// - reflexivity: a == a</span><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">PartialEq</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">eq</span></span>(&<span class="hljs-keyword">self</span>, other: &<span class="hljs-keyword">Self</span>) -> <span class="hljs-built_in">bool</span>;<br>}<br><br><span class="hljs-comment">// Eq has no trait items! The eq method is already</span><br><span class="hljs-comment">// declared by PartialEq, but "impling" Eq</span><br><span class="hljs-comment">// for a type promises this additional equality property:</span><br><span class="hljs-comment">// - reflexivity: a == a</span><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Eq</span></span>: <span class="hljs-built_in">PartialEq</span> {}<br><br><span class="hljs-comment">// f64 impls PartialEq but not Eq because NaN != NaN</span><br><span class="hljs-comment">// i32 impls PartialEq & Eq because there's no NaNs :)</span><br></code></pre></td></tr></table></figure><h3 id="14-Auto-Trait-自动Trait"><a href="#14-Auto-Trait-自动Trait" class="headerlink" title="14. Auto Trait: 自动Trait"></a>14. Auto Trait: 自动Trait</h3><ol><li><p>自动 Trait 是指如果一个类型的所有成员都实现了该 trait,该类型就会自动实现该 trait。</p></li><li><p>“成员(member)”的含义取决于类型,例如:结构体的字段、枚举的变量、数组的元素、元组的项,等等</p></li><li><p>自动 trait 必须是标记 trait,但不是所有标记trait都是自动trait</p></li><li><p>自动trait的例子</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// implemented for types which are safe to send between threads</span><br><span class="hljs-keyword">unsafe</span> auto <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Send</span></span> {}<br><br><span class="hljs-comment">// implemented for types whose references are safe to send between threads</span><br><span class="hljs-keyword">unsafe</span> auto <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Sync</span></span> {}<br></code></pre></td></tr></table></figure></li></ol><h3 id="15-unsafe-Trait"><a href="#15-unsafe-Trait" class="headerlink" title="15. unsafe Trait"></a>15. unsafe Trait</h3><ol><li><p>trait 可以被标记为unsafe,指的是实现trait的时候可以带一些unsafe代码</p></li><li><p>Send Sync这两个Trait就是unsafe Trait。如果手动实现Send Sync这两个Trait的时候,需要加上unsafe</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// SomeType is not Send or Sync</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span> {<br> not_send_or_sync: *<span class="hljs-keyword">const</span> (),<br>}<br><br><span class="hljs-comment">// but if we're confident that our impl doesn't have any data</span><br><span class="hljs-comment">// races we can explicitly mark it as Send and Sync using unsafe</span><br><span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Send</span> <span class="hljs-keyword">for</span> SomeType {}<br><span class="hljs-keyword">unsafe</span> <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Sync</span> <span class="hljs-keyword">for</span> SomeType {}<br></code></pre></td></tr></table></figure></li></ol><h2 id="二、Auto-Trait"><a href="#二、Auto-Trait" class="headerlink" title="二、Auto Trait"></a>二、Auto Trait</h2><h3 id="1-Send-amp-Sync"><a href="#1-Send-amp-Sync" class="headerlink" title="1. Send & Sync"></a>1. Send & Sync</h3><ol><li><p>如果一个类型满足Send,说明该类型可以安全地在线程中发送(或者说移动)</p></li><li><p>如果一个类型满足Sync,说明该类型可以安全地在线程中共享引用</p></li><li><p>In more precise terms some type T is Sync if and only if &T is Send.</p><p> 当且仅当&T类型满足Send Trait,T才满足Sync Trait</p></li><li><p>几乎所有的类型都满足Send、Sync</p><ol><li>Send Trait例外:Rc<ol><li>线程安全的版本:Arc</li></ol></li><li>Sync Trait例外:Rc、Cell、RefCell<ol><li>线程安全的Cell/RefCell : Mutex/RwLock</li></ol></li><li>可以使用Mutex/Rwlock包装原始类型获得原子性,也可以使用标准库提供的:AtomBool之类的原子类型</li></ol></li><li><p>即使没有任何内部同步的类型,由于Rust借用规则的保证,这个类型大概率格式满足Sync的</p><ol><li>一个例子,多个线程只读共享&str 类型的 greeting_ref,<ol><li><p>在不可变引用仍存在时,不允许出现可变引用。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> crossbeam::thread;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> greeting = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello"</span>);<br> <span class="hljs-keyword">let</span> greeting_ref = &greeting;<br> <br> thread::scope(|scoped_thread| {<br> <span class="hljs-comment">// spawn 3 threads</span><br> <span class="hljs-keyword">for</span> n <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>..=<span class="hljs-number">3</span> {<br> <span class="hljs-comment">// greeting_ref copied into every thread</span><br> scoped_thread.spawn(<span class="hljs-keyword">move</span> |_| {<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{} {}"</span>, greeting_ref, n); <span class="hljs-comment">// prints "Hello {n}"</span><br> });<br> }<br> <br> <span class="hljs-comment">// line below could cause UB or data races but compiler rejects it</span><br> greeting += <span class="hljs-string">" world"</span>; <span class="hljs-comment">// ❌ cannot mutate greeting while immutable refs exist</span><br> });<br> <br> <span class="hljs-comment">// can mutate greeting after every thread has joined</span><br> greeting += <span class="hljs-string">" world"</span>; <span class="hljs-comment">// ✅</span><br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, greeting); <span class="hljs-comment">// prints "Hello world"</span><br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><p> b. 也可以一个线程可写</p><p> 在一个可变引用存在的时候,不允许出现第二个可变引用</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> crossbeam::thread;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> greeting = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"Hello"</span>);<br> <span class="hljs-keyword">let</span> greeting_ref = &<span class="hljs-keyword">mut</span> greeting;<br> <br> thread::scope(|scoped_thread| {<br> <span class="hljs-comment">// greeting_ref moved into thread</span><br> scoped_thread.spawn(<span class="hljs-keyword">move</span> |_| {<br> *greeting_ref += <span class="hljs-string">" world"</span>;<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, greeting_ref); <span class="hljs-comment">// prints "Hello world"</span><br> });<br> <br> <span class="hljs-comment">// line below could cause UB or data races but compiler rejects it</span><br> greeting += <span class="hljs-string">"!!!"</span>; <span class="hljs-comment">// ❌ cannot mutate greeting while mutable refs exist</span><br> });<br> <br> <span class="hljs-comment">// can mutate greeting after the thread has joined</span><br> greeting += <span class="hljs-string">"!!!"</span>; <span class="hljs-comment">// ✅</span><br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, greeting); <span class="hljs-comment">// prints "Hello world!!!"</span><br>}<br></code></pre></td></tr></table></figure><h3 id="2-Sized"><a href="#2-Sized" class="headerlink" title="2. Sized"></a>2. Sized</h3><ol><li><p>如果一个类型是<code>Sized</code>,这意味着它的类型大小在编译期是可知的,并且可以在栈上创建一个该类型的实例。</p></li><li><p>所有的泛型隐含了Sized Trait约束</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span><T>(t: &T) {}<br><br><span class="hljs-comment">// example above desugared</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span><T: <span class="hljs-built_in">Sized</span>>(t: &T) {}<br></code></pre></td></tr></table></figure></li><li><p>因为所有的泛型类型上都有一个隐含的Sized约束,如果我们想要选择松弛这个约束,我们需要使用特定的“宽松约束(relaxed bound)”语法——?Sized,该语法目前只为Sized trait 存在。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// now T can be unsized</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">func</span></span><T: ?<span class="hljs-built_in">Sized</span>>(t: &T) {}<br></code></pre></td></tr></table></figure></li><li><p>所有的Trait隐含了<code>?size</code> 的约束</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span> {}<br><br><span class="hljs-comment">// example above desugared</span><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Trait</span></span>: ?<span class="hljs-built_in">Sized</span> {}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h2 id="三、General-Traits"><a href="#三、General-Traits" class="headerlink" title="三、General Traits"></a>三、General Traits</h2><h3 id="1-Default"><a href="#1-Default" class="headerlink" title="1. Default"></a>1. Default</h3><ol><li><p>可以为实现Default的类型构造默认值</p><ol><li>注意default函数没有self参数,因为其功能就是创造一个类型实例</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Color</span></span> {<br> r: <span class="hljs-built_in">u8</span>,<br> g: <span class="hljs-built_in">u8</span>,<br> b: <span class="hljs-built_in">u8</span>,<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">Default</span> <span class="hljs-keyword">for</span> Color {<br> <span class="hljs-comment">// default color is black</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">default</span></span>() -> <span class="hljs-keyword">Self</span> {<br> Color {<br> r: <span class="hljs-number">0</span>,<br> g: <span class="hljs-number">0</span>,<br> b: <span class="hljs-number">0</span>,<br> }<br> }<br>}<br><span class="hljs-comment">// 默认值构造</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-comment">// just give me some color!</span><br> <span class="hljs-keyword">let</span> color = Color::default();<br>}<br></code></pre></td></tr></table></figure></li><li><p>default函数也可以用在泛型类型中</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">guarantee_length</span></span><T: <span class="hljs-built_in">Default</span>>(<span class="hljs-keyword">mut</span> vec: <span class="hljs-built_in">Vec</span><T>, min_len: <span class="hljs-built_in">usize</span>) -> <span class="hljs-built_in">Vec</span><T> {<br> <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..min_len.saturating_sub(vec.len()) {<br> vec.push(T::default());<br> }<br> vec<br>}<br></code></pre></td></tr></table></figure></li><li><p>也可以用派生宏的方式实现Default trait</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// default color is still black</span><br><span class="hljs-comment">// because u8::default() == 0</span><br><span class="hljs-meta">#[derive(Default)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Color</span></span> {<br> r: <span class="hljs-built_in">u8</span>,<br> g: <span class="hljs-built_in">u8</span>,<br> b: <span class="hljs-built_in">u8</span><br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="2-Clone"><a href="#2-Clone" class="headerlink" title="2. Clone"></a>2. Clone</h3><p>预备知识</p><ol><li><p>Self指代实现的类型</p></li><li><p>Methods指对类型实例操作的函数</p></li><li><p><strong><strong>Default Impls 指Trait定义中可以包含函数/方法的默认实现</strong></strong></p></li><li><p><strong><strong>Derive Macros 派生宏 指的是类型定义的时候可以通过派生宏的方式实现一个Trait</strong></strong></p></li><li><p>Clone trait中包含两个函数</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Clone</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">clone</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-keyword">Self</span>;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">clone_from</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, source: &<span class="hljs-keyword">Self</span>);<br>}<br></code></pre></td></tr></table></figure></li><li><p>Clone Trait的作用是,从一个不可变引用,转换为所拥有的值。即&T->T。</p><ol><li>Clone不保证这种转换的效率,所以它会很慢并且成本较高。</li></ol></li><li><p>可以通过派生宏快速实现Clone Trait</p><ol><li>下面的例子中两种实现是等价的</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(Clone)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span> {<br> cloneable_member1: CloneableType1,<br> cloneable_member2: CloneableType2,<br> <span class="hljs-comment">// etc</span><br>}<br><br><span class="hljs-comment">// macro generates impl below</span><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">Clone</span> <span class="hljs-keyword">for</span> SomeType {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">clone</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-keyword">Self</span> {<br> SomeType {<br> cloneable_member1: <span class="hljs-keyword">self</span>.cloneable_member1.clone(),<br> cloneable_member2: <span class="hljs-keyword">self</span>.cloneable_member2.clone(),<br> <span class="hljs-comment">// etc</span><br> }<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="3-Copy"><a href="#3-Copy" class="headerlink" title="3. Copy"></a>3. Copy</h3><p>预备知识</p><ol><li><p>标记Trait</p></li><li><p>子集和超集</p></li><li><p>派生宏</p></li><li><p>Copy Trait</p><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Copy</span></span>: <span class="hljs-built_in">Clone</span> {}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 按位拷贝,即T -> T</p></li><li><p>实现</p><p> 只能通过派生宏实现,下面的两个实现是等价的</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(Copy, Clone)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SomeType</span></span>;<br></code></pre></td></tr></table></figure></li></ol></li><li><p>Copy是Clone的子集:如果一个类型实现了Copy,那么这个类型自然满足Clone</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// this is what the derive macro generates</span><br><span class="hljs-keyword">impl</span><T: <span class="hljs-built_in">Copy</span>> <span class="hljs-built_in">Clone</span> <span class="hljs-keyword">for</span> T {<br> <span class="hljs-comment">// the clone method becomes just a copy</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">clone</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-keyword">Self</span> {<br> *<span class="hljs-keyword">self</span><br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>一个类型如果实现了Copy,它在被移动(move)时的行为就发生了改变。默认情况下,所有的类型都有移动(move)语义 ,但是一旦某个类型实现了Copy,它就有了拷贝(copy)语义 。</p><ol><li><p>一个例子,第一种情况下发生“移动”:src在移动后不可用。第二种情况发生“拷贝”:src在拷贝后仍有效</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// a "move", src: !Copy</span><br><span class="hljs-keyword">let</span> dest = src;<br><br><span class="hljs-comment">// a "copy", src: Copy</span><br><span class="hljs-keyword">let</span> dest = src;<br></code></pre></td></tr></table></figure></li><li><p>Vec<> 类型只能移动,不能拷贝</p><ol><li><p>Vec<i32>类型如下</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust">{ data: *<span class="hljs-keyword">mut</span> [<span class="hljs-built_in">i32</span>], length: <span class="hljs-built_in">usize</span>, capacity: <span class="hljs-built_in">usize</span> }<br></code></pre></td></tr></table></figure></li><li><p>如果允许Copy的话</p><p> src和dest就都有了数据的可变引用,这不满足Rust借用器检查</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust">src = { data: *<span class="hljs-keyword">mut</span> [<span class="hljs-built_in">i32</span>], length: <span class="hljs-built_in">usize</span>, capacity: <span class="hljs-built_in">usize</span> }<br>dest = { data: *<span class="hljs-keyword">mut</span> [<span class="hljs-built_in">i32</span>], length: <span class="hljs-built_in">usize</span>, capacity: <span class="hljs-built_in">usize</span> }<br></code></pre></td></tr></table></figure></li></ol></li><li><p>Option<>类型也可以实现拷贝</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust">{ is_valid: <span class="hljs-built_in">bool</span>, data: <span class="hljs-built_in">i32</span> }<br></code></pre></td></tr></table></figure><p> 拷贝后</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust">src = { is_valid: <span class="hljs-built_in">bool</span>, data: <span class="hljs-built_in">i32</span> }<br>dest = { is_valid: <span class="hljs-built_in">bool</span>, data: <span class="hljs-built_in">i32</span> }<br></code></pre></td></tr></table></figure></li></ol></li><li><p>需要注意:Copy trait需要程序员显式地实现。</p></li></ol><h3 id="4-Any"><a href="#4-Any" class="headerlink" title="4. Any"></a>4. Any</h3><ol><li><p>预备知识</p><ol><li><strong>泛型覆盖实现,Trait定义的函数中,参数重满足一些Trait的类型,提供默认实现</strong></li></ol></li><li><p>Any Trait作用:实现“多态”的需求</p><ol><li>一个例子,从dyn Any类型中取出类型T需要使用<code>downcast_ref::<T>()</code> 和 <code>downcast_mut::<T>()</code> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::any::Any;<br><br><span class="hljs-meta">#[derive(Default)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">inc</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) {<br> <span class="hljs-keyword">self</span>.x += <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">self</span>.y += <span class="hljs-number">1</span>;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">map_any</span></span>(<span class="hljs-keyword">mut</span> any: <span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> Any>) -> <span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> Any> {<br> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(num) = any.downcast_mut::<<span class="hljs-built_in">i32</span>>() {<br> *num += <span class="hljs-number">1</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(string) = any.downcast_mut::<<span class="hljs-built_in">String</span>>() {<br> *string += <span class="hljs-string">"!"</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(point) = any.downcast_mut::<Point>() {<br> point.inc();<br> }<br> any<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> vec: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> Any>> = <span class="hljs-built_in">vec!</span>[<br> <span class="hljs-built_in">Box</span>::new(<span class="hljs-number">0</span>),<br> <span class="hljs-built_in">Box</span>::new(<span class="hljs-built_in">String</span>::from(<span class="hljs-string">"a"</span>)),<br> <span class="hljs-built_in">Box</span>::new(Point::default()),<br> ];<br> <span class="hljs-comment">// vec = [0, "a", Point { x: 0, y: 0 }]</span><br> vec = vec.into_iter().map(map_any).collect();<br> <span class="hljs-comment">// vec = [1, "a!", Point { x: 1, y: 1 }]</span><br>}<br></code></pre></td></tr></table></figure> 但也可以通过参数化多态来实现多态 在大多数情况下,参数化多态要优于临时多态性,后者也可以用枚举(enum)来模拟,枚举具有更好的类型安全,需要的间接(抽象)也更少。 <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(Default)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">inc</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) {<br> <span class="hljs-keyword">self</span>.x += <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">self</span>.y += <span class="hljs-number">1</span>;<br> }<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Stuff</span></span> {<br> Integer(<span class="hljs-built_in">i32</span>),<br> <span class="hljs-built_in">String</span>(<span class="hljs-built_in">String</span>),<br> Point(Point),<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">map_stuff</span></span>(<span class="hljs-keyword">mut</span> stuff: Stuff) -> Stuff {<br> <span class="hljs-keyword">match</span> &<span class="hljs-keyword">mut</span> stuff {<br> Stuff::Integer(num) => *num += <span class="hljs-number">1</span>,<br> Stuff::<span class="hljs-built_in">String</span>(string) => *string += <span class="hljs-string">"!"</span>,<br> Stuff::Point(point) => point.inc(),<br> }<br> stuff<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> vec = <span class="hljs-built_in">vec!</span>[<br> Stuff::Integer(<span class="hljs-number">0</span>),<br> Stuff::<span class="hljs-built_in">String</span>(<span class="hljs-built_in">String</span>::from(<span class="hljs-string">"a"</span>)),<br> Stuff::Point(Point::default()),<br> ];<br> <span class="hljs-comment">// vec = [0, "a", Point { x: 0, y: 0 }]</span><br> vec = vec.into_iter().map(map_stuff).collect();<br> <span class="hljs-comment">// vec = [1, "a!", Point { x: 1, y: 1 }]</span><br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>Any Trait的实现</p><p> 不需要手动实现,均被“<strong>泛型覆盖实现</strong>”所覆盖</p><p> Any trait中仅包含一个type_id函数,标准库的默认实现已经覆盖了所有类型</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// https://doc.rust-lang.org/std/any/trait.Any.html</span><br><span class="hljs-meta">#[stable(feature = <span class="hljs-meta-string">"rust1"</span>, since = <span class="hljs-meta-string">"1.0.0"</span>)]</span><br><span class="hljs-meta">#[cfg_attr(not(test), rustc_diagnostic_item = <span class="hljs-meta-string">"Any"</span>)]</span><br><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Any</span></span>: <span class="hljs-symbol">'static</span> {<br> <span class="hljs-comment">/// Gets the `TypeId` of `self`.</span><br> <span class="hljs-comment">///</span><br> <span class="hljs-comment">/// # Examples</span><br> <span class="hljs-comment">///</span><br> <span class="hljs-comment">/// ```</span><br> <span class="hljs-comment">/// use std::any::{Any, TypeId};</span><br> <span class="hljs-comment">///</span><br> <span class="hljs-comment">/// fn is_string(s: &dyn Any) -> bool {</span><br> <span class="hljs-comment">/// TypeId::of::<String>() == s.type_id()</span><br> <span class="hljs-comment">/// }</span><br> <span class="hljs-comment">///</span><br> <span class="hljs-comment">/// assert_eq!(is_string(&0), false);</span><br> <span class="hljs-comment">/// assert_eq!(is_string(&"cookie monster".to_string()), true);</span><br> <span class="hljs-comment">/// ```</span><br> <span class="hljs-meta">#[stable(feature = <span class="hljs-meta-string">"get_type_id"</span>, since = <span class="hljs-meta-string">"1.34.0"</span>)]</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">type_id</span></span>(&<span class="hljs-keyword">self</span>) -> TypeId;<br>}<br><br><span class="hljs-meta">#[stable(feature = <span class="hljs-meta-string">"rust1"</span>, since = <span class="hljs-meta-string">"1.0.0"</span>)]</span><br><span class="hljs-keyword">impl</span><T: <span class="hljs-symbol">'static</span> + ?<span class="hljs-built_in">Sized</span>> Any <span class="hljs-keyword">for</span> T {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">type_id</span></span>(&<span class="hljs-keyword">self</span>) -> TypeId {<br> TypeId::of::<T>()<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol><h2 id="四、Formatting-Trait"><a href="#四、Formatting-Trait" class="headerlink" title="四、Formatting Trait"></a>四、Formatting Trait</h2><table><thead><tr><th>Trait</th><th>Placeholder</th><th>Description</th></tr></thead><tbody><tr><td>Display</td><td>{}</td><td>display representation</td></tr><tr><td>Debug</td><td>{:?}</td><td>debug representation</td></tr><tr><td>Octal</td><td>{:o}</td><td>octal representation</td></tr><tr><td>LowerHex</td><td>{:x}</td><td>lowercase hex representation</td></tr><tr><td>UpperHex</td><td>{:X}</td><td>uppercase hex representation</td></tr><tr><td>Pointer</td><td>{:p}</td><td>memory address</td></tr><tr><td>Binary</td><td>{:b}</td><td>binary representation</td></tr><tr><td>LowerExp</td><td>{:e}</td><td>lowercase exponential representation</td></tr><tr><td>UpperExp</td><td>{:E}</td><td>uppercase exponential representation</td></tr></tbody></table><h3 id="1-Display-Trait-amp-ToString-Trait"><a href="#1-Display-Trait-amp-ToString-Trait" class="headerlink" title="1. Display Trait & ToString Trait"></a>1. Display Trait & ToString Trait</h3><ol><li><p>Display trait</p><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Display</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> Formatter<<span class="hljs-symbol">'_</span>>) -> <span class="hljs-built_in">Result</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用:用户定义的类型想格式化打印的时候可以实现Display trait</p><p> 比如给Point 类型实现Display trait,实现后类型的实例就可以在格式化输出中输出为字符串</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::fmt;<br><br><span class="hljs-meta">#[derive(Default)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> fmt::Display <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> fmt::Formatter<<span class="hljs-symbol">'_</span>>) -> fmt::<span class="hljs-built_in">Result</span> {<br> <span class="hljs-built_in">write!</span>(f, <span class="hljs-string">"({}, {})"</span>, <span class="hljs-keyword">self</span>.x, <span class="hljs-keyword">self</span>.y)<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-built_in">println!</span>(<span class="hljs-string">"origin: {}"</span>, Point::default());<br> <span class="hljs-comment">// prints "origin: (0, 0)"</span><br><br> <span class="hljs-comment">// get Point's Display representation as a String</span><br> <span class="hljs-keyword">let</span> stringified_point = <span class="hljs-built_in">format!</span>(<span class="hljs-string">"{}"</span>, Point::default());<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-string">"(0, 0)"</span>, stringified_point); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>ToString </p><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">ToString</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">to_string</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">String</span>;<br>}<br><br><span class="hljs-keyword">impl</span><T: Display + ?<span class="hljs-built_in">Sized</span>> <span class="hljs-built_in">ToString</span> <span class="hljs-keyword">for</span> T;<br></code></pre></td></tr></table></figure></li><li><p>所有实现Display Trait的类型都自动实现Tostring Trait(Again,泛型覆盖实现)</p></li><li><p>可以调用to_string 方法将对象转换为格式化打印</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">display_point</span></span>() {<br> <span class="hljs-keyword">let</span> origin = Point::default();<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-built_in">format!</span>(<span class="hljs-string">"{}"</span>, origin), <span class="hljs-string">"(0, 0)"</span>);<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">point_to_string</span></span>() {<br> <span class="hljs-keyword">let</span> origin = Point::default();<br> <span class="hljs-built_in">assert_eq!</span>(origin.to_string(), <span class="hljs-string">"(0, 0)"</span>);<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">display_equals_to_string</span></span>() {<br> <span class="hljs-keyword">let</span> origin = Point::default();<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-built_in">format!</span>(<span class="hljs-string">"{}"</span>, origin), origin.to_string());<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h3 id="2-Debug"><a href="#2-Debug" class="headerlink" title="2. Debug"></a>2. Debug</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Debug</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> Formatter<<span class="hljs-symbol">'_</span>>) -> <span class="hljs-built_in">Result</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>实现Debug Trait:可以通过派生宏,也可以自己定义输出</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs rust"><br><span class="hljs-meta">#[derive(Debug)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-comment">// derive macro generates impl below</span><br><span class="hljs-keyword">impl</span> fmt::<span class="hljs-built_in">Debug</span> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> fmt::Formatter<<span class="hljs-symbol">'_</span>>) -> fmt::<span class="hljs-built_in">Result</span> {<br> f.debug_struct(<span class="hljs-string">"Point"</span>)<br> .field(<span class="hljs-string">"x"</span>, &<span class="hljs-keyword">self</span>.x)<br> .field(<span class="hljs-string">"y"</span>, &<span class="hljs-keyword">self</span>.y)<br> .finish()<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>dbg! 输出和 print! 输出的区别</p><ul><li>dbg!打印到 stderr 而不是 stdout,因此在我们的程序中,能够很容易地和标准输出的输出结果区分。</li><li>dbg!会连同传入的表达式和表达式的计算结果一起打印出来。</li><li>dbg!会获取传入参数的所有权并将其返回,因此你可以在表达式中使用它:</li></ul></li></ol><h2 id="五、Opertor-Traits"><a href="#五、Opertor-Traits" class="headerlink" title="五、Opertor Traits"></a>五、Opertor Traits</h2><p>Rust中所有操作符均有一个Trait与之对应</p><table><thead><tr><th>Trait(s)</th><th>Category</th><th>Operator(s)</th><th>Description</th></tr></thead><tbody><tr><td>Eq, PartialEq</td><td>comparison</td><td>==</td><td>equality</td></tr><tr><td>Ord, PartialOrd</td><td>comparison</td><td><, >, <=, >=</td><td>comparison</td></tr><tr><td>Add</td><td>arithmetic</td><td>+</td><td>addition</td></tr><tr><td>AddAssign</td><td>arithmetic</td><td>+=</td><td>addition assignment</td></tr><tr><td>BitAnd</td><td>arithmetic</td><td>&</td><td>bitwise AND</td></tr><tr><td>BitAndAssign</td><td>arithmetic</td><td>&=</td><td>bitwise assignment</td></tr><tr><td>BitXor</td><td>arithmetic</td><td>^</td><td>bitwise XOR</td></tr><tr><td>BitXorAssign</td><td>arithmetic</td><td>^=</td><td>bitwise XOR assignment</td></tr><tr><td>Div</td><td>arithmetic</td><td>/</td><td>division</td></tr><tr><td>DivAssign</td><td>arithmetic</td><td>/=</td><td>division assignment</td></tr><tr><td>Mul</td><td>arithmetic</td><td>*</td><td>multiplication</td></tr><tr><td>MulAssign</td><td>arithmetic</td><td>*=</td><td>multiplication assignment</td></tr><tr><td>Neg</td><td>arithmetic</td><td>-</td><td>unary negation</td></tr><tr><td>Not</td><td>arithmetic</td><td>!</td><td>unary logical negation</td></tr><tr><td>Rem</td><td>arithmetic</td><td>%</td><td>remainder</td></tr><tr><td>RemAssign</td><td>arithmetic</td><td>%=</td><td>remainder assignment</td></tr><tr><td>Shl</td><td>arithmetic</td><td><<</td><td>left shift</td></tr><tr><td>ShlAssign</td><td>arithmetic</td><td><<=</td><td>left shift assignment</td></tr><tr><td>Shr</td><td>arithmetic</td><td>>></td><td>right shift</td></tr><tr><td>ShrAssign</td><td>arithmetic</td><td>>>=</td><td>right shift assignment</td></tr><tr><td>Sub</td><td>arithmetic</td><td>-</td><td>subtraction</td></tr><tr><td>SubAssign</td><td>arithmetic</td><td>-=</td><td>subtraction assignment</td></tr><tr><td>Fn</td><td>closure</td><td>(…args)</td><td>immutable closure invocation</td></tr><tr><td>FnMut</td><td>closure</td><td>(…args)</td><td>mutable closure invocation</td></tr><tr><td>FnOnce</td><td>closure</td><td>(…args)</td><td>one-time closure invocation</td></tr><tr><td>Deref</td><td>other</td><td>*</td><td>immutable dereference</td></tr><tr><td>DerefMut</td><td>other</td><td>*</td><td>mutable derenence</td></tr><tr><td>Drop</td><td>other</td><td>-</td><td>type destructor</td></tr><tr><td>Index</td><td>other</td><td>[]</td><td>immutable index</td></tr><tr><td>IndexMut</td><td>other</td><td>[]</td><td>mutable index</td></tr><tr><td>RangeBounds</td><td>other</td><td>..</td><td>range</td></tr></tbody></table><p>下面逐个介绍</p><h3 id="1-Comparison-Traits"><a href="#1-Comparison-Traits" class="headerlink" title="1. Comparison Traits"></a>1. <strong><strong>Comparison Traits</strong></strong></h3><table><thead><tr><th>Trait(s)</th><th>Category</th><th>Operator(s)</th><th>Description</th></tr></thead><tbody><tr><td>Eq, PartialEq</td><td>comparison</td><td>==</td><td>equality</td></tr><tr><td>Ord, PartialOrd</td><td>comparison</td><td><, >, <=, >=</td><td>comparison</td></tr></tbody></table><ol><li><p>PartialEq Trait</p><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">PartialEq</span></span><Rhs = <span class="hljs-keyword">Self</span>> <br><span class="hljs-keyword">where</span><br> Rhs: ?<span class="hljs-built_in">Sized</span>, <br>{<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">eq</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">bool</span>;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ne</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">bool</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 满足PartialEq<Rhs>类型的实例可以通过==操作符检查是否和Rhs类型的实例相等。</p></li><li><p>特点</p><ol><li>PartialEq表示的是集合论中的:对称性、传递性<ul><li>a == b也意味着b == a(对称性)</li><li>a == b && b == c 意味着 a == c (传递性)</li></ul></li></ol></li><li><p>实现</p><ol><li>如果一个类型中所有成员均实现了PartialEq,那么这个类型的PartialEq Trait可以被派生宏实现</li></ol></li><li><p>引用类型的实现</p><ol><li><p>again,由于”泛型覆盖实现”的特性,一旦一个类型满足了PartialEq trait,那么对应的引用类型的比较也自然被实现了,换句话说,该类型的引用类型也可以被比较了。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// this impl only gives us: Point == Point</span><br><span class="hljs-meta">#[derive(PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span><br>}<br><br><span class="hljs-comment">// all of the generic blanket impls below</span><br><span class="hljs-comment">// are provided by the standard library</span><br><br><span class="hljs-comment">// this impl gives us: &Point == &Point</span><br><span class="hljs-keyword">impl</span><A, B> <span class="hljs-built_in">PartialEq</span><&<span class="hljs-symbol">'_</span> B> <span class="hljs-keyword">for</span> &<span class="hljs-symbol">'_</span> A<br><span class="hljs-keyword">where</span> A: <span class="hljs-built_in">PartialEq</span><B> + ?<span class="hljs-built_in">Sized</span>, B: ?<span class="hljs-built_in">Sized</span>;<br><br><span class="hljs-comment">// this impl gives us: &mut Point == &Point</span><br><span class="hljs-keyword">impl</span><A, B> <span class="hljs-built_in">PartialEq</span><&<span class="hljs-symbol">'_</span> B> <span class="hljs-keyword">for</span> &<span class="hljs-symbol">'_</span> <span class="hljs-keyword">mut</span> A<br><span class="hljs-keyword">where</span> A: <span class="hljs-built_in">PartialEq</span><B> + ?<span class="hljs-built_in">Sized</span>, B: ?<span class="hljs-built_in">Sized</span>;<br><br><span class="hljs-comment">// this impl gives us: &Point == &mut Point</span><br><span class="hljs-keyword">impl</span><A, B> <span class="hljs-built_in">PartialEq</span><&<span class="hljs-symbol">'_</span> <span class="hljs-keyword">mut</span> B> <span class="hljs-keyword">for</span> &<span class="hljs-symbol">'_</span> A<br><span class="hljs-keyword">where</span> A: <span class="hljs-built_in">PartialEq</span><B> + ?<span class="hljs-built_in">Sized</span>, B: ?<span class="hljs-built_in">Sized</span>;<br><br><span class="hljs-comment">// this impl gives us: &mut Point == &mut Point</span><br><span class="hljs-keyword">impl</span><A, B> <span class="hljs-built_in">PartialEq</span><&<span class="hljs-symbol">'_</span> <span class="hljs-keyword">mut</span> B> <span class="hljs-keyword">for</span> &<span class="hljs-symbol">'_</span> <span class="hljs-keyword">mut</span> A<br><span class="hljs-keyword">where</span> A: <span class="hljs-built_in">PartialEq</span><B> + ?<span class="hljs-built_in">Sized</span>, B: ?<span class="hljs-built_in">Sized</span>;<br></code></pre></td></tr></table></figure></li></ol><p> ii. 因为这个 trait 是泛型的,所以我们可以在不同的类型之间定义相等性(比较)。标准库利用这一点实现了类字符串类型之间的相互比较,比如String、&str、PathBuf、&Path、OsString、&OsStr等等。</p></li></ol></li><li><p>Eq Trait</p><ol><li><p>定义</p><p> 是一个标记Trait,是PartialEq的子集</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Eq</span></span>: <span class="hljs-built_in">PartialEq</span><<span class="hljs-keyword">Self</span>> {}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 在PartialEq基础上要求“自反性”。也就是对于任意的a,都有a == a。</p><p> 从这种意义上来说,Eq对PartialEq进行了细化,因为它表示了一个更为严格的相等性。</p></li><li><p>实现</p><p> 如果一个类型的所有成员都实现了Eq,那么Eq的实现可以派生到这个类型。</p><p> 浮点型实现了PartialEq但是没有实现Eq,因为NaN != NaN。几乎所有其他的实现了PartialEq的类型都实现了Eq,除非它们包含浮点类型。</p></li><li><p>备注:一旦一个类型实现了PartialEq和Debug,我们可以就可以在assert_eq!宏中使用它。</p><ol><li><p>还可以比较实现了PartialEq类型的集合。下面是一个例子</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(PartialEq, Debug)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example_assert</span></span>(p1: Point, p2: Point) {<br> <span class="hljs-built_in">assert_eq!</span>(p1, p2);<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example_compare_collections</span></span><T: <span class="hljs-built_in">PartialEq</span>>(vec1: <span class="hljs-built_in">Vec</span><T>, vec2: <span class="hljs-built_in">Vec</span><T>) {<br> <span class="hljs-comment">// if T: PartialEq this now works!</span><br> <span class="hljs-keyword">if</span> vec1 == vec2 {<br> <span class="hljs-comment">// some code</span><br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// other code</span><br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol></li></ol><h3 id="2-Hash-Trait"><a href="#2-Hash-Trait" class="headerlink" title="2. Hash Trait"></a>2. Hash Trait</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Hash</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hash</span></span><H: Hasher>(&<span class="hljs-keyword">self</span>, state: &<span class="hljs-keyword">mut</span> H);<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hash_slice</span></span><H: Hasher>(data: &[<span class="hljs-keyword">Self</span>], state: &<span class="hljs-keyword">mut</span> H);<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 满足Hash Trait的类型可以被满足Hasher trait的的对象计算哈希值</p><p> 也就可以放在HashMap HashSet 的Key中</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::hash::Hasher;<br><span class="hljs-keyword">use</span> std::hash::Hash;<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Hash <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hash</span></span><H: Hasher>(&<span class="hljs-keyword">self</span>, hasher: &<span class="hljs-keyword">mut</span> H) {<br> hasher.write_i32(<span class="hljs-keyword">self</span>.x);<br> hasher.write_i32(<span class="hljs-keyword">self</span>.y);<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>实现</p><p> 可以通过派生宏实现</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(Hash)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br></code></pre></td></tr></table></figure></li><li><p>特点</p><ol><li>如果a == b那么a.hash() == b.hash()</li><li>如果一个类型的Eq Trait是派生宏实现的,Hash Trait也需要是被派生宏实现;或者二者可以均被手动实现。<ol><li>但不要混合,因为可能会破坏“如果a == b那么a.hash() == b.hash()” 的保证</li></ol></li></ol></li></ol><h3 id="3-PartialOrd"><a href="#3-PartialOrd" class="headerlink" title="3. PartialOrd"></a>3. <strong><strong>PartialOrd</strong></strong></h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Ordering</span></span> {<br> Less,<br> Equal,<br> Greater,<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">PartialOrd</span></span><Rhs = <span class="hljs-keyword">Self</span>>: <span class="hljs-built_in">PartialEq</span><Rhs> <br><span class="hljs-keyword">where</span><br> Rhs: ?<span class="hljs-built_in">Sized</span>, <br>{<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">partial_cmp</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">Option</span><Ordering>;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">lt</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">le</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">gt</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ge</span></span>(&<span class="hljs-keyword">self</span>, other: &Rhs) -> <span class="hljs-built_in">bool</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 满足PartialOrd<Rhs> Trait的类型可以通过 </ ≤/ >/ ≥ 比较运算符和Rhs类型的实例比较大小</p></li><li><p>实现</p><ol><li><p>如果一个类型的所有成员都实现了PartialOrd,那么它就可以被派生实现</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-meta">#[derive(PartialEq, PartialOrd)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-meta">#[derive(PartialEq, PartialOrd)]</span><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Stoplight</span></span> {<br> Red,<br> Yellow,<br> Green,<br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>特点</p><ol><li><p>PartialOrd表示的是集合论的偏序</p><p><a href="https://zh.wikipedia.org/zh-cn/%E5%81%8F%E5%BA%8F%E5%85%B3%E7%B3%BB">https://zh.wikipedia.org/zh-cn/偏序关系</a></p><p>集合中上的一个关系,这个关系对于集合中的部分元素具有反对称性+传递性,那么这个关系在这个集合上就是偏序(PartialOrder,局部序)的</p><ol><li>a < b implies !(a > b) (asymmetry) 反对称性</li><li>a < b && b < c implies a < c (transitivity) 传递性</li></ol></li><li><p>PartialOrder Trait是PartialEq Trait的子集</p><ol><li><p>二者的实现必须等价</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">must_always_agree</span></span><T: <span class="hljs-built_in">PartialOrd</span> + <span class="hljs-built_in">PartialEq</span>>(t1: T, t2: T) {<br> <span class="hljs-built_in">assert_eq!</span>(t1.partial_cmp(&t2) == <span class="hljs-literal">Some</span>(Ordering::Equal), t1 == t2);<br>}<br></code></pre></td></tr></table></figure></li><li><p>PartialOrder Trait 是在PartialEq Trait上的“特例化/细化”</p><p> 满足PartialEq的类型只能比较相等/不等;满足PartialOrder的类型也可以比较相等/不相等,但是不等中还可以得出大于/小于的结论</p></li></ol></li><li><p>派生宏实现的PartialOrder Trait,基于类型中成员变量的先后顺序比较的</p><p> 下面是个例子,前者先比较x,后者先比较y</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// generates PartialOrd impl which orders</span><br><span class="hljs-comment">// Points based on x member first and</span><br><span class="hljs-comment">// y member second because that's the order</span><br><span class="hljs-comment">// they appear in the source code</span><br><span class="hljs-meta">#[derive(PartialOrd, PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-comment">// generates DIFFERENT PartialOrd impl</span><br><span class="hljs-comment">// which orders Points based on y member</span><br><span class="hljs-comment">// first and x member second</span><br><span class="hljs-meta">#[derive(PartialOrd, PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> y: <span class="hljs-built_in">i32</span>,<br> x: <span class="hljs-built_in">i32</span>,<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h3 id="4-Ord"><a href="#4-Ord" class="headerlink" title="4. Ord"></a>4. Ord</h3><ol><li><p>定义</p><p> Ord Trait是Eq Trait和PartialOrder Trait的子集</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Ord</span></span>: <span class="hljs-built_in">Eq</span> + <span class="hljs-built_in">PartialOrd</span><<span class="hljs-keyword">Self</span>> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cmp</span></span>(&<span class="hljs-keyword">self</span>, other: &<span class="hljs-keyword">Self</span>) -> Ordering;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">max</span></span>(<span class="hljs-keyword">self</span>, other: <span class="hljs-keyword">Self</span>) -> <span class="hljs-keyword">Self</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">min</span></span>(<span class="hljs-keyword">self</span>, other: <span class="hljs-keyword">Self</span>) -> <span class="hljs-keyword">Self</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">clamp</span></span>(<span class="hljs-keyword">self</span>, min: <span class="hljs-keyword">Self</span>, max: <span class="hljs-keyword">Self</span>) -> <span class="hljs-keyword">Self</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><ol><li>比较大小、在两个实例中找最大、最小</li><li>一旦一个类型实现了Ord,我们就可以把它存储在BTreeMap和BTreeSet,还可以在 slice 上使用 sort()方法对其进行排序,这同样适用于其他可以解引用为 slice 的类型,比如数组、Vec和VecDeque。</li></ol></li><li><p>实现</p><ol><li><p>通过派生宏/手动实现</p><p> 这里注意,Ord Trait是PartialOrderTrait的子集,实现了Ord Trait后,PartialOrder的实现可以使用Ord Trait中的实现。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::cmp::Ordering;<br><br><span class="hljs-comment">// of course we can use the derive macros here</span><br><span class="hljs-meta">#[derive(Ord, PartialOrd, Eq, PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-comment">// note: as with PartialOrd, the Ord derive macro</span><br><span class="hljs-comment">// orders a type based on the lexicographical order</span><br><span class="hljs-comment">// of its members</span><br><br><span class="hljs-comment">// but here's the impls if we wrote them out by hand</span><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">Ord</span> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cmp</span></span>(&<span class="hljs-keyword">self</span>, other: &<span class="hljs-keyword">Self</span>) -> Ordering {<br> <span class="hljs-keyword">match</span> <span class="hljs-keyword">self</span>.x.cmp(&other.x) {<br> Ordering::Equal => <span class="hljs-keyword">self</span>.y.cmp(&other.y),<br> ordering => ordering,<br> }<br> }<br>}<br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">PartialOrd</span> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">partial_cmp</span></span>(&<span class="hljs-keyword">self</span>, other: &<span class="hljs-keyword">Self</span>) -> <span class="hljs-built_in">Option</span><Ordering> {<br> <span class="hljs-literal">Some</span>(<span class="hljs-keyword">self</span>.cmp(other))<br> }<br>}<br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">PartialEq</span> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">eq</span></span>(&<span class="hljs-keyword">self</span>, other: &<span class="hljs-keyword">Self</span>) -> <span class="hljs-built_in">bool</span> {<br> <span class="hljs-keyword">self</span>.cmp(other) == Ordering::Equal<br> }<br>}<br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">Eq</span> <span class="hljs-keyword">for</span> Point {}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>特点</p><ol><li><p>表示集合论的全序</p><p> <a href="https://zh.wikipedia.org/wiki/%E5%85%A8%E5%BA%8F%E5%85%B3%E7%B3%BB">https://zh.wikipedia.org/wiki/全序关系</a></p><p> 集合中上的一个关系,这个关系对于集合中的所有元素均具有反对称性+传递性,那么这个关系在这个集合上就是全序(Order,全序)的</p><ol><li>a < b implies !(a > b) (asymmetry) 反对称性</li><li>a < b && b < c implies a < c (transitivity) 传递性</li></ol></li><li><p>Float 类型仅实现了PartialOrder Trait,但没有实现Order Trait</p><p> 因为NaN < 0 == false and NaN >= 0 == false 这两个断言都是正确的,违反了对所有元素的“反对称性”</p></li></ol></li></ol><h3 id="5-Arithmetic-Traits"><a href="#5-Arithmetic-Traits" class="headerlink" title="5. Arithmetic Traits"></a>5. <strong><strong>Arithmetic Traits</strong></strong></h3><table><thead><tr><th>Trait(s)</th><th>Category</th><th>Operator(s)</th><th>Description</th></tr></thead><tbody><tr><td>Add</td><td>arithmetic</td><td>+</td><td>addition</td></tr><tr><td>AddAssign</td><td>arithmetic</td><td>+=</td><td>addition assignment</td></tr><tr><td>BitAnd</td><td>arithmetic</td><td>&</td><td>bitwise AND</td></tr><tr><td>BitAndAssign</td><td>arithmetic</td><td>&=</td><td>bitwise assignment</td></tr><tr><td>BitXor</td><td>arithmetic</td><td>^</td><td>bitwise XOR</td></tr><tr><td>BitXorAssign</td><td>arithmetic</td><td>^=</td><td>bitwise XOR assignment</td></tr><tr><td>Div</td><td>arithmetic</td><td>/</td><td>division</td></tr><tr><td>DivAssign</td><td>arithmetic</td><td>/=</td><td>division assignment</td></tr><tr><td>Mul</td><td>arithmetic</td><td>*</td><td>multiplication</td></tr><tr><td>MulAssign</td><td>arithmetic</td><td>*=</td><td>multiplication assignment</td></tr><tr><td>Neg</td><td>arithmetic</td><td>-</td><td>unary negation</td></tr><tr><td>Not</td><td>arithmetic</td><td>!</td><td>unary logical negation</td></tr><tr><td>Rem</td><td>arithmetic</td><td>%</td><td>remainder</td></tr><tr><td>RemAssign</td><td>arithmetic</td><td>%=</td><td>remainder assignment</td></tr><tr><td>Shl</td><td>arithmetic</td><td><<</td><td>left shift</td></tr><tr><td>ShlAssign</td><td>arithmetic</td><td><<=</td><td>left shift assignment</td></tr><tr><td>Shr</td><td>arithmetic</td><td>>></td><td>right shift</td></tr><tr><td>ShrAssign</td><td>arithmetic</td><td>>>=</td><td>right shift assignment</td></tr><tr><td>Sub</td><td>arithmetic</td><td>-</td><td>subtraction</td></tr><tr><td>SubAssign</td><td>arithmetic</td><td>-=</td><td>subtraction assignment</td></tr></tbody></table><h3 id="6-Closure-Traits"><a href="#6-Closure-Traits" class="headerlink" title="6. Closure Traits"></a>6. <strong><strong>Closure Traits</strong></strong></h3><table><thead><tr><th>Trait(s)</th><th>Category</th><th>Operator(s)</th><th>Description</th></tr></thead><tbody><tr><td>Fn</td><td>closure</td><td>(…args)</td><td>immutable closure invocation</td></tr><tr><td>FnMut</td><td>closure</td><td>(…args)</td><td>mutable closure invocation</td></tr><tr><td>FnOnce</td><td>closure</td><td>(…args)</td><td>one-time closure invocation</td></tr></tbody></table><p>包含三个Trait:Fn/FnMut/FnOnce</p><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">FnOnce</span></span><Args> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">call_once</span></span>(<span class="hljs-keyword">self</span>, args: Args) -> Self::Output;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">FnMut</span></span><Args>: <span class="hljs-built_in">FnOnce</span><Args> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">call_mut</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, args: Args) -> Self::Output;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Fn</span></span><Args>: <span class="hljs-built_in">FnMut</span><Args> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">call</span></span>(&<span class="hljs-keyword">self</span>, args: Args) -> Self::Output;<br>}<br></code></pre></td></tr></table></figure></li><li><p>实现</p><p> 虽然存在这些 trait,但是在 stable 的 Rust 中,我们无法为自己的类型实现这些 trait。我们能够创建的唯一能够实现这些 trait 的类型就是闭包。闭包根据其从环境中所捕获的内容来决定它到底是实现FnOnce、FnMut还是Fn。</p></li><li><p>特点</p><ol><li><p>FnOnce只能被调用一次</p><p> 执行这个闭包的时候会“移动”一些值</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> range = <span class="hljs-number">0</span>..<span class="hljs-number">10</span>;<br> <span class="hljs-keyword">let</span> get_range_count = || range.count();<br> <span class="hljs-built_in">assert_eq!</span>(get_range_count(), <span class="hljs-number">10</span>); <span class="hljs-comment">// ✅</span><br> get_range_count(); <span class="hljs-comment">// ❌</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>FnMut</p><ol><li><p>FnMut闭包可以被多次调用,并且可以修改它从环境中捕获到的变量。FnMut闭包是“有状态的”</p><p> 这是一个例子,闭包会修改外部的min,并将nums中所有不是升序的值删掉</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> nums = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">8</span>, <span class="hljs-number">10</span>, <span class="hljs-number">7</span>, <span class="hljs-number">15</span>, <span class="hljs-number">18</span>, <span class="hljs-number">13</span>];<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> min = <span class="hljs-built_in">i32</span>::MIN;<br> <span class="hljs-keyword">let</span> ascending = nums.into_iter().filter(|&n| {<br> <span class="hljs-keyword">if</span> n <= min {<br> <span class="hljs-literal">false</span><br> } <span class="hljs-keyword">else</span> {<br> min = n;<br> <span class="hljs-literal">true</span><br> }<br> }).collect::<<span class="hljs-built_in">Vec</span><_>>();<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>, <span class="hljs-number">10</span>, <span class="hljs-number">15</span>, <span class="hljs-number">18</span>], ascending); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>FnMut是FnOnce的子集,</p><ul><li>FnOnce会获取它的参数的所有权并且只能被调用一次,</li><li>FnMut仅要求获取参数的可变引用并且可以被多次调用,</li><li>FnMut细化了FnOnce。FnMut可以被用于任何可以使用FnOnce的地方。</li></ul></li></ol></li><li><p>Fn</p><ol><li><p>Fn闭包也可以被调用多次,但是不能修改外部环境的变量,但可以访问。Fn是“无状态”的</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> nums = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">8</span>, <span class="hljs-number">10</span>, <span class="hljs-number">7</span>, <span class="hljs-number">15</span>, <span class="hljs-number">18</span>, <span class="hljs-number">13</span>];<br> <span class="hljs-keyword">let</span> min = <span class="hljs-number">9</span>;<br> <span class="hljs-keyword">let</span> greater_than_9 = nums.into_iter().filter(|&n| n > min).collect::<<span class="hljs-built_in">Vec</span><_>>();<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">10</span>, <span class="hljs-number">15</span>, <span class="hljs-number">18</span>, <span class="hljs-number">13</span>], greater_than_9); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>Fn是FnMut的子集</p><ul><li>FnMut要求可变引用并且可以被多次调用,</li><li>Fn只要求不可变引用并可以被多次调用,</li><li>Fn细化了FnMut。Fn可以被用于任何可以使用FnMut的地方,当然也包括可以使用FnOnce的地方。</li></ul></li></ol></li></ol></li><li><p>其他</p><ol><li>如果一个闭包不访问任何环境中的变量,从技术角度来讲它算不上是闭包,而只是一个被匿名声明的内联函数,并且可以作为一个普通函数指针(即Fn)被使用和传递,这包括可以使用FnMut和FnOnce的地方。 <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">add_one</span></span>(x: <span class="hljs-built_in">i32</span>) -> <span class="hljs-built_in">i32</span> {<br> x + <span class="hljs-number">1</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> fn_ptr: <span class="hljs-function"><span class="hljs-keyword">fn</span></span>(<span class="hljs-built_in">i32</span>) -> <span class="hljs-built_in">i32</span> = add_one;<br> <span class="hljs-built_in">assert_eq!</span>(fn_ptr(<span class="hljs-number">1</span>), <span class="hljs-number">2</span>); <span class="hljs-comment">// ✅</span><br> <br> <span class="hljs-comment">// capture-less closure cast to fn pointer</span><br> fn_ptr = |x| x + <span class="hljs-number">1</span>; <span class="hljs-comment">// same as add_one</span><br> <span class="hljs-built_in">assert_eq!</span>(fn_ptr(<span class="hljs-number">1</span>), <span class="hljs-number">2</span>); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure> 下面是一个传递普通函数指针而不是闭包的示例: <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> nums = <span class="hljs-built_in">vec!</span>[-<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, -<span class="hljs-number">3</span>, <span class="hljs-number">3</span>];<br> <span class="hljs-keyword">let</span> absolutes: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">i32</span>> = nums.into_iter().map(<span class="hljs-built_in">i32</span>::abs).collect();<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>], absolutes); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h3 id="7-Deref-Trait"><a href="#7-Deref-Trait" class="headerlink" title="7. Deref Trait"></a>7. Deref Trait</h3><table><thead><tr><th>Trait(s)</th><th>Category</th><th>Operator(s)</th><th>Description</th><th></th></tr></thead><tbody><tr><td>Deref</td><td>other</td><td>*</td><td>immutable dereference</td><td>不可变引用</td></tr><tr><td>DerefMut</td><td>other</td><td>*</td><td>mutable derenence</td><td>可变引用</td></tr></tbody></table><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Deref</span></span> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Target</span></span>: ?<span class="hljs-built_in">Sized</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">deref</span></span>(&<span class="hljs-keyword">self</span>) -> &Self::Target;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">DerefMut</span></span>: Deref {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">deref_mut</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> &<span class="hljs-keyword">mut</span> Self::Target;<br>}<br></code></pre></td></tr></table></figure></li><li><p>功能</p><p> Deref<Target = T>类型可以使用 * 操作符解引用为T类型。</p></li><li><p>特点</p><ol><li>当类型被作为函数参数传递、从函数返回或者作为方法调用的一部分时,Rust 会自动对这些类型进行解引用。<ol><li>我们可以在一个期望<code>&str</code>和<code>&[T]</code>的函数中可以传入<code>&String</code>和<code>&Vec<T></code>,<ol><li><code>String</code>实现了<code>Deref<Target = str></code></li><li><code>Vec<T></code>实现了<code>Deref<Target = [T]></code>。</li></ol></li></ol></li><li>Deref和DerefMut应该仅被实现于智能指针类型。</li><li>不要用deref来实现OOP的多态</li></ol></li></ol><h3 id="8-Index-amp-IndexMut"><a href="#8-Index-amp-IndexMut" class="headerlink" title="8. Index & IndexMut"></a>8. <strong>Index & IndexMut</strong></h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Index</span></span><Idx: ?<span class="hljs-built_in">Sized</span>> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span>: ?<span class="hljs-built_in">Sized</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">index</span></span>(&<span class="hljs-keyword">self</span>, index: Idx) -> &Self::Output;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">IndexMut</span></span><Idx>: Index<Idx> <span class="hljs-keyword">where</span> Idx: ?<span class="hljs-built_in">Sized</span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">index_mut</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, index: Idx) -> &<span class="hljs-keyword">mut</span> Self::Output;<br>}<br></code></pre></td></tr></table></figure></li><li><p>功能</p><ol><li>我们可以用[]索引具有 T 值的Index<T, Output = U>类型,索引操作将返回&U值。</li></ol></li><li><p>特点</p><ol><li><p>索引操作后,编译器默认在返回值前插入解引用</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-comment">// Vec<i32> impls Index<usize, Output = i32> so</span><br> <span class="hljs-comment">// indexing Vec<i32> should produce &i32s and yet...</span><br> <span class="hljs-keyword">let</span> vec = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];<br> <span class="hljs-keyword">let</span> num_ref: &<span class="hljs-built_in">i32</span> = vec[<span class="hljs-number">0</span>]; <span class="hljs-comment">// ❌ expected &i32 found i32</span><br> <br> <span class="hljs-comment">// above line actually desugars to</span><br> <span class="hljs-keyword">let</span> num_ref: &<span class="hljs-built_in">i32</span> = *vec[<span class="hljs-number">0</span>]; <span class="hljs-comment">// ❌ expected &i32 found i32</span><br><br> <span class="hljs-comment">// both of these alternatives work</span><br> <span class="hljs-keyword">let</span> num: <span class="hljs-built_in">i32</span> = vec[<span class="hljs-number">0</span>]; <span class="hljs-comment">// ✅</span><br> <span class="hljs-keyword">let</span> num_ref = &vec[<span class="hljs-number">0</span>]; <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>用来索引的Idx是一个泛型,举几个其他类型用来索引的例子</p><ol><li><p>可以是usize</p></li><li><p>可以是其他整数类型</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::ops::Index;<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">WrappingIndex</span></span><T>(<span class="hljs-built_in">Vec</span><T>);<br><br><span class="hljs-keyword">impl</span><T> Index<<span class="hljs-built_in">usize</span>> <span class="hljs-keyword">for</span> WrappingIndex<T> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = T;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">index</span></span>(&<span class="hljs-keyword">self</span>, index: <span class="hljs-built_in">usize</span>) -> &T {<br> &<span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>[index % <span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>.len()]<br> }<br>}<br><br><span class="hljs-keyword">impl</span><T> Index<<span class="hljs-built_in">i128</span>> <span class="hljs-keyword">for</span> WrappingIndex<T> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = T;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">index</span></span>(&<span class="hljs-keyword">self</span>, index: <span class="hljs-built_in">i128</span>) -> &T {<br> <span class="hljs-keyword">let</span> self_len = <span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>.len() <span class="hljs-keyword">as</span> <span class="hljs-built_in">i128</span>;<br> <span class="hljs-keyword">let</span> idx = (((index % self_len) + self_len) % self_len) <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>;<br> &<span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>[idx]<br> }<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">indexes</span></span>() {<br> <span class="hljs-keyword">let</span> wrapping_vec = WrappingIndex(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">1</span>, wrapping_vec[<span class="hljs-number">0_usize</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">2</span>, wrapping_vec[<span class="hljs-number">1_usize</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">3</span>, wrapping_vec[<span class="hljs-number">2_usize</span>]);<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">wrapping_indexes</span></span>() {<br> <span class="hljs-keyword">let</span> wrapping_vec = WrappingIndex(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">1</span>, wrapping_vec[<span class="hljs-number">3_usize</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">2</span>, wrapping_vec[<span class="hljs-number">4_usize</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">3</span>, wrapping_vec[<span class="hljs-number">5_usize</span>]);<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">neg_indexes</span></span>() {<br> <span class="hljs-keyword">let</span> wrapping_vec = WrappingIndex(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">1</span>, wrapping_vec[-<span class="hljs-number">3_i128</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">2</span>, wrapping_vec[-<span class="hljs-number">2_i128</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">3</span>, wrapping_vec[-<span class="hljs-number">1_i128</span>]);<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">wrapping_neg_indexes</span></span>() {<br> <span class="hljs-keyword">let</span> wrapping_vec = WrappingIndex(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">1</span>, wrapping_vec[-<span class="hljs-number">6_i128</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">2</span>, wrapping_vec[-<span class="hljs-number">5_i128</span>]);<br> <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-number">3</span>, wrapping_vec[-<span class="hljs-number">4_i128</span>]);<br>}<br></code></pre></td></tr></table></figure></li><li><p>可以是满足 Range<usize> 的类型</p><p> 下面这个例子中,1.. /..4/1..4 都是满足 Range<usize> 的类型</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {<br> <span class="hljs-keyword">let</span> vec = <span class="hljs-built_in">vec!</span>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];<br> <span class="hljs-built_in">assert_eq!</span>(&vec[..], &[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]); <span class="hljs-comment">// ✅</span><br> <span class="hljs-built_in">assert_eq!</span>(&vec[<span class="hljs-number">1</span>..], &[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]); <span class="hljs-comment">// ✅</span><br> <span class="hljs-built_in">assert_eq!</span>(&vec[..<span class="hljs-number">4</span>], &[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]); <span class="hljs-comment">// ✅</span><br> <span class="hljs-built_in">assert_eq!</span>(&vec[<span class="hljs-number">1</span>..<span class="hljs-number">4</span>], &[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>可以是枚举</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::ops::Index;<br><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">BasketballPosition</span></span> {<br> PointGuard,<br> ShootingGuard,<br> Center,<br> PowerForward,<br> SmallForward,<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BasketballPlayer</span></span> {<br> name: &<span class="hljs-symbol">'static</span> <span class="hljs-built_in">str</span>,<br> position: BasketballPosition,<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BasketballTeam</span></span> {<br> point_guard: BasketballPlayer,<br> shooting_guard: BasketballPlayer,<br> center: BasketballPlayer,<br> power_forward: BasketballPlayer,<br> small_forward: BasketballPlayer,<br>}<br><br><span class="hljs-keyword">impl</span> Index<BasketballPosition> <span class="hljs-keyword">for</span> BasketballTeam {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Output</span></span> = BasketballPlayer;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">index</span></span>(&<span class="hljs-keyword">self</span>, position: BasketballPosition) -> &BasketballPlayer {<br> <span class="hljs-keyword">match</span> position {<br> BasketballPosition::PointGuard => &<span class="hljs-keyword">self</span>.point_guard,<br> BasketballPosition::ShootingGuard => &<span class="hljs-keyword">self</span>.shooting_guard,<br> BasketballPosition::Center => &<span class="hljs-keyword">self</span>.center,<br> BasketballPosition::PowerForward => &<span class="hljs-keyword">self</span>.power_forward,<br> BasketballPosition::SmallForward => &<span class="hljs-keyword">self</span>.small_forward,<br> }<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol></li></ol><h3 id="8-Drop-Trait"><a href="#8-Drop-Trait" class="headerlink" title="8. Drop Trait"></a>8. Drop Trait</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Drop</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">drop</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>);<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> drop函数会在该类型离开作用域但是销毁之前被调用。用来销毁某些外部资源</p></li><li><p>例子</p><ol><li><p>标准库中有一个BufWriter类型让我们能够把写入的数据缓冲到Write类型中。</p><p> 为了避免Bufwriter的内容在写入底层write类型之前就被销毁,Bufwirter实现了Drop Trait</p><p> 让Bufwriter对象在销毁前保证刷新缓冲区</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span><W: Write> <span class="hljs-built_in">Drop</span> <span class="hljs-keyword">for</span> BufWriter<W> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">drop</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) {<br> <span class="hljs-keyword">self</span>.flush_buf();<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>Rust 中的Mutexs没有unlock()方法,因为它们不需要!</p><ol><li>在Mutex上调用lock()会返回一个MutexGuard,</li><li>当MutexGuard离开作用域时,它会调用drop函数自动解锁(unlock)Mutex,</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span><T: ?<span class="hljs-built_in">Sized</span>> <span class="hljs-built_in">Drop</span> <span class="hljs-keyword">for</span> MutexGuard<<span class="hljs-symbol">'_</span>, T> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">drop</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) {<br> <span class="hljs-keyword">unsafe</span> {<br> <span class="hljs-keyword">self</span>.lock.inner.raw_unlock();<br> }<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h2 id="六、Conversion-Traits"><a href="#六、Conversion-Traits" class="headerlink" title="六、Conversion Traits"></a>六、<strong><strong>Conversion Traits</strong></strong></h2><h3 id="1-From-amp-Into"><a href="#1-From-amp-Into" class="headerlink" title="1. From&Into"></a>1. From&Into</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">From</span></span><T> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(T) -> <span class="hljs-keyword">Self</span>;<br>}<br></code></pre></td></tr></table></figure> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Into</span></span><T> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">into</span></span>(<span class="hljs-keyword">self</span>) -> T;<br>}<br><br><span class="hljs-keyword">impl</span><T, U> <span class="hljs-built_in">Into</span><U> <span class="hljs-keyword">for</span> T<br><span class="hljs-keyword">where</span><br> U: <span class="hljs-built_in">From</span><T>,<br>{<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">into</span></span>(<span class="hljs-keyword">self</span>) -> U {<br> U::from(<span class="hljs-keyword">self</span>)<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><ol><li>From Trait允许把T类型转换为Self类型(通常为自己实现的类型)</li><li>只能为自己的类型实现From<T>,因为Into<T>的实现会通过 generic blanket impl 自动提供</li></ol></li><li><p>使用例子</p><ol><li><p>基础例子</p><ol><li><p>可以从(x,y)tuple或者 array 转换为Point类型</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><(<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>)> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>((x, y): (<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>)) -> <span class="hljs-keyword">Self</span> {<br> Point { x, y }<br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><Point> <span class="hljs-keyword">for</span> (<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>) {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(Point { x, y }: Point) -> <span class="hljs-keyword">Self</span> {<br> (x, y)<br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><[<span class="hljs-built_in">i32</span>; <span class="hljs-number">2</span>]> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>([x, y]: [<span class="hljs-built_in">i32</span>; <span class="hljs-number">2</span>]) -> <span class="hljs-keyword">Self</span> {<br> Point { x, y }<br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><Point> <span class="hljs-keyword">for</span> [<span class="hljs-built_in">i32</span>; <span class="hljs-number">2</span>] {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(Point { x, y }: Point) -> <span class="hljs-keyword">Self</span> {<br> [x, y]<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span>() {<br> <span class="hljs-comment">// 从 (i32, i32) 到 Point</span><br> <span class="hljs-keyword">let</span> point = Point::from((<span class="hljs-number">0</span>, <span class="hljs-number">0</span>));<br> <span class="hljs-keyword">let</span> point: Point = (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>).into();<br><br> <span class="hljs-comment">// 从 Point 到 (i32, i32)</span><br> <span class="hljs-keyword">let</span> tuple = <(<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>)>::from(point);<br> <span class="hljs-keyword">let</span> tuple: (<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>) = point.into();<br><br> <span class="hljs-comment">// 从 [i32; 2] 到 Point</span><br> <span class="hljs-keyword">let</span> point = Point::from([<span class="hljs-number">0</span>, <span class="hljs-number">0</span>]);<br> <span class="hljs-keyword">let</span> point: Point = [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>].into();<br><br> <span class="hljs-comment">// 从 Point 到 [i32; 2]</span><br> <span class="hljs-keyword">let</span> array = <[<span class="hljs-built_in">i32</span>; <span class="hljs-number">2</span>]>::from(point);<br> <span class="hljs-keyword">let</span> array: [<span class="hljs-built_in">i32</span>; <span class="hljs-number">2</span>] = point.into();<br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>使得“需要对参数拥有所有权的”函数具有通用型,不关心传进来的参数是拥有值还是值得引用</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Person</span></span> {<br> name: <span class="hljs-built_in">String</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Person {<br> <span class="hljs-comment">// 接受:</span><br> <span class="hljs-comment">// - String</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new1</span></span>(name: <span class="hljs-built_in">String</span>) -> Person {<br> Person { name }<br> }<br><br> <span class="hljs-comment">// 接受:</span><br> <span class="hljs-comment">// - String</span><br> <span class="hljs-comment">// - &String</span><br> <span class="hljs-comment">// - &str</span><br> <span class="hljs-comment">// - Box<str></span><br> <span class="hljs-comment">// - Cow<'_, str></span><br> <span class="hljs-comment">// - char</span><br> <span class="hljs-comment">// 因为上面所有的类型都可以转换为 String</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new2</span></span><N: <span class="hljs-built_in">Into</span><<span class="hljs-built_in">String</span>>>(name: N) -> Person {<br> Person { name: name.into() }<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h2 id="七、错误处理"><a href="#七、错误处理" class="headerlink" title="七、错误处理"></a>七、错误处理</h2><h3 id="1-Error-Trait"><a href="#1-Error-Trait" class="headerlink" title="1. Error Trait"></a>1. Error Trait</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Error</span></span>: <span class="hljs-built_in">Debug</span> + Display {<br> <span class="hljs-comment">// 提供默认实现</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">source</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><&(<span class="hljs-keyword">dyn</span> Error + <span class="hljs-symbol">'static</span>)>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">backtrace</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><&Backtrace>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">description</span></span>(&<span class="hljs-keyword">self</span>) -> &<span class="hljs-built_in">str</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cause</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><&<span class="hljs-keyword">dyn</span> Error>;<br>} <br></code></pre></td></tr></table></figure></li><li><p>功能</p><ol><li>rust中,错误是被return返回的,而不是被throw抛出的</li></ol></li><li><p>一个例子</p><ol><li><p>安全除法</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::fmt;<br><span class="hljs-keyword">use</span> std::error;<br><br><span class="hljs-meta">#[derive(Debug, PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DivByZero</span></span>;<br><br><span class="hljs-keyword">impl</span> fmt::Display <span class="hljs-keyword">for</span> DivByZero {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> fmt::Formatter<<span class="hljs-symbol">'_</span>>) -> fmt::<span class="hljs-built_in">Result</span> {<br> <span class="hljs-built_in">write!</span>(f, <span class="hljs-string">"division by zero error"</span>)<br> }<br>}<br><br><span class="hljs-keyword">impl</span> error::Error <span class="hljs-keyword">for</span> DivByZero {}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">safe_div</span></span>(numerator: <span class="hljs-built_in">i32</span>, denominator: <span class="hljs-built_in">i32</span>) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">i32</span>, DivByZero> {<br> <span class="hljs-keyword">if</span> denominator == <span class="hljs-number">0</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(DivByZero);<br> }<br> <span class="hljs-literal">Ok</span>(numerator / denominator)<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_safe_div</span></span>() {<br> <span class="hljs-built_in">assert_eq!</span>(safe_div(<span class="hljs-number">8</span>, <span class="hljs-number">2</span>), <span class="hljs-literal">Ok</span>(<span class="hljs-number">4</span>));<br> <span class="hljs-built_in">assert_eq!</span>(safe_div(<span class="hljs-number">5</span>, <span class="hljs-number">0</span>), <span class="hljs-literal">Err</span>(DivByZero));<br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>特点</p><ol><li><p>错误是被返回而不是被抛出,所以这些错误必须被显式地处理,如果当前函数无法处理错误,该函数应该把错误传递给自己的调用者。</p></li><li><p>传递错误的最常用方式是使用?操作符,它是现在已经弃用的try!宏的语法糖</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-built_in">macro_rules!</span> <span class="hljs-keyword">try</span> {<br> ($expr:expr) => {<br> <span class="hljs-keyword">match</span> $expr {<br> <span class="hljs-comment">// if Ok just unwrap the value</span><br> <span class="hljs-literal">Ok</span>(val) => val,<br> <span class="hljs-comment">// if Err map the err value using From and return</span><br> <span class="hljs-literal">Err</span>(err) => {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(<span class="hljs-built_in">From</span>::from(err));<br> }<br> }<br> };<br>}<br></code></pre></td></tr></table></figure></li><li><p>一个返回io::Error的例子</p><ol><li><p>这个例子中,所有可能的error都是io::Error类型的</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::io::Read;<br><span class="hljs-keyword">use</span> std::path::Path;<br><span class="hljs-keyword">use</span> std::io;<br><span class="hljs-keyword">use</span> std::fs::File;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_file_to_string</span></span>(path: &Path) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">String</span>, io::Error> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> file = File::open(path)?; <span class="hljs-comment">// ⬆️ io::Error</span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> contents = <span class="hljs-built_in">String</span>::new();<br> file.read_to_string(&<span class="hljs-keyword">mut</span> contents)?; <span class="hljs-comment">// ⬆️ io::Error</span><br> <span class="hljs-literal">Ok</span>(contents)<br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>如何返回不同种类的Error</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::io::Read;<br><span class="hljs-keyword">use</span> std::path::Path;<br><span class="hljs-keyword">use</span> std::io;<br><span class="hljs-keyword">use</span> std::fs::File;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">sum_file</span></span>(path: &Path) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">i32</span>, <span class="hljs-comment">/*这里放置什么? */</span>> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> file = File::open(path)?; <span class="hljs-comment">// ⬆️ io::Error</span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> contents = <span class="hljs-built_in">String</span>::new();<br> file.read_to_string(&<span class="hljs-keyword">mut</span> contents)?; <span class="hljs-comment">// ⬆️ io::Error</span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> sum = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> contents.lines() {<br> sum += line.parse::<<span class="hljs-built_in">i32</span>>()?; <span class="hljs-comment">// ⬆️ ParseIntError</span><br> }<br> <span class="hljs-literal">Ok</span>(sum)<br>}<br></code></pre></td></tr></table></figure><ol><li><p>一种方法是,使用dyn error::Error 接受任意类型Error</p><ol><li><p>任何一种error类型均可以转换为Box<dyn error::Error> 类型</p><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span><E: error::Error> <span class="hljs-built_in">From</span><E> <span class="hljs-keyword">for</span> <span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> error::Error>;<br></code></pre></td></tr></table></figure></li><li><p>所以,任意一种Error均可以通过Box<dyn error::Error> 返回给调用者</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::fs::File;<br><span class="hljs-keyword">use</span> std::io::Read;<br><span class="hljs-keyword">use</span> std::path::Path;<br><span class="hljs-keyword">use</span> std::error;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">sum_file</span></span>(path: &Path) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">Box</span><<span class="hljs-keyword">dyn</span> error::Error>> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> file = File::open(path)?; <span class="hljs-comment">// ⬆️ io::Error -> Box<dyn error::Error></span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> contents = <span class="hljs-built_in">String</span>::new();<br> file.read_to_string(&<span class="hljs-keyword">mut</span> contents)?; <span class="hljs-comment">// ⬆️ io::Error -> Box<dyn error::Error></span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> sum = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> contents.lines() {<br> sum += line.parse::<<span class="hljs-built_in">i32</span>>()?; <span class="hljs-comment">// ⬆️ ParseIntError -> Box<dyn error::Error></span><br> }<br> <span class="hljs-literal">Ok</span>(sum)<br>}<br></code></pre></td></tr></table></figure></li><li><p>如果调用者想知道返回错误的具体类型,需要使用downcast_ref()</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">handle_sum_file_errors</span></span>(path: &Path) {<br> <span class="hljs-keyword">match</span> sum_file(path) {<br> <span class="hljs-literal">Ok</span>(sum) => <span class="hljs-built_in">println!</span>(<span class="hljs-string">"the sum is {}"</span>, sum),<br> <span class="hljs-literal">Err</span>(err) => {<br> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(e) = err.downcast_ref::<io::Error>() {<br> <span class="hljs-comment">// 处理 io::Error</span><br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(e) = err.downcast_ref::<ParseIntError>() {<br> <span class="hljs-comment">// 处理 ParseIntError</span><br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 我们知道 sum_file 只会返回上面错误中的其中一个</span><br> <span class="hljs-comment">// 所以不会到达这个分支</span><br> <span class="hljs-built_in">unreachable!</span>();<br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li><li><p>第二种方法是:使用Enum类型枚举所有可能的error</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::num::ParseIntError;<br><span class="hljs-keyword">use</span> std::fs::File;<br><span class="hljs-keyword">use</span> std::io;<br><span class="hljs-keyword">use</span> std::io::Read;<br><span class="hljs-keyword">use</span> std::path::Path;<br><span class="hljs-keyword">use</span> std::error;<br><span class="hljs-keyword">use</span> std::fmt;<br><br><span class="hljs-meta">#[derive(Debug)]</span><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">SumFileError</span></span> {<br> Io(io::Error),<br> Parse(ParseIntError),<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><io::Error> <span class="hljs-keyword">for</span> SumFileError {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(err: io::Error) -> <span class="hljs-keyword">Self</span> {<br> SumFileError::Io(err)<br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><ParseIntError> <span class="hljs-keyword">for</span> SumFileError {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(err: ParseIntError) -> <span class="hljs-keyword">Self</span> {<br> SumFileError::Parse(err)<br> }<br>}<br><br><span class="hljs-keyword">impl</span> fmt::Display <span class="hljs-keyword">for</span> SumFileError {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> fmt::Formatter<<span class="hljs-symbol">'_</span>>) -> fmt::<span class="hljs-built_in">Result</span> {<br> <span class="hljs-keyword">match</span> <span class="hljs-keyword">self</span> {<br> SumFileError::Io(err) => <span class="hljs-built_in">write!</span>(f, <span class="hljs-string">"sum file error: {}"</span>, err),<br> SumFileError::Parse(err) => <span class="hljs-built_in">write!</span>(f, <span class="hljs-string">"sum file error: {}"</span>, err),<br> }<br> }<br>}<br><br><span class="hljs-keyword">impl</span> error::Error <span class="hljs-keyword">for</span> SumFileError {<br> <span class="hljs-comment">// 这个方法的默认实现总是返回 None</span><br> <span class="hljs-comment">//但是我们现在重写它,让它更有用 </span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">source</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><&(<span class="hljs-keyword">dyn</span> error::Error + <span class="hljs-symbol">'static</span>)> {<br> <span class="hljs-literal">Some</span>(<span class="hljs-keyword">match</span> <span class="hljs-keyword">self</span> {<br> SumFileError::Io(err) => err,<br> SumFileError::Parse(err) => err,<br> })<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">sum_file</span></span>(path: &Path) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">i32</span>, SumFileError> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> file = File::open(path)?; <span class="hljs-comment">// ⬆️ io::Error -> SumFileError</span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> contents = <span class="hljs-built_in">String</span>::new();<br> file.read_to_string(&<span class="hljs-keyword">mut</span> contents)?; <span class="hljs-comment">// ⬆️ io::Error -> SumFileError</span><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> sum = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> contents.lines() {<br> sum += line.parse::<<span class="hljs-built_in">i32</span>>()?; <span class="hljs-comment">// ⬆️ ParseIntError -> SumFileError</span><br> }<br> <span class="hljs-literal">Ok</span>(sum)<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">handle_sum_file_errors</span></span>(path: &Path) {<br> <span class="hljs-keyword">match</span> sum_file(path) {<br> <span class="hljs-literal">Ok</span>(sum) => <span class="hljs-built_in">println!</span>(<span class="hljs-string">"the sum is {}"</span>, sum),<br> <span class="hljs-literal">Err</span>(SumFileError::Io(err)) => {<br> <span class="hljs-comment">// 处理 io::Error</span><br> },<br> <span class="hljs-literal">Err</span>(SumFileError::Parse(err)) => {<br> <span class="hljs-comment">// 处理 ParseIntError</span><br> },<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol></li></ol><h2 id="六、转换类型(续)"><a href="#六、转换类型(续)" class="headerlink" title="六、转换类型(续)"></a>六、转换类型(续)</h2><h3 id="1-TryFrom-amp-TryInto"><a href="#1-TryFrom-amp-TryInto" class="headerlink" title="1. TryFrom & TryInto"></a>1. TryFrom & TryInto</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">TryFrom</span></span><T> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: T) -> <span class="hljs-built_in">Result</span><<span class="hljs-keyword">Self</span>, Self::Error>;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">TryInto</span></span><T> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_into</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Result</span><T, Self::Error>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>功能:允许失败版本的From/Into</p></li><li><p>一个例子</p><ol><li><p>Point中的x和y如果值小于-1000或者大于1000没有意义,则可以如下实现转换函数</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::convert::TryFrom;<br><span class="hljs-keyword">use</span> std::error;<br><span class="hljs-keyword">use</span> std::fmt;<br><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-meta">#[derive(Debug)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">OutOfBounds</span></span>;<br><br><span class="hljs-keyword">impl</span> fmt::Display <span class="hljs-keyword">for</span> OutOfBounds {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> fmt::Formatter<<span class="hljs-symbol">'_</span>>) -> fmt::<span class="hljs-built_in">Result</span> {<br> <span class="hljs-built_in">write!</span>(f, <span class="hljs-string">"out of bounds"</span>)<br> }<br>}<br><br><span class="hljs-keyword">impl</span> error::Error <span class="hljs-keyword">for</span> OutOfBounds {}<br><br><span class="hljs-comment">// 现在是可以出错的</span><br><span class="hljs-keyword">impl</span> TryFrom<(<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>)> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = OutOfBounds;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>((x, y): (<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>)) -> <span class="hljs-built_in">Result</span><Point, OutOfBounds> {<br> <span class="hljs-keyword">if</span> x.abs() > <span class="hljs-number">1000</span> || y.abs() > <span class="hljs-number">1000</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">Err</span>(OutOfBounds);<br> }<br> <span class="hljs-literal">Ok</span>(Point { x, y })<br> }<br>}<br><br><span class="hljs-comment">// 仍然是不会出错的</span><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><Point> <span class="hljs-keyword">for</span> (<span class="hljs-built_in">i32</span>, <span class="hljs-built_in">i32</span>) {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(Point { x, y }: Point) -> <span class="hljs-keyword">Self</span> {<br> (x, y)<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h3 id="2-FromStr"><a href="#2-FromStr" class="headerlink" title="2. FromStr"></a>2. FromStr</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">FromStr</span></span> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Err</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_str</span></span>(s: &<span class="hljs-built_in">str</span>) -> <span class="hljs-built_in">Result</span><<span class="hljs-keyword">Self</span>, Self::<span class="hljs-literal">Err</span>>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>功能</p><p> FromStr 类型允许执行一个从&str到Self的可失败的转换。最常见的使用是在&str上调用.parse()方法。</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::FromStr;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span><T: FromStr>(s: &<span class="hljs-symbol">'static</span> <span class="hljs-built_in">str</span>) {<br> <span class="hljs-comment">// 这些都是相等的</span><br> <span class="hljs-keyword">let</span> t: <span class="hljs-built_in">Result</span><T, _> = FromStr::from_str(s);<br> <span class="hljs-keyword">let</span> t = T::from_str(s);<br> <span class="hljs-keyword">let</span> t: <span class="hljs-built_in">Result</span><T, _> = s.parse();<br> <span class="hljs-keyword">let</span> t = s.parse::<T>(); <span class="hljs-comment">// 最常见的</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>一个例子</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::error;<br><span class="hljs-keyword">use</span> std::fmt;<br><span class="hljs-keyword">use</span> std::iter::Enumerate;<br><span class="hljs-keyword">use</span> std::num::ParseIntError;<br><span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::{Chars, FromStr};<br><br><span class="hljs-meta">#[derive(Debug, Eq, PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Point</span></span> {<br> x: <span class="hljs-built_in">i32</span>,<br> y: <span class="hljs-built_in">i32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> Point {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(x: <span class="hljs-built_in">i32</span>, y: <span class="hljs-built_in">i32</span>) -> <span class="hljs-keyword">Self</span> {<br> Point { x, y }<br> }<br>}<br><br><span class="hljs-meta">#[derive(Debug, PartialEq)]</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParsePointError</span></span>;<br><br><span class="hljs-keyword">impl</span> fmt::Display <span class="hljs-keyword">for</span> ParsePointError {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&<span class="hljs-keyword">self</span>, f: &<span class="hljs-keyword">mut</span> fmt::Formatter<<span class="hljs-symbol">'_</span>>) -> fmt::<span class="hljs-built_in">Result</span> {<br> <span class="hljs-built_in">write!</span>(f, <span class="hljs-string">"failed to parse point"</span>)<br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span><ParseIntError> <span class="hljs-keyword">for</span> ParsePointError {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(_e: ParseIntError) -> <span class="hljs-keyword">Self</span> {<br> ParsePointError<br> }<br>}<br><br><span class="hljs-keyword">impl</span> error::Error <span class="hljs-keyword">for</span> ParsePointError {}<br><br><span class="hljs-keyword">impl</span> FromStr <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Err</span></span> = ParsePointError;<br><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_str</span></span>(s: &<span class="hljs-built_in">str</span>) -> <span class="hljs-built_in">Result</span><<span class="hljs-keyword">Self</span>, Self::<span class="hljs-literal">Err</span>> {<br> <span class="hljs-keyword">let</span> is_num = |(_, c): &(<span class="hljs-built_in">usize</span>, <span class="hljs-built_in">char</span>)| matches!(c, <span class="hljs-string">'0'</span>..=<span class="hljs-string">'9'</span> | <span class="hljs-string">'-'</span>);<br> <span class="hljs-keyword">let</span> isnt_num = |t: &(_, _)| !is_num(t);<br><br> <span class="hljs-keyword">let</span> get_num =<br> |char_idxs: &<span class="hljs-keyword">mut</span> Enumerate<Chars<<span class="hljs-symbol">'_</span>>>| -> <span class="hljs-built_in">Result</span><(<span class="hljs-built_in">usize</span>, <span class="hljs-built_in">usize</span>), ParsePointError> {<br> <span class="hljs-keyword">let</span> (start, _) = char_idxs<br> .skip_while(isnt_num)<br> .next()<br> .ok_or(ParsePointError)?;<br> <span class="hljs-keyword">let</span> (end, _) = char_idxs<br> .skip_while(is_num)<br> .next()<br> .ok_or(ParsePointError)?;<br> <span class="hljs-literal">Ok</span>((start, end))<br> };<br><br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> char_idxs = s.chars().enumerate();<br> <span class="hljs-keyword">let</span> (x_start, x_end) = get_num(&<span class="hljs-keyword">mut</span> char_idxs)?;<br> <span class="hljs-keyword">let</span> (y_start, y_end) = get_num(&<span class="hljs-keyword">mut</span> char_idxs)?;<br><br> <span class="hljs-keyword">let</span> x = s[x_start..x_end].parse::<<span class="hljs-built_in">i32</span>>()?;<br> <span class="hljs-keyword">let</span> y = s[y_start..y_end].parse::<<span class="hljs-built_in">i32</span>>()?;<br><br> <span class="hljs-literal">Ok</span>(Point { x, y })<br> }<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">pos_x_y</span></span>() {<br> <span class="hljs-keyword">let</span> p = <span class="hljs-string">"(4, 5)"</span>.parse::<Point>();<br> <span class="hljs-built_in">assert_eq!</span>(p, <span class="hljs-literal">Ok</span>(Point::new(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>)));<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">neg_x_y</span></span>() {<br> <span class="hljs-keyword">let</span> p = <span class="hljs-string">"(-6, -2)"</span>.parse::<Point>();<br> <span class="hljs-built_in">assert_eq!</span>(p, <span class="hljs-literal">Ok</span>(Point::new(-<span class="hljs-number">6</span>, -<span class="hljs-number">2</span>)));<br>}<br><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">not_a_point</span></span>() {<br> <span class="hljs-keyword">let</span> p = <span class="hljs-string">"not a point"</span>.parse::<Point>();<br> <span class="hljs-built_in">assert_eq!</span>(p, <span class="hljs-literal">Err</span>(ParsePointError));<br>}<br></code></pre></td></tr></table></figure></li><li><p>特点</p><ol><li><p>FromStr和TryFrom<&str>有着相同的签名。只要我们通过其中一个实现另一个,先实现哪个并不重要。</p><ol><li>一个例子</li></ol> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span> TryFrom<&<span class="hljs-built_in">str</span>> <span class="hljs-keyword">for</span> Point {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = <Point <span class="hljs-keyword">as</span> FromStr>::<span class="hljs-literal">Err</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(s: &<span class="hljs-built_in">str</span>) -> <span class="hljs-built_in">Result</span><Point, Self::Error> {<br> <Point <span class="hljs-keyword">as</span> FromStr>::from_str(s)<br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol><h3 id="2-AsRef-amp-AsMut"><a href="#2-AsRef-amp-AsMut" class="headerlink" title="2. AsRef & AsMut"></a>2. <strong><strong>AsRef & AsMut</strong></strong></h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">AsRef</span></span><T: ?<span class="hljs-built_in">Sized</span>> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_ref</span></span>(&<span class="hljs-keyword">self</span>) -> &T;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">AsMut</span></span><T: ?<span class="hljs-built_in">Sized</span>> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_mut</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> &<span class="hljs-keyword">mut</span> T;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用:</p><ol><li><p>总的来说,AsRef被用于轻量级的引用到引用之间的转换。下面举几个实际的例子</p></li><li><p>AsRef trait可以使函数在是否获取所有权上具有通用性:</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// accepts:</span><br><span class="hljs-comment">// - &str</span><br><span class="hljs-comment">// - &String</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">takes_str</span></span>(s: &<span class="hljs-built_in">str</span>) {<br> <span class="hljs-comment">// use &str</span><br>}<br><br><span class="hljs-comment">// accepts:</span><br><span class="hljs-comment">// - &str</span><br><span class="hljs-comment">// - &String</span><br><span class="hljs-comment">// - String</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">takes_asref_str</span></span><S: <span class="hljs-built_in">AsRef</span><<span class="hljs-built_in">str</span>>>(s: S) {<br> <span class="hljs-keyword">let</span> s: &<span class="hljs-built_in">str</span> = s.as_ref();<br> <span class="hljs-comment">// use &str</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span>(slice: &<span class="hljs-built_in">str</span>, borrow: &<span class="hljs-built_in">String</span>, owned: <span class="hljs-built_in">String</span>) {<br> takes_str(slice);<br> takes_str(borrow);<br> takes_str(owned); <span class="hljs-comment">// ❌</span><br> takes_asref_str(slice);<br> takes_asref_str(borrow);<br> takes_asref_str(owned); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>AsRef 可以返回类型内部私有数据的引用。通常这个私有数据被一个不可变类型所包裹</p><p> 例如标准库的String, 包裹了Vec<u8> 作为私有数据。内部的Vec<u8>不能被公开,因为如果这样的话,人们就会修改里面的字节并破坏String中有效的 UTF-8 编码。但是,暴露内部字节数组的一个不可变的只读引用是安全的,</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">String</span></span> {<br> vec: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">u8</span>>,<br>}<br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">AsRef</span><[<span class="hljs-built_in">u8</span>]> <span class="hljs-keyword">for</span> <span class="hljs-built_in">String</span>;<br></code></pre></td></tr></table></figure></li><li><p>AsRef用于语义相等的事物之间引用到引用的转换,</p><ol><li><p>一个反例</p><p> 下面是为任意类型实现AsRef</p><p> User 包含String/u32, 但User不是String/u32。对于这样的类型实现AsRef没有什么意义,</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span></span> {<br> name: <span class="hljs-built_in">String</span>,<br> email: <span class="hljs-built_in">String</span>,<br> age: <span class="hljs-built_in">u32</span>,<br> height: <span class="hljs-built_in">u32</span>,<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">AsRef</span><<span class="hljs-built_in">String</span>> <span class="hljs-keyword">for</span> User {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_ref</span></span>(&<span class="hljs-keyword">self</span>) -> &<span class="hljs-built_in">String</span> {、<br> <span class="hljs-comment">//我们返回 name 还是 email? </span><br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">AsRef</span><<span class="hljs-built_in">u32</span>> <span class="hljs-keyword">for</span> User {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_ref</span></span>(&<span class="hljs-keyword">self</span>) -> &<span class="hljs-built_in">u32</span> {<br> <span class="hljs-comment">//我们返回 age 还是 height?</span><br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>一个正确的例子</p><p> Moderator 是一个User,User可以做的事情,Moderator也应该可以做</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span></span> {<br> name: <span class="hljs-built_in">String</span>,<br> age: <span class="hljs-built_in">u32</span>,<br>}<br><br><span class="hljs-comment">//不幸地是,标准库并没有提供一个generic blanket impl来避免这种重复的实现</span><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">AsRef</span><User> <span class="hljs-keyword">for</span> User {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_ref</span></span>(&<span class="hljs-keyword">self</span>) -> &User {<br> <span class="hljs-keyword">self</span><br> }<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Privilege</span></span> {<br> BanUsers,<br> EditPosts,<br> DeletePosts,<br>}<br><br><span class="hljs-comment">//尽管 Moderators 有一些特殊权限,它们仍然是普通的 User </span><br><span class="hljs-comment">//并且应该做相同的事情</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Moderator</span></span> {<br> user: User,<br> privileges: <span class="hljs-built_in">Vec</span><Privilege><br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">AsRef</span><Moderator> <span class="hljs-keyword">for</span> Moderator {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_ref</span></span>(&<span class="hljs-keyword">self</span>) -> &Moderator {<br> <span class="hljs-keyword">self</span><br> }<br>}<br><br><span class="hljs-keyword">impl</span> <span class="hljs-built_in">AsRef</span><User> <span class="hljs-keyword">for</span> Moderator {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">as_ref</span></span>(&<span class="hljs-keyword">self</span>) -> &User {<br> &<span class="hljs-keyword">self</span>.user<br> }<br>}<br><br><span class="hljs-comment">//使用 User 和 Moderators (也是一种User)应该都是可以调用的</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">create_post</span></span><U: <span class="hljs-built_in">AsRef</span><User>>(u: U) {<br> <span class="hljs-keyword">let</span> user = u.as_ref();<br> <span class="hljs-comment">// etc</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span>(user: User, moderator: Moderator) {<br> create_post(&user);<br> create_post(&moderator); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol></li></ol><h3 id="3-Borrow-x2F-BorrowMut"><a href="#3-Borrow-x2F-BorrowMut" class="headerlink" title="3. Borrow/BorrowMut"></a>3. Borrow/BorrowMut</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Borrow</span></span><Borrowed><br><span class="hljs-keyword">where</span><br> Borrowed: ?<span class="hljs-built_in">Sized</span>,<br>{<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">borrow</span></span>(&<span class="hljs-keyword">self</span>) -> &Borrowed;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">BorrowMut</span></span><Borrowed>: Borrow<Borrowed><br><span class="hljs-keyword">where</span><br> Borrowed: ?<span class="hljs-built_in">Sized</span>,<br>{<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">borrow_mut</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> &<span class="hljs-keyword">mut</span> Borrowed;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><ol><li>最初的作用:使用&str类型的值在HashSet、HashMap、BTreeSet和BTreeMap中查找String类型的 key。</li><li>我们可以把Borrow<T>和BorrowMut<T>看作更严格的AsRef<T>和AsMut<T>,它们返回的引用&T与Self有等价性的Eq、Hash和Ord实现。</li></ol></li><li><p>例子</p><p> borrow/borrow_mut 返回的引用 是满足Eq、Hash、Ord三个Trait的</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::borrow::Borrow;<br><span class="hljs-keyword">use</span> std::hash::Hasher;<br><span class="hljs-keyword">use</span> std::collections::hash_map::DefaultHasher;<br><span class="hljs-keyword">use</span> std::hash::Hash;<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">get_hash</span></span><T: Hash>(t: T) -> <span class="hljs-built_in">u64</span> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hasher = DefaultHasher::new();<br> t.hash(&<span class="hljs-keyword">mut</span> hasher);<br> hasher.finish()<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">asref_example</span></span><Owned, Ref>(owned1: Owned, owned2: Owned)<br><span class="hljs-keyword">where</span><br> Owned: <span class="hljs-built_in">Eq</span> + <span class="hljs-built_in">Ord</span> + Hash + <span class="hljs-built_in">AsRef</span><Ref>,<br> Ref: <span class="hljs-built_in">Eq</span> + <span class="hljs-built_in">Ord</span> + Hash<br>{<br> <span class="hljs-keyword">let</span> ref1: &Ref = owned1.as_ref();<br> <span class="hljs-keyword">let</span> ref2: &Ref = owned2.as_ref();<br><br> <span class="hljs-comment">// refs aren't required to be equal if owned types are equal</span><br> <span class="hljs-built_in">assert_eq!</span>(owned1 == owned2, ref1 == ref2); <span class="hljs-comment">// ❌</span><br><br> <span class="hljs-keyword">let</span> owned1_hash = get_hash(&owned1);<br> <span class="hljs-keyword">let</span> owned2_hash = get_hash(&owned2);<br> <span class="hljs-keyword">let</span> ref1_hash = get_hash(&ref1);<br> <span class="hljs-keyword">let</span> ref2_hash = get_hash(&ref2);<br><br> <span class="hljs-comment">// ref hashes aren't required to be equal if owned type hashes are equal</span><br> <span class="hljs-built_in">assert_eq!</span>(owned1_hash == owned2_hash, ref1_hash == ref2_hash); <span class="hljs-comment">// ❌</span><br><br> <span class="hljs-comment">// ref comparisons aren't required to match owned type comparisons</span><br> <span class="hljs-built_in">assert_eq!</span>(owned1.cmp(&owned2), ref1.cmp(&ref2)); <span class="hljs-comment">// ❌</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">borrow_example</span></span><Owned, Borrowed>(owned1: Owned, owned2: Owned)<br><span class="hljs-keyword">where</span><br> Owned: <span class="hljs-built_in">Eq</span> + <span class="hljs-built_in">Ord</span> + Hash + Borrow<Borrowed>,<br> Borrowed: <span class="hljs-built_in">Eq</span> + <span class="hljs-built_in">Ord</span> + Hash<br>{<br> <span class="hljs-keyword">let</span> borrow1: &Borrowed = owned1.borrow();<br> <span class="hljs-keyword">let</span> borrow2: &Borrowed = owned2.borrow();<br><br> <span class="hljs-comment">// borrows are required to be equal if owned types are equal</span><br> <span class="hljs-built_in">assert_eq!</span>(owned1 == owned2, borrow1 == borrow2); <span class="hljs-comment">// ✅</span><br><br> <span class="hljs-keyword">let</span> owned1_hash = get_hash(&owned1);<br> <span class="hljs-keyword">let</span> owned2_hash = get_hash(&owned2);<br> <span class="hljs-keyword">let</span> borrow1_hash = get_hash(&borrow1);<br> <span class="hljs-keyword">let</span> borrow2_hash = get_hash(&borrow2);<br><br> <span class="hljs-comment">// borrow hashes are required to be equal if owned type hashes are equal</span><br> <span class="hljs-built_in">assert_eq!</span>(owned1_hash == owned2_hash, borrow1_hash == borrow2_hash); <span class="hljs-comment">// ✅</span><br><br> <span class="hljs-comment">// borrow comparisons are required to match owned type comparisons</span><br> <span class="hljs-built_in">assert_eq!</span>(owned1.cmp(&owned2), borrow1.cmp(&borrow2)); <span class="hljs-comment">// ✅</span><br>}<br></code></pre></td></tr></table></figure></li><li><p>特点</p><ol><li>这个Trait是为一个类型的引用类型准备的,很少由程序员手动实现引用类型的Trait</li><li><code>T:Borrorw<T></code>已经为所有的类型T实现了,所以我们不需要手动地实现它;并且我们不需要创建一个U以用来<code>T:Borrow<U></code></li></ol></li></ol><h3 id="4-ToOwned"><a href="#4-ToOwned" class="headerlink" title="4. ToOwned"></a>4. ToOwned</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">ToOwned</span></span> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Owned</span></span>: Borrow<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">to_owned</span></span>(&<span class="hljs-keyword">self</span>) -> Self::Owned;<br><br> <span class="hljs-comment">// 提供默认实现</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">clone_into</span></span>(&<span class="hljs-keyword">self</span>, target: &<span class="hljs-keyword">mut</span> Self::Owned);<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><ol><li>ToOwned是Clone的一个更为通用的版本。Clone允许我们获取一个&T并把它转为一个T,但是ToOwned允许我们拿到一个&Borrowed并把它转为一个Owned,其中Owned: Borrow<Borrowed>。<ol><li>换句话说,我们不能从一个&str克隆一个String,或者从一个&Path克隆一个PathBuf,或者从一个&OsStr克隆一个OsString,因为clone方法签名不支持这种跨类型的克隆,这就是ToOwned产生的原因。</li></ol></li></ol></li></ol><h2 id="八、Iteration-Traits"><a href="#八、Iteration-Traits" class="headerlink" title="八、Iteration Traits"></a>八、<strong><strong>Iteration Traits</strong></strong></h2><h3 id="1-Iterator"><a href="#1-Iterator" class="headerlink" title="1. Iterator"></a>1. Iterator</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Iterator</span></span> {<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Item</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">next</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><Self::Item>;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">size_hint</span></span>(&<span class="hljs-keyword">self</span>) -> (<span class="hljs-built_in">usize</span>, <span class="hljs-built_in">Option</span><<span class="hljs-built_in">usize</span>>);<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">count</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">usize</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">last</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">advance_by</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, n: <span class="hljs-built_in">usize</span>) -> <span class="hljs-built_in">Result</span><(), <span class="hljs-built_in">usize</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">nth</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, n: <span class="hljs-built_in">usize</span>) -> <span class="hljs-built_in">Option</span><Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">step_by</span></span>(<span class="hljs-keyword">self</span>, step: <span class="hljs-built_in">usize</span>) -> StepBy<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">chain</span></span><U>(<br> <span class="hljs-keyword">self</span>, <br> other: U<br> ) -> Chain<<span class="hljs-keyword">Self</span>, <U <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::IntoIter><br> <span class="hljs-keyword">where</span><br> U: <span class="hljs-built_in">IntoIterator</span><Item = Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">zip</span></span><U>(<span class="hljs-keyword">self</span>, other: U) -> Zip<<span class="hljs-keyword">Self</span>, <U <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::IntoIter><br> <span class="hljs-keyword">where</span><br> U: <span class="hljs-built_in">IntoIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">map</span></span><B, F>(<span class="hljs-keyword">self</span>, f: F) -> Map<<span class="hljs-keyword">Self</span>, F><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> B;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">for_each</span></span><F>(<span class="hljs-keyword">self</span>, f: F)<br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item);<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">filter</span></span><P>(<span class="hljs-keyword">self</span>, predicate: P) -> Filter<<span class="hljs-keyword">Self</span>, P><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">filter_map</span></span><B, F>(<span class="hljs-keyword">self</span>, f: F) -> FilterMap<<span class="hljs-keyword">Self</span>, F><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">Option</span><B>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">enumerate</span></span>(<span class="hljs-keyword">self</span>) -> Enumerate<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">peekable</span></span>(<span class="hljs-keyword">self</span>) -> Peekable<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">skip_while</span></span><P>(<span class="hljs-keyword">self</span>, predicate: P) -> SkipWhile<<span class="hljs-keyword">Self</span>, P><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">take_while</span></span><P>(<span class="hljs-keyword">self</span>, predicate: P) -> TakeWhile<<span class="hljs-keyword">Self</span>, P><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">map_while</span></span><B, P>(<span class="hljs-keyword">self</span>, predicate: P) -> MapWhile<<span class="hljs-keyword">Self</span>, P><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">Option</span><B>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">skip</span></span>(<span class="hljs-keyword">self</span>, n: <span class="hljs-built_in">usize</span>) -> Skip<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">take</span></span>(<span class="hljs-keyword">self</span>, n: <span class="hljs-built_in">usize</span>) -> Take<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">scan</span></span><St, B, F>(<span class="hljs-keyword">self</span>, initial_state: St, f: F) -> Scan<<span class="hljs-keyword">Self</span>, St, F><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&<span class="hljs-keyword">mut</span> St, Self::Item) -> <span class="hljs-built_in">Option</span><B>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">flat_map</span></span><U, F>(<span class="hljs-keyword">self</span>, f: F) -> FlatMap<<span class="hljs-keyword">Self</span>, U, F><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> U,<br> U: <span class="hljs-built_in">IntoIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">flatten</span></span>(<span class="hljs-keyword">self</span>) -> Flatten<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> Self::Item: <span class="hljs-built_in">IntoIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fuse</span></span>(<span class="hljs-keyword">self</span>) -> Fuse<<span class="hljs-keyword">Self</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">inspect</span></span><F>(<span class="hljs-keyword">self</span>, f: F) -> Inspect<<span class="hljs-keyword">Self</span>, F><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item);<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">by_ref</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> &<span class="hljs-keyword">mut</span> <span class="hljs-keyword">Self</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">collect</span></span><B>(<span class="hljs-keyword">self</span>) -> B<br> <span class="hljs-keyword">where</span><br> B: FromIterator<Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">partition</span></span><B, F>(<span class="hljs-keyword">self</span>, f: F) -> (B, B)<br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> <span class="hljs-built_in">bool</span>,<br> B: <span class="hljs-built_in">Default</span> + <span class="hljs-built_in">Extend</span><Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">partition_in_place</span></span><<span class="hljs-symbol">'a</span>, T, P>(<span class="hljs-keyword">self</span>, predicate: P) -> <span class="hljs-built_in">usize</span><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">DoubleEndedIterator</span><Item = &<span class="hljs-symbol">'a</span> <span class="hljs-keyword">mut</span> T>,<br> T: <span class="hljs-symbol">'a</span>,<br> P: <span class="hljs-built_in">FnMut</span>(&T) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_partitioned</span></span><P>(<span class="hljs-keyword">self</span>, predicate: P) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_fold</span></span><B, F, R>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, init: B, f: F) -> R<br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(B, Self::Item) -> R,<br> R: Try<<span class="hljs-literal">Ok</span> = B>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_for_each</span></span><F, R>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, f: F) -> R<br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> R,<br> R: Try<<span class="hljs-literal">Ok</span> = ()>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fold</span></span><B, F>(<span class="hljs-keyword">self</span>, init: B, f: F) -> B<br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(B, Self::Item) -> B;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fold_first</span></span><F>(<span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item, Self::Item) -> Self::Item;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">all</span></span><F>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">any</span></span><F>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">find</span></span><P>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, predicate: P) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">find_map</span></span><B, F>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">Option</span><B><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">Option</span><B>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_find</span></span><F, R>(<br> &<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, <br> f: F<br> ) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">Option</span><Self::Item>, <R <span class="hljs-keyword">as</span> Try>::Error><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> R,<br> R: Try<<span class="hljs-literal">Ok</span> = <span class="hljs-built_in">bool</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">position</span></span><P>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, predicate: P) -> <span class="hljs-built_in">Option</span><<span class="hljs-built_in">usize</span>><br> <span class="hljs-keyword">where</span><br> P: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">rposition</span></span><P>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, predicate: P) -> <span class="hljs-built_in">Option</span><<span class="hljs-built_in">usize</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">ExactSizeIterator</span> + <span class="hljs-built_in">DoubleEndedIterator</span>,<br> P: <span class="hljs-built_in">FnMut</span>(Self::Item) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">max</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> Self::Item: <span class="hljs-built_in">Ord</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">min</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> Self::Item: <span class="hljs-built_in">Ord</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">max_by_key</span></span><B, F>(<span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> B,<br> B: <span class="hljs-built_in">Ord</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">max_by</span></span><F>(<span class="hljs-keyword">self</span>, compare: F) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item, &Self::Item) -> Ordering;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">min_by_key</span></span><B, F>(<span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item) -> B,<br> B: <span class="hljs-built_in">Ord</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">min_by</span></span><F>(<span class="hljs-keyword">self</span>, compare: F) -> <span class="hljs-built_in">Option</span><Self::Item><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item, &Self::Item) -> Ordering;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">rev</span></span>(<span class="hljs-keyword">self</span>) -> Rev<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">DoubleEndedIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">unzip</span></span><A, B, FromA, FromB>(<span class="hljs-keyword">self</span>) -> (FromA, FromB)<br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Iterator</span><Item = (A, B)>,<br> FromA: <span class="hljs-built_in">Default</span> + <span class="hljs-built_in">Extend</span><A>,<br> FromB: <span class="hljs-built_in">Default</span> + <span class="hljs-built_in">Extend</span><B>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">copied</span></span><<span class="hljs-symbol">'a</span>, T>(<span class="hljs-keyword">self</span>) -> Copied<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Iterator</span><Item = &<span class="hljs-symbol">'a</span> T>,<br> T: <span class="hljs-symbol">'a</span> + <span class="hljs-built_in">Copy</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cloned</span></span><<span class="hljs-symbol">'a</span>, T>(<span class="hljs-keyword">self</span>) -> Cloned<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Iterator</span><Item = &<span class="hljs-symbol">'a</span> T>,<br> T: <span class="hljs-symbol">'a</span> + <span class="hljs-built_in">Clone</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cycle</span></span>(<span class="hljs-keyword">self</span>) -> Cycle<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Clone</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">sum</span></span><S>(<span class="hljs-keyword">self</span>) -> S<br> <span class="hljs-keyword">where</span><br> S: Sum<Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">product</span></span><P>(<span class="hljs-keyword">self</span>) -> P<br> <span class="hljs-keyword">where</span><br> P: Product<Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cmp</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> Ordering<br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span><Item = Self::Item>,<br> Self::Item: <span class="hljs-built_in">Ord</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cmp_by</span></span><I, F>(<span class="hljs-keyword">self</span>, other: I, cmp: F) -> Ordering<br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item, <I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item) -> Ordering,<br> I: <span class="hljs-built_in">IntoIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">partial_cmp</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">Option</span><Ordering><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialOrd</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">partial_cmp_by</span></span><I, F>(<br> <span class="hljs-keyword">self</span>, <br> other: I, <br> partial_cmp: F<br> ) -> <span class="hljs-built_in">Option</span><Ordering><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item, <I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item) -> <span class="hljs-built_in">Option</span><Ordering>,<br> I: <span class="hljs-built_in">IntoIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">eq</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialEq</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">eq_by</span></span><I, F>(<span class="hljs-keyword">self</span>, other: I, eq: F) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item, <I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item) -> <span class="hljs-built_in">bool</span>,<br> I: <span class="hljs-built_in">IntoIterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ne</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialEq</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">lt</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialOrd</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">le</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialOrd</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">gt</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialOrd</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ge</span></span><I>(<span class="hljs-keyword">self</span>, other: I) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> I: <span class="hljs-built_in">IntoIterator</span>,<br> Self::Item: <span class="hljs-built_in">PartialOrd</span><<I <span class="hljs-keyword">as</span> <span class="hljs-built_in">IntoIterator</span>>::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_sorted</span></span>(<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> Self::Item: <span class="hljs-built_in">PartialOrd</span><Self::Item>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_sorted_by</span></span><F>(<span class="hljs-keyword">self</span>, compare: F) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(&Self::Item, &Self::Item) -> <span class="hljs-built_in">Option</span><Ordering>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_sorted_by_key</span></span><F, K>(<span class="hljs-keyword">self</span>, f: F) -> <span class="hljs-built_in">bool</span><br> <span class="hljs-keyword">where</span><br> F: <span class="hljs-built_in">FnMut</span>(Self::Item) -> K,<br> K: <span class="hljs-built_in">PartialOrd</span><K>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>实现</p><ol><li><p>Iterator<Item = T>类型可以被迭代并产生T类型。没有IteratorMut trait。每个Iterator实现可以指定它返回的是不可变引用、可变引用还是拥有通过Item关联类型的值。</p><table><thead><tr><th>Vec<T> method</th><th>Returns</th></tr></thead><tbody><tr><td>.iter()</td><td>Iterator<Item = &T></td></tr><tr><td>.iter_mut()</td><td>Iterator<Item = &mut T></td></tr><tr><td>.into_iter()</td><td>Iterator<Item = T></td></tr><tr><td></td><td></td></tr></tbody></table></li><li><p>大多数类型没有它们自己的迭代器。如果一个类型是可迭代的,我们几乎总是实现自定义的迭代器类型来迭代它,而不是让它自己迭代。</p><p> 可以使用Vec 的iter方法对自己的类型迭代</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">MyType</span></span> {<br> items: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">String</span>><br>}<br><br><span class="hljs-keyword">impl</span> MyType {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">iter</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-keyword">impl</span> <span class="hljs-built_in">Iterator</span><Item = &<span class="hljs-built_in">String</span>> {<br> <span class="hljs-keyword">self</span>.items.iter()<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>It says that any mutable reference to an iterator is also an iterator. This is useful to know because it allows us to use iterator methods with self receivers as if they had &mut self receivers.</p><ol><li>这里是说Iterator定义的方法,接受self的方法和接受&mut self的方法是一样的</li></ol></li><li><p>如果我们想对一个超过三项的迭代器进行处理的函数,函数要先消耗三个元素,再进行处理</p><ol><li><p>这是一个错误范例</p><p> take方法接受self对象,换句话说,它消耗了iter这个实例,接下来再调用iter会报错</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span><I: <span class="hljs-built_in">Iterator</span><Item = <span class="hljs-built_in">i32</span>>>(<span class="hljs-keyword">mut</span> iter: I) {<br> <span class="hljs-keyword">let</span> first3: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">i32</span>> = iter.take(<span class="hljs-number">3</span>).collect();<br> <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> iter { <span class="hljs-comment">// ❌ iter consumed in line above</span><br> <span class="hljs-comment">// process remaining items</span><br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>下面两种方法可以达到目的</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span><I: <span class="hljs-built_in">Iterator</span><Item = <span class="hljs-built_in">i32</span>>>(<span class="hljs-keyword">mut</span> iter: I) {<br> <span class="hljs-keyword">let</span> first3: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">i32</span>> = <span class="hljs-built_in">vec!</span>[<br> iter.next().unwrap(),<br> iter.next().unwrap(),<br> iter.next().unwrap(),<br> ];<br> <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> iter { <span class="hljs-comment">// ✅</span><br> <span class="hljs-comment">// process remaining items</span><br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span><I: <span class="hljs-built_in">Iterator</span><Item = <span class="hljs-built_in">i32</span>>>(<span class="hljs-keyword">mut</span> iter: I) {<br> <span class="hljs-keyword">let</span> first3: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">i32</span>> = iter.by_ref().take(<span class="hljs-number">3</span>).collect();<br> <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> iter { <span class="hljs-comment">// ✅</span><br> <span class="hljs-comment">// process remaining items</span><br> }<br>}<br></code></pre></td></tr></table></figure></li></ol></li></ol></li></ol><h3 id="2-IntoIter"><a href="#2-IntoIter" class="headerlink" title="2. IntoIter"></a>2. IntoIter</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">IntoIterator</span></span> <br><span class="hljs-keyword">where</span><br> <Self::IntoIter <span class="hljs-keyword">as</span> <span class="hljs-built_in">Iterator</span>>::Item == Self::Item, <br>{<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Item</span></span>;<br> <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">IntoIter</span></span>: <span class="hljs-built_in">Iterator</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">into_iter</span></span>(<span class="hljs-keyword">self</span>) -> Self::IntoIter;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 让类型T转换为迭代器类型,对于不可变引用、可变引用也如此</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-comment">// vec = Vec<T></span><br><span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> vec {} <span class="hljs-comment">// v = T</span><br><br><span class="hljs-comment">// above line desugared</span><br><span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> vec.into_iter() {}<br><br><span class="hljs-comment">// vec = Vec<T></span><br><span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> &vec {} <span class="hljs-comment">// v = &T</span><br><br><span class="hljs-comment">// above example desugared</span><br><span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> (&vec).into_iter() {}<br><br><span class="hljs-comment">// vec = Vec<T></span><br><span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> &<span class="hljs-keyword">mut</span> vec {} <span class="hljs-comment">// v = &mut T</span><br><br><span class="hljs-comment">// above example desugared</span><br><span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> (&<span class="hljs-keyword">mut</span> vec).into_iter() {}<br></code></pre></td></tr></table></figure></li></ol><h3 id="3-FromIterator"><a href="#3-FromIterator" class="headerlink" title="3. FromIterator"></a>3. FromIterator</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">FromIterator</span></span><A> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_iter</span></span><T>(iter: T) -> <span class="hljs-keyword">Self</span><br> <span class="hljs-keyword">where</span><br> T: <span class="hljs-built_in">IntoIterator</span><Item = A>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p> 从迭代器类型转换为类型T</p><p> 比如collect方法</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">collect</span></span><B>(<span class="hljs-keyword">self</span>) -> B<br><span class="hljs-keyword">where</span><br> B: FromIterator<Self::Item>;<br></code></pre></td></tr></table></figure></li><li><p>例子</p><p> collect 方法“收集”一个Iterator<Item = char> 到 String:</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">filter_letters</span></span>(string: &<span class="hljs-built_in">str</span>) -> <span class="hljs-built_in">String</span> {<br> string.chars().filter(|c| c.is_alphabetic()).collect()<br>}<br></code></pre></td></tr></table></figure></li></ol><h3 id="4-标准库类型靠迭代器相互转换的例子"><a href="#4-标准库类型靠迭代器相互转换的例子" class="headerlink" title="4. 标准库类型靠迭代器相互转换的例子"></a>4. 标准库类型靠迭代器相互转换的例子</h3><figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::collections::{BTreeSet, HashMap, HashSet, LinkedList};<br><br><span class="hljs-comment">// String -> HashSet<char></span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">unique_chars</span></span>(string: &<span class="hljs-built_in">str</span>) -> HashSet<<span class="hljs-built_in">char</span>> {<br> string.chars().collect()<br>}<br><br><span class="hljs-comment">// Vec<T> -> BTreeSet<T></span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">ordered_unique_items</span></span><T: <span class="hljs-built_in">Ord</span>>(vec: <span class="hljs-built_in">Vec</span><T>) -> BTreeSet<T> {<br> vec.into_iter().collect()<br>}<br><br><span class="hljs-comment">// HashMap<K, V> -> LinkedList<(K, V)></span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">entry_list</span></span><K, V>(map: HashMap<K, V>) -> LinkedList<(K, V)> {<br> map.into_iter().collect()<br>}<br><br><span class="hljs-comment">// and countless more possible examples</span><br></code></pre></td></tr></table></figure><h2 id="九、IO-Trait"><a href="#九、IO-Trait" class="headerlink" title="九、IO Trait"></a>九、IO Trait</h2><h3 id="1-Read-x2F-Write"><a href="#1-Read-x2F-Write" class="headerlink" title="1. Read/Write"></a>1. Read/Write</h3><ol><li><p>定义</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Read</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &<span class="hljs-keyword">mut</span> [<span class="hljs-built_in">u8</span>]) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">usize</span>>;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_vectored</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, bufs: &<span class="hljs-keyword">mut</span> [IoSliceMut<<span class="hljs-symbol">'_</span>>]) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">usize</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_read_vectored</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-keyword">unsafe</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">initializer</span></span>(&<span class="hljs-keyword">self</span>) -> Initializer;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_to_end</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &<span class="hljs-keyword">mut</span> <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">u8</span>>) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">usize</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_to_string</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &<span class="hljs-keyword">mut</span> <span class="hljs-built_in">String</span>) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">usize</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_exact</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &<span class="hljs-keyword">mut</span> [<span class="hljs-built_in">u8</span>]) -> <span class="hljs-built_in">Result</span><()>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">by_ref</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> &<span class="hljs-keyword">mut</span> <span class="hljs-keyword">Self</span><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">bytes</span></span>(<span class="hljs-keyword">self</span>) -> Bytes<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">chain</span></span><R: Read>(<span class="hljs-keyword">self</span>, next: R) -> Chain<<span class="hljs-keyword">Self</span>, R><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">take</span></span>(<span class="hljs-keyword">self</span>, limit: <span class="hljs-built_in">u64</span>) -> Take<<span class="hljs-keyword">Self</span>><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>;<br>}<br><br><span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Write</span></span> {<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &[<span class="hljs-built_in">u8</span>]) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">usize</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">flush</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">Result</span><()>;<br><br> <span class="hljs-comment">// provided default impls</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write_vectored</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, bufs: &[IoSlice<<span class="hljs-symbol">'_</span>>]) -> <span class="hljs-built_in">Result</span><<span class="hljs-built_in">usize</span>>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">is_write_vectored</span></span>(&<span class="hljs-keyword">self</span>) -> <span class="hljs-built_in">bool</span>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write_all</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, buf: &[<span class="hljs-built_in">u8</span>]) -> <span class="hljs-built_in">Result</span><()>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write_all_vectored</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, bufs: &<span class="hljs-keyword">mut</span> [IoSlice<<span class="hljs-symbol">'_</span>>]) -> <span class="hljs-built_in">Result</span><()>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">write_fmt</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, fmt: Arguments<<span class="hljs-symbol">'_</span>>) -> <span class="hljs-built_in">Result</span><()>;<br> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">by_ref</span></span>(&<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -> &<span class="hljs-keyword">mut</span> <span class="hljs-keyword">Self</span><br> <span class="hljs-keyword">where</span><br> <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>实现</p><ol><li><p>标准库同样提供了泛型覆盖实现</p><p> Read类型的任何可变引用也都是Read,Write同理</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">impl</span><R: Read + ?<span class="hljs-built_in">Sized</span>> Read <span class="hljs-keyword">for</span> &<span class="hljs-keyword">mut</span> R;<br><span class="hljs-keyword">impl</span><W: Write + ?<span class="hljs-built_in">Sized</span>> Write <span class="hljs-keyword">for</span> &<span class="hljs-keyword">mut</span> W;<br></code></pre></td></tr></table></figure></li></ol></li><li><p>例子</p><p> &[u8] 实现了Read,Vec<u8>实现了Write</p><p> 因此我们可以对我们的文件处理函数进行简单的单元测试,通过使用String转换为&[u8]以及从Vec<u8> 转换为String:</p> <figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs rust"><span class="hljs-keyword">use</span> std::path::Path;<br><span class="hljs-keyword">use</span> std::fs::File;<br><span class="hljs-keyword">use</span> std::io::Read;<br><span class="hljs-keyword">use</span> std::io::Write;<br><span class="hljs-keyword">use</span> std::io;<br><br><span class="hljs-comment">// function we want to test</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">uppercase</span></span><R: Read, W: Write>(<span class="hljs-keyword">mut</span> read: R, <span class="hljs-keyword">mut</span> write: W) -> <span class="hljs-built_in">Result</span><(), io::Error> {<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buffer = <span class="hljs-built_in">String</span>::new();<br> read.read_to_string(&<span class="hljs-keyword">mut</span> buffer)?;<br> <span class="hljs-keyword">let</span> uppercase = buffer.to_uppercase();<br> write.write_all(uppercase.as_bytes())?;<br> write.flush()?;<br> <span class="hljs-literal">Ok</span>(())<br>}<br><br><span class="hljs-comment">// in actual program we'd pass Files</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example</span></span>(in_path: &Path, out_path: &Path) -> <span class="hljs-built_in">Result</span><(), io::Error> {<br> <span class="hljs-keyword">let</span> in_file = File::open(in_path)?;<br> <span class="hljs-keyword">let</span> out_file = File::open(out_path)?;<br> uppercase(in_file, out_file)<br>}<br><br><span class="hljs-comment">// however in unit tests we can use Strings!</span><br><span class="hljs-meta">#[test]</span> <span class="hljs-comment">// ✅</span><br><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">example_test</span></span>() {<br> <span class="hljs-keyword">let</span> in_file: <span class="hljs-built_in">String</span> = <span class="hljs-string">"i am screaming"</span>.into();<br> <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> out_file: <span class="hljs-built_in">Vec</span><<span class="hljs-built_in">u8</span>> = <span class="hljs-built_in">Vec</span>::new();<br> uppercase(in_file.as_bytes(), &<span class="hljs-keyword">mut</span> out_file).unwrap();<br> <span class="hljs-keyword">let</span> out_result = <span class="hljs-built_in">String</span>::from_utf8(out_file).unwrap();<br> <span class="hljs-built_in">assert_eq!</span>(out_result, <span class="hljs-string">"I AM SCREAMING"</span>);<br>}<br></code></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag>Rust</tag>
</tags>
</entry>
<entry>
<title>CS144-Lab4实验笔记</title>
<link href="/202205/cs144-lab4/"/>
<url>/202205/cs144-lab4/</url>
<content type="html"><![CDATA[<h2 id="实验简介"><a href="#实验简介" class="headerlink" title="实验简介"></a>实验简介</h2><p>这个实验主要完成TCP Endpoint的功能,一个TCP端点包含一个TCPSender,和一个TCPReceiver。进行TCP连接的时候,通常是一个TCP端点A向另一个TCP端点B发送“连接建立”请求,经过三次握手后TCP连接建立。之后两个端点间可以相互传送数据。每当一端要发送的字节流到达EOF的时候,向对端发送一个“连接关闭”请求,最后当两个端点均已经发送连接关闭的请求,并且都接到了这个请求的响应后,TCP连接就算关闭了。</p><h3 id="TCP状态机"><a href="#TCP状态机" class="headerlink" title="TCP状态机"></a>TCP状态机</h3><p>简介中简单描述了下TCP通信的流程,但具体的TCP状态转移需要考虑一些边界情况,可参考下图,<br><img src="/img/cs144-pic/TCP-FSM.png"></p><h3 id="TCP连接的建立-非同时握手"><a href="#TCP连接的建立-非同时握手" class="headerlink" title="TCP连接的建立(非同时握手)"></a>TCP连接的建立(非同时握手)</h3><p><img src="/img/cs144-pic/TCP-SYN.png"></p><h3 id="TCP连接的关闭-非同时关闭"><a href="#TCP连接的关闭-非同时关闭" class="headerlink" title="TCP连接的关闭(非同时关闭)"></a>TCP连接的关闭(非同时关闭)</h3><p><img src="/img/cs144-pic/TCP-FIN.png"></p><blockquote><p>以上图片均来源于 <a href="https://en.wikipedia.org/wiki/The_Linux_Programming_Interface">Linux/UNIX系统编程手册,人民邮电出版社</a></p></blockquote><h2 id="实验思路"><a href="#实验思路" class="headerlink" title="实验思路"></a>实验思路</h2><h3 id="接受报文"><a href="#接受报文" class="headerlink" title="接受报文"></a>接受报文</h3><p>TCP连接从网络上接受报文,并调用<code>segment_received</code>函数。当调用发生时,函数应该如下工作:</p><ol><li>如果收到了 <code>RST</code> 报文,则关闭这个连接,并将接受字节流和发送字节流均设为ERROR状态。否则:</li><li>将报文交给<code>TCPReceiver</code>来处理本报文的包头中的<code>seqno/SYN/FIN</code>和数据段</li><li>如果收到了<code>ACK</code>报文(也就是除了第一个SYN报文外的所有正常报文),通知<code>TCPSender</code>更新<code>ackno</code>和<code>win_size</code></li><li>如果当前报文包含数据或者SYN/FIN标志位有效,则至少响应一个报文,以通知对端本端最新的<code>ackno</code>和<code>win_size</code>。</li><li>如果收到了Keepalive报文,见下。</li></ol><h3 id="发送报文"><a href="#发送报文" class="headerlink" title="发送报文"></a>发送报文</h3><ol><li>每当<code>TCPSender</code>将一个TCP报文添加到它的待发送队列中时,TCP连接需要从中取出并将其发送。</li><li>在发送当前数据包之前,TCPConnection 会根据<code>TCPReceiver</code>的<code>ackno</code> 和 <code>win_size</code>,将其放置进待发送 TCPSegment 中,并设置报文的 <code>ACK</code>。</li></ol><h3 id="TCP-KeepAlives"><a href="#TCP-KeepAlives" class="headerlink" title="TCP KeepAlives"></a>TCP KeepAlives</h3><p>KeepAlive是一种周期性检查另一端主机的该TCP连接是否存活的机制。在<a href="https://datatracker.ietf.org/doc/html/rfc1122#page-101">RFC 1122</a> 中被描述。</p><p>KeepAlive报文是一个空的报文段(或者仅包含一个任意字节)。它的序列号等于对端已经ACK过的字节减1: <code>SEG.SEQ = SND.NXT-1</code>,这个序列号对应的数据显然被对方成功接受了,所以不会对对端的接受数据造成影响。如果接受方能正确处理这个KeepAlive报文,则同样返回一个空的<code>ACK</code>报文即可,表明当前TCP连接仍然存活。</p><p>KeepAlive报文和其响应报文都不会包含任何新的有效数据,丢失的时候也不会重传。RFC1122同样指出,仅凭一个没有收到响应的KeepAlive报文不能判断对端的TCP连接已经停止工作,所以失败时需要多次发送Keepalive报文。</p><p>讲义中给出了处理对端KeepAlive报文的伪算法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cpp"> <span class="hljs-keyword">if</span> (_receiver.<span class="hljs-built_in">ackno</span>().<span class="hljs-built_in">has_value</span>() <span class="hljs-built_in"><span class="hljs-keyword">and</span></span> (seg.<span class="hljs-built_in">length_in_sequence_space</span>() == <span class="hljs-number">0</span>)<br> <span class="hljs-keyword">and</span> seg.<span class="hljs-built_in">header</span>().seqno == _receiver.<span class="hljs-built_in">ackno</span>().<span class="hljs-built_in">value</span>() - <span class="hljs-number">1</span>) {<br> _sender.<span class="hljs-built_in">send_empty_segment</span>();<br>}<br></code></pre></td></tr></table></figure><h3 id="接受RST报文"><a href="#接受RST报文" class="headerlink" title="接受RST报文"></a>接受RST报文</h3><p>有几种情况本端会收到对端的RST报文</p><ul><li>本端向对端没有打开的端口发送了SYN报文,会收到RST报文。</li><li>TCP连接建立后,对端突然崩溃,本端再发送正常报文时,会收到RST报文。</li><li>TIME-WAIT状态下收到了之前的数据报文,本端正常回复<code>ACK</code>,但对端已经关闭了这个连接,所以会回复一个RST报文(这种情况会导致本端TIME-WAIT状态提前结束,称为<a href="https://www.rfc-editor.org/rfc/rfc1337.html">TIME-WAIT Assassination,在RFC1337中有所描述</a>)</li><li>对端主动发出RST报文</li></ul><p>本端收到了RST报文后,不需要任何回复,直接关闭这条TCP连接,并将发送/接受的数据流关闭。(实验中是将 <code>ByteStream inbound/outbound</code>设为 <code>ERROR</code>状态)</p><h3 id="发送RST报文"><a href="#发送RST报文" class="headerlink" title="发送RST报文"></a>发送RST报文</h3><p>上一节已经说明了什么情况下会发送RST报文,但还有以下情况使得一端会主动发出RST报文</p><ul><li>本端socket程序收到了程序终止的信号,如<code>SIGINT</code>,直接发送<code>RST</code>终止连接。</li><li>实验讲义中说明,如果当前重传次数超过上限,则需要<code>RST</code>报文终止连接。</li></ul><p><code>RST</code>报文的<code>seqno</code>部分必须是正确的(正确指的是在对端的接受窗口内,亦即<code>(RCV.NXT <= SEG.SEQ < RCV.NXT+RCV.WND)</code>,<a href="https://datatracker.ietf.org/doc/html/rfc5961#section-3">这样可以防止TCP重置攻击,见RFC5961</a></p><h2 id="调试心得"><a href="#调试心得" class="headerlink" title="调试心得"></a>调试心得</h2><ol><li><p>TCPConnection起始状态是<code>LISTEN</code>,<code>active</code>变量应该为<code>true</code></p><p>测试函数里面,FSM什么都不执行就是LISTEN态。</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// tests/tcp_expectation.hh</span><br><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Listen</span> :</span> <span class="hljs-keyword">public</span> TCPAction {<br> <span class="hljs-function">std::string <span class="hljs-title">description</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">"listen"</span>; }<br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(TCPTestHarness &)</span> <span class="hljs-keyword">const</span> </span>{}<br>};<br><br></code></pre></td></tr></table></figure><p> 我最开始的实现时候<code>_active</code>设为false,<code>t_ack_rst</code> 这个测试包含一个LISTEN态的比较,死活过不去。</p></li><li><p>同时打开</p><p>按照RFC 793的描述,同时打开的情况下,双方第二次握手均应该发送<code>SYN+ACK</code>包,但是测试逻辑这里却希望仅希望收到<code>ACK</code>包</p><figure class="highlight bnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs bnf"> TCP A TCP B<br><br>1. CLOSED CLOSED<br><br>2. SYN-SENT --> <span class="hljs-attribute"><SEQ=100></span><span class="hljs-attribute"><CTL=SYN></span> ...<br><br>3. SYN-RECEIVED <span class="hljs-attribute"><-- <SEQ=300></span><span class="hljs-attribute"><CTL=SYN></span> <span class="hljs-attribute"><-- SYN-SENT</span><br><span class="hljs-attribute"></span><br><span class="hljs-attribute">4. ... <SEQ=100></span><span class="hljs-attribute"><CTL=SYN></span> --> SYN-RECEIVED<br><br>5. SYN-RECEIVED --> <span class="hljs-attribute"><SEQ=100></span><span class="hljs-attribute"><ACK=301></span><span class="hljs-attribute"><CTL=SYN,ACK></span> ...<br><br>6. ESTABLISHED <span class="hljs-attribute"><-- <SEQ=300></span><span class="hljs-attribute"><ACK=101></span><span class="hljs-attribute"><CTL=SYN,ACK></span> <span class="hljs-attribute"><-- SYN-RECEIVED</span><br><span class="hljs-attribute"></span><br><span class="hljs-attribute">7. ... <SEQ=101></span><span class="hljs-attribute"><ACK=301></span><span class="hljs-attribute"><CTL=ACK></span> --> ESTABLISHED<br><br> Simultaneous Connection Synchronization<br><br> Figure 8.<br></code></pre></td></tr></table></figure><p>测试函数</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">TCPTestHarness <span class="hljs-title">test_2</span><span class="hljs-params">(cfg)</span></span>;<br><br>test_2.<span class="hljs-built_in">execute</span>(Connect{});<br>test_2.<span class="hljs-built_in">execute</span>(<span class="hljs-built_in">Tick</span>(<span class="hljs-number">1</span>));<br><br>TCPSegment seg = test_2.<span class="hljs-built_in">expect_seg</span>(ExpectOneSegment{}.<span class="hljs-built_in">with_syn</span>(<span class="hljs-literal">true</span>).<span class="hljs-built_in">with_ack</span>(<span class="hljs-literal">false</span>),<br> <span class="hljs-string">"test 2 failed: could not parse SYN segment or invalid flags"</span>);<br><span class="hljs-keyword">auto</span> &seg_hdr = seg.<span class="hljs-built_in">header</span>();<br><br>test_2.<span class="hljs-built_in">execute</span>(ExpectState{State::SYN_SENT});<br><br><span class="hljs-comment">// send SYN (no ACK yet)</span><br><span class="hljs-function"><span class="hljs-keyword">const</span> WrappingInt32 <span class="hljs-title">isn</span><span class="hljs-params">(rd())</span></span>;<br>test_2.<span class="hljs-built_in">send_syn</span>(isn);<br>test_2.<span class="hljs-built_in">execute</span>(<span class="hljs-built_in">Tick</span>(<span class="hljs-number">1</span>));<br><br>test_2.<span class="hljs-built_in">expect_seg</span>(ExpectOneSegment{}.<span class="hljs-built_in">with_syn</span>(<span class="hljs-literal">false</span>).<span class="hljs-built_in">with_ack</span>(<span class="hljs-literal">true</span>).<span class="hljs-built_in">with_ackno</span>(isn + <span class="hljs-number">1</span>),<br> <span class="hljs-string">"test 2 failed: bad ACK for SYN"</span>);<br><br>test_2.<span class="hljs-built_in">execute</span>(ExpectState{State::SYN_RCVD});<br></code></pre></td></tr></table></figure></li><li><p>调试tun144/145</p></li></ol><p>本地tun144/tun145两张网卡无法通信。tun145发的包tun144收不到</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ ip a<br>...<br>49: tun144: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 500<br> link/none <br> inet 169.254.144.1/24 scope global tun144<br> valid_lft forever preferred_lft forever<br> inet6 fe80::40ad:1e69:28e2:988c/64 scope link stable-privacy <br> valid_lft forever preferred_lft forever<br>50: tun145: <NO-CARRIER,POINTOPOINT,MULTICAST,NOARP,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 500<br> link/none <br> inet 169.254.145.1/24 scope global tun145<br> valid_lft forever preferred_lft forever<br> inet6 fe80::8e6c:7fed:e456:8f35/64 scope link stable-privacy <br> valid_lft forever preferred_lft forever<br></code></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ ip r<br>default via 10.19.0.254 dev eno1 proto static metric 100 <br>default via 10.30.0.254 dev eno3 proto static metric 20102 <br>10.19.0.0/24 dev eno1 proto kernel scope link src 10.19.0.36 metric 100 <br>10.30.0.0/19 dev eno3 proto kernel scope link src 10.30.19.36 metric 102 <br>169.254.0.0/16 dev tun144 scope link metric 1000 linkdown <br>169.254.144.0/24 dev tun144 scope link linkdown rto_min lock 10ms <br>169.254.145.0/24 dev tun145 scope link linkdown rto_min lock 10ms <br>172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 <br></code></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ sudo iptables -L -n -t nat <br>Chain PREROUTING (policy ACCEPT)<br>target prot opt <span class="hljs-built_in">source</span> destination <br>DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL<br>CONNMARK all -- 169.254.144.0/24 0.0.0.0/0 CONNMARK <span class="hljs-built_in">set</span> 0x90<br>CONNMARK all -- 169.254.145.0/24 0.0.0.0/0 CONNMARK <span class="hljs-built_in">set</span> 0x91<br><br>Chain INPUT (policy ACCEPT)<br>target prot opt <span class="hljs-built_in">source</span> destination <br><br>Chain OUTPUT (policy ACCEPT)<br>target prot opt <span class="hljs-built_in">source</span> destination <br>DOCKER all -- 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL<br><br>Chain POSTROUTING (policy ACCEPT)<br>target prot opt <span class="hljs-built_in">source</span> destination <br>MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 <br>MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:9001<br>MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:9000<br>MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 connmark match 0x90<br>MASQUERADE all -- 0.0.0.0/0 0.0.0.0/0 connmark match 0x91<br><br>Chain DOCKER (2 references)<br>target prot opt <span class="hljs-built_in">source</span> destination <br>RETURN all -- 0.0.0.0/0 0.0.0.0/0 <br>DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9001 to:172.17.0.2:9001<br>DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9000 to:172.17.0.2:9000<br></code></pre></td></tr></table></figure><ol start="4"><li>Debug打印</li></ol><p>可以加上TCPConnection状态打印函数,为了避免影响性能,存在DEBUG宏的时候编译,或者传给gcc<code>-DDEBUG</code>参数</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs cpp"> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TCPConnectionDebugger</span> {</span><br> <span class="hljs-keyword">private</span>:<br> <span class="hljs-keyword">bool</span> open_debugger{<span class="hljs-literal">true</span>};<br><br> <span class="hljs-keyword">public</span>:<br> <span class="hljs-built_in">TCPConnectionDebugger</span>() : <span class="hljs-built_in">open_debugger</span>(<span class="hljs-literal">true</span>) {}<br> ~<span class="hljs-built_in">TCPConnectionDebugger</span>() {}<br><br> <span class="hljs-function">std::string <span class="hljs-title">color_1</span><span class="hljs-params">(<span class="hljs-keyword">const</span> std::string &data)</span> </span>{ <span class="hljs-keyword">return</span> data; }<br><br> <span class="hljs-function">std::string <span class="hljs-title">color_2</span><span class="hljs-params">(<span class="hljs-keyword">const</span> std::string &data)</span> </span>{ <span class="hljs-keyword">return</span> data; }<br><br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">print_segment</span><span class="hljs-params">(<span class="hljs-keyword">const</span> TCPConnection &that,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> TCPSegment &seg,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> std::string &desription,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">bool</span> check = <span class="hljs-literal">true</span>)</span> </span>{<br> <span class="hljs-built_in">DUMMY_CODE</span>(that, seg, desription, check);<br><span class="hljs-meta">#<span class="hljs-meta-keyword">ifdef</span> DEBUG</span><br> std::cerr << <span class="hljs-string">"\n"</span> << <span class="hljs-built_in">color_1</span>(desription) << <span class="hljs-string">"\n"</span>;<br> std::cerr << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Flag"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">header</span>().syn ? <span class="hljs-string">"S"</span> : <span class="hljs-string">""</span>) << (seg.<span class="hljs-built_in">header</span>().fin ? <span class="hljs-string">"F"</span> : <span class="hljs-string">""</span>)<br> << (seg.<span class="hljs-built_in">header</span>().ack ? <span class="hljs-string">"A"</span> : <span class="hljs-string">""</span>) << (seg.<span class="hljs-built_in">header</span>().rst ? <span class="hljs-string">"R"</span> : <span class="hljs-string">""</span>) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Sequnce Number"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">header</span>().seqno.<span class="hljs-built_in">raw_value</span>()) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Acknowledgement Number"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">header</span>().ackno) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Window Size"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">header</span>().win) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Payload"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">payload</span>().<span class="hljs-built_in">size</span>() ? seg.<span class="hljs-built_in">payload</span>().<span class="hljs-built_in">str</span>() : <span class="hljs-string">"empty string"</span>)<br> << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Payload Size"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">payload</span>().<span class="hljs-built_in">size</span>()) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Sequnce Space"</span>) + <span class="hljs-string">" : "</span>) << (seg.<span class="hljs-built_in">length_in_sequence_space</span>()) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"ackno of sender"</span>) + <span class="hljs-string">" : "</span>) << (that._sender.<span class="hljs-built_in">next_seqno_absolute</span>()) << <span class="hljs-string">"\n"</span><br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"next seqno absolute of sender"</span>) + <span class="hljs-string">" : "</span>) << (that._sender.<span class="hljs-built_in">next_seqno_absolute</span>())<br> << <span class="hljs-string">"\n"</span>;<br> std::cerr << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Active: "</span>) + ((that._active) ? <span class="hljs-string">"Y "</span> : <span class="hljs-string">"N"</span>))<br> << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">" TIME_WAIT: "</span>) + ((that._linger_after_streams_finish) ? <span class="hljs-string">"Y\n"</span> : <span class="hljs-string">"N\n"</span>));<br> std::cerr << (<span class="hljs-built_in">color_2</span>(<span class="hljs-string">"Sender State: "</span>)) << that.<span class="hljs-built_in">state</span>().<span class="hljs-built_in">name</span>() << std::endl;<br><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span><br> }<br> };<br><br></code></pre></td></tr></table></figure><h2 id="性能调优"><a href="#性能调优" class="headerlink" title="性能调优"></a>性能调优</h2><ol><li>第一次实现的性能<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ ./apps/tcp_benchmark<br>CPU-limited throughput : 2.65 Gbit/s<br>CPU-limited throughput with reordering: 1.45 Gbit/s<br></code></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag>CS144</tag>
<tag>TCP/IP</tag>
<tag>Networking</tag>
</tags>
</entry>
<entry>
<title>CS144-Lab3实验笔记</title>
<link href="/202205/cs144-lab3/"/>
<url>/202205/cs144-lab3/</url>
<content type="html"><![CDATA[<h2 id="实验简介"><a href="#实验简介" class="headerlink" title="实验简介"></a>实验简介</h2><p>Lab3这个实验主要完成的是TCP发送端的功能。应用程序写入字节流到socket,TCP Sender负责将本端的字节流发送到对端。</p><p>具体地说,TCP Sender负责: </p><ul><li><p>根据对端的“接受窗口大小 + 期望收到的下一个字节”,来构造要发送的TCP报文头部和数据。</p></li><li><p>如果超出规定的时间后仍未收到一个已发送报文的应答,则需要重传这个报文。</p></li></ul><p>要实现的功能有:1. 如何构造要发送的TCP头部和数据;2. 处理对端对于发送报文的响应;3. 重传没有收到响应的报文</p><h2 id="实验思路"><a href="#实验思路" class="headerlink" title="实验思路"></a>实验思路</h2><h3 id="1-构造要发送的TCP头部和数据"><a href="#1-构造要发送的TCP头部和数据" class="headerlink" title="1. 构造要发送的TCP头部和数据"></a>1. 构造要发送的TCP头部和数据</h3><p>1.1 想发送数据需要先建立连接(也就是三次握手后才能发送数据)</p><blockquote><p>RFC 793定义的TCP不允许第一个SYN包携带数据,CS144按此实现。</p><p>但TCP Fast Open是允许第一个SYN包携带数据的。<br>参考<a href="https://lwn.net/Articles/508865/">TCP Fast Open: expediting web services</a></p></blockquote><p>1.2 建立连接后按照对端窗口大小减去已发送未被应答的部分,尽可能地用ByteStream内的数据填充TCP DataPayload。报文的序列号应该设置为“报文第一个字节的相对序列号”</p><p>1.3 如果本端ByteStream读到了EOF,并且本TCP报文可以再多携带一个字节,则发送FIN报文。</p><p>1.4 发送新建的报文时,记录这个报文以便重传,直到收到应答;更新下一次会发送的ByteStream用到的绝对序列号。</p><p>1.5 第一个SYN报文,假定对方窗口为1。</p><h3 id="2-对端对于发送报文的响应"><a href="#2-对端对于发送报文的响应" class="headerlink" title="2. 对端对于发送报文的响应"></a>2. 对端对于发送报文的响应</h3><p>对端响应的报文有两个字段需要发送方关心,</p><ul><li><code>ackno</code> 表示对端期望收到下一个数据包的序列号</li><li><code>win_size</code> 表示对端期望接受数据包的最大长度</li></ul><p>收到<code>ackno</code> 后先转换为基于<code>isn</code>的绝对序列号,如果新的绝对序列号小于本端的绝对序列号,说明收到了重复的确认,直接返回即可。</p><p>否则认为所有绝对序列号在新序列号之前的那些报文都已经成功地被对端接受了。</p><h3 id="3-重传没有收到响应的报文"><a href="#3-重传没有收到响应的报文" class="headerlink" title="3. 重传没有收到响应的报文"></a>3. 重传没有收到响应的报文</h3><p>这部分主要参考RFC6298 Section 5的算法实现,按照这个算法,在各个函数里面加一些逻辑即可~</p><p>这个RFC主要规定了TCP如何度量RTO(retransmission timeout),以及在经过一个RTO时间后如何进行重传的算法。</p><p>本实验主要关心如何实现重传算法,这部分内容在<a href="https://www.rfc-editor.org/rfc/rfc6298.html#section-5">Section 5</a>中描述。</p><p>下面给出大致翻译</p><blockquote><p>5.管理RTO计时器</p><p>对于一个重传计时器,<strong>必须满足不能过早地重传这个要求</strong>,也就是不能在小于一个RTO时间内进行重传。<br>以下是管理重传计时器的推荐算法:</p><p>5.1 每次发送包含数据的数据包时(这里的发送包括重传),如果计时器没有启动,则启动计时器。这样计时器才有可能在一个RTO时间后过期(指的是当前的RTO)</p><p>5.2 如果收到了所有“已发送但未被确认”数据的ACK报文后,关闭重传计时器</p><p>5.3 每次收到对新数据的ACK报文时,重启重传计时器,使其可以在一个RTO后过期(指的是当前的RTO)</p><p>当重传计时器过期后,执行如下操作<br>5.4 重传最早的”已发送但未被确认”的TCP段</p><p>5.5 主机(注,这里指TCP发送端)<strong>必须</strong>将新的RTO设置为当前RTO值的两倍。(RTO的上界在本RFC 2.5节中讨论了)</p><p>5.6 启动重传计时器,使其可以在一个RTO后过期(指的是当前的RTO)</p><p>5.7 如果在等待SYN-ACK这个TCP报文时候,重传计时器过期了,并且这个计时器的RTO小于3秒,则当数据传输开始的时候(即,三次握手完成后),RTO必须重新初始化为3秒</p></blockquote><h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>计时器的实现</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RetransTimer</span> {</span><br> <span class="hljs-keyword">private</span>:<br> <span class="hljs-keyword">uint32_t</span> _remaining_time{<span class="hljs-number">0</span>};<br> <span class="hljs-keyword">bool</span> _is_stopped{<span class="hljs-literal">false</span>};<br> <span class="hljs-keyword">bool</span> _is_expired{<span class="hljs-literal">false</span>};<br><br> <span class="hljs-keyword">public</span>:<br> <span class="hljs-built_in">RetransTimer</span>() : _remaining_time(<span class="hljs-number">0</span>), _is_stopped(<span class="hljs-literal">true</span>), _is_expired(<span class="hljs-literal">false</span>) {}<br> <span class="hljs-built_in">RetransTimer</span>(<span class="hljs-keyword">uint32_t</span> init_time, <span class="hljs-keyword">bool</span> stopped, <span class="hljs-keyword">bool</span> expired)<br> : _remaining_time(init_time), _is_stopped(stopped), _is_expired(expired) {}<br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">tick_to_retrans_timer</span><span class="hljs-params">(<span class="hljs-keyword">uint32_t</span> ms_since_last_tick)</span> </span>{<br> <span class="hljs-keyword">if</span> (_is_stopped) {<br> <span class="hljs-keyword">return</span>;<br> }<br> <span class="hljs-keyword">if</span> (ms_since_last_tick >= _remaining_time) {<br> _remaining_time = <span class="hljs-number">0</span>;<br> _is_expired = <span class="hljs-literal">true</span>;<br> } <span class="hljs-keyword">else</span> {<br> _remaining_time -= ms_since_last_tick;<br> }<br> }<br> <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">is_expired</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> _is_expired; }<br> <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">is_stopped</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> _is_stopped; }<br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">start_new_timer</span><span class="hljs-params">(<span class="hljs-keyword">uint32_t</span> new_rto)</span> </span>{<br> _remaining_time = new_rto;<br> _is_expired = <span class="hljs-literal">false</span>;<br> _is_stopped = <span class="hljs-literal">false</span>;<br> }<br><br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">stop_retrans_timer</span><span class="hljs-params">()</span> </span>{<br> _is_stopped = <span class="hljs-literal">true</span>;<br> _is_expired = <span class="hljs-literal">false</span>;<br> _remaining_time = <span class="hljs-number">0</span>;<br> }<br>};<br><br></code></pre></td></tr></table></figure><p>类内新增成员</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TCPSender</span> {</span><br> <span class="hljs-keyword">private</span>:<br> <span class="hljs-comment">//! Some TCP state flags send to the other side,</span><br> <span class="hljs-keyword">bool</span> _syn_sent{<span class="hljs-literal">false</span>};<br> <span class="hljs-comment">//! When FIN is sent, it means that the data stream is closed on its own side, `fill_window` will return directly</span><br> <span class="hljs-keyword">bool</span> _fin_sent{<span class="hljs-literal">false</span>};<br> <span class="hljs-comment">//! the receive windows size, from the other side</span><br> <span class="hljs-keyword">uint16_t</span> _win_size{<span class="hljs-number">1</span>};<br><br> <span class="hljs-comment">//! Once the segment is filled the window(using the data payload), it will be sent to the other side</span><br> <span class="hljs-comment">//! In this lab `send_segments` means move the segment to `_segments_out` FIFO and `_outstanding_segments` map</span><br> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">send_segment</span><span class="hljs-params">(TCPSegment &seg)</span></span>;<br> <span class="hljs-comment">//! keep track of segments which have been sent but not yet acked by the receiver</span><br> <span class="hljs-comment">//!@{</span><br> <span class="hljs-comment">// first-> the absolute sequence number, it will be mono increased</span><br> <span class="hljs-comment">// second-> the outstanding tcp segment</span><br> std::vector<std::pair<<span class="hljs-keyword">size_t</span>, TCPSegment>> _outstanding_segments{};<br> <span class="hljs-keyword">size_t</span> _outstanding_bytes{<span class="hljs-number">0</span>};<br> <span class="hljs-comment">// !@}</span><br><br> <span class="hljs-comment">//! our initial sequence number, the number for our SYN.</span><br> WrappingInt32 _isn;<br><br> <span class="hljs-comment">//! outbound queue of segments that the TCPSender wants sent</span><br> std::queue<TCPSegment> _segments_out{};<br><br> <span class="hljs-comment">//! retransmission timer for the connection</span><br> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> _initial_retransmission_timeout;<br> RetransTimer _retrans_timer{};<br> <span class="hljs-comment">//! current retransmission timeout value, aka RTO</span><br> <span class="hljs-keyword">uint64_t</span> _current_retransmission_timeout{<span class="hljs-number">0</span>};<br> <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> _consecutive_retransmission_cnt{<span class="hljs-number">0</span>};<br> ...<br>}<br></code></pre></td></tr></table></figure><p>方法实现</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">//! \param[in] capacity the capacity of the outgoing byte stream</span><br><span class="hljs-comment">//! \param[in] retx_timeout the initial amount of time to wait before retransmitting the oldest outstanding segment</span><br><span class="hljs-comment">//! \param[in] fixed_isn the Initial Sequence Number to use, if set (otherwise uses a random ISN)</span><br>TCPSender::<span class="hljs-built_in">TCPSender</span>(<span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> capacity, <span class="hljs-keyword">const</span> <span class="hljs-keyword">uint16_t</span> retx_timeout, <span class="hljs-keyword">const</span> std::optional<WrappingInt32> fixed_isn)<br> : _isn(fixed_isn.<span class="hljs-built_in">value_or</span>(WrappingInt32{<span class="hljs-built_in">random_device</span>()()}))<br> , _initial_retransmission_timeout{retx_timeout}<br> , _stream(capacity) {<br> _current_retransmission_timeout = _initial_retransmission_timeout;<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">uint64_t</span> <span class="hljs-title">TCPSender::bytes_in_flight</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> _outstanding_bytes; }<br><br><span class="hljs-comment">//! \details set the segment header and payload, fill the other side receive window size as much as possible</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">TCPSender::fill_window</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">size_t</span> receiver_win_size = _win_size ? _win_size : <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">while</span> ((receiver_win_size > _outstanding_bytes) && !_fin_sent) {<br> TCPSegment seg;<br> <span class="hljs-keyword">if</span> (!_syn_sent) {<br> seg.<span class="hljs-built_in">header</span>().syn = <span class="hljs-literal">true</span>;<br> _syn_sent = <span class="hljs-literal">true</span>;<br> }<br> seg.<span class="hljs-built_in">header</span>().seqno = <span class="hljs-built_in">next_seqno</span>();<br><br> <span class="hljs-comment">// the max bytes could this segment carried</span><br> <span class="hljs-keyword">size_t</span> max_payload_size =<br> <span class="hljs-built_in">min</span>(TCPConfig::MAX_PAYLOAD_SIZE, receiver_win_size - _outstanding_bytes - seg.<span class="hljs-built_in">header</span>().syn);<br> string payload = _stream.<span class="hljs-built_in">read</span>(max_payload_size);<br> seg.<span class="hljs-built_in">payload</span>() = <span class="hljs-built_in">Buffer</span>(std::<span class="hljs-built_in">move</span>(payload));<br> <span class="hljs-comment">// send FIN flag if reached EOF of stream</span><br> <span class="hljs-keyword">if</span> (!_fin_sent && _stream.<span class="hljs-built_in">eof</span>() && seg.<span class="hljs-built_in">length_in_sequence_space</span>() + _outstanding_bytes < receiver_win_size) {<br> seg.<span class="hljs-built_in">header</span>().fin = <span class="hljs-literal">true</span>;<br> _fin_sent = <span class="hljs-literal">true</span>;<br> }<br> <span class="hljs-keyword">if</span> (seg.<span class="hljs-built_in">length_in_sequence_space</span>()) {<br> <span class="hljs-built_in">send_segment</span>(seg);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br>}<br><span class="hljs-comment">//! The segment here is NOT EMPTY (non zero length in sequence space)</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">TCPSender::send_segment</span><span class="hljs-params">(TCPSegment &seg)</span> </span>{<br> _segments_out.<span class="hljs-built_in">push</span>(seg);<br> _outstanding_segments.<span class="hljs-built_in">emplace_back</span>(_next_seqno, seg);<br> <span class="hljs-keyword">const</span> <span class="hljs-keyword">auto</span> seg_length = seg.<span class="hljs-built_in">length_in_sequence_space</span>();<br> _next_seqno += seg_length;<br> _outstanding_bytes += seg_length;<br><br> <span class="hljs-keyword">if</span> (_retrans_timer.<span class="hljs-built_in">is_stopped</span>()) {<br> _retrans_timer.<span class="hljs-built_in">start_new_timer</span>(_current_retransmission_timeout);<br> }<br>}<br><br><span class="hljs-comment">//! \param ackno The remote receiver's ackno (acknowledgment number)</span><br><span class="hljs-comment">//! \param window_size The remote receiver's advertised window size</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">TCPSender::ack_received</span><span class="hljs-params">(<span class="hljs-keyword">const</span> WrappingInt32 ackno, <span class="hljs-keyword">const</span> <span class="hljs-keyword">uint16_t</span> window_size)</span> </span>{<br> <span class="hljs-comment">//! `absolute_ackno` is the number of bytes that the receiver received.</span><br> <span class="hljs-comment">//! `_next_seqno` is the number of bytes that the sender wants to send, i.e. the last `absolute-seqno`</span><br> <span class="hljs-keyword">size_t</span> absolute_ackno = <span class="hljs-built_in">unwrap</span>(ackno, _isn, _next_seqno);<br> <span class="hljs-keyword">if</span> (absolute_ackno > _next_seqno) {<br> <span class="hljs-keyword">return</span>;<br> }<br> _win_size = window_size;<br> <span class="hljs-comment">//! Remove segments that have now been fully acknoledged segment in `_outstanding_segment`</span><br> <span class="hljs-keyword">auto</span> iter = _outstanding_segments.<span class="hljs-built_in">begin</span>();<br> <span class="hljs-keyword">bool</span> acked_new_data = <span class="hljs-literal">false</span>;<br> <span class="hljs-keyword">while</span> (!_outstanding_segments.<span class="hljs-built_in">empty</span>()) {<br> <span class="hljs-keyword">const</span> <span class="hljs-keyword">auto</span> &seg = iter->second;<br> <span class="hljs-keyword">const</span> <span class="hljs-keyword">auto</span> seg_length = seg.<span class="hljs-built_in">length_in_sequence_space</span>();<br> <span class="hljs-keyword">if</span> (iter->first + seg_length <= absolute_ackno) {<br> <span class="hljs-comment">// erase returns the iterator following the last removed element.</span><br> iter = _outstanding_segments.<span class="hljs-built_in">erase</span>(iter);<br> _outstanding_bytes -= seg_length;<br> acked_new_data = <span class="hljs-literal">true</span>;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> <span class="hljs-comment">//! When received a valid ackno, which means the receiver receipt of the new data</span><br> <span class="hljs-comment">//! the retransmission timer will restart if there are outstanding segments (for the current value of RTO).,</span><br> <span class="hljs-comment">//! otherwise the timer will stop</span><br> <span class="hljs-keyword">if</span> (acked_new_data) {<br> _current_retransmission_timeout = _initial_retransmission_timeout;<br> <span class="hljs-keyword">if</span> (!_outstanding_segments.<span class="hljs-built_in">empty</span>()) {<br> _retrans_timer.<span class="hljs-built_in">start_new_timer</span>(_current_retransmission_timeout);<br> } <span class="hljs-keyword">else</span> {<br> _retrans_timer.<span class="hljs-built_in">stop_retrans_timer</span>();<br> }<br> _consecutive_retransmission_cnt = <span class="hljs-number">0</span>;<br> }<br> <span class="hljs-built_in">fill_window</span>();<br> <span class="hljs-keyword">return</span>;<br>}<br><br><span class="hljs-comment">//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">TCPSender::tick</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> ms_since_last_tick)</span> </span>{<br> _retrans_timer.<span class="hljs-built_in">tick_to_retrans_timer</span>(ms_since_last_tick);<br> <span class="hljs-comment">// If the retrans_timer is expired, it will retransmit the earliest segment when the window size is not zero</span><br> <span class="hljs-comment">// then double the RTO, restart a new timer.</span><br> <span class="hljs-keyword">if</span> (_retrans_timer.<span class="hljs-built_in">is_expired</span>() && !_outstanding_segments.<span class="hljs-built_in">empty</span>()) {<br> <span class="hljs-keyword">auto</span> iter = _outstanding_segments.<span class="hljs-built_in">begin</span>();<br> <span class="hljs-keyword">if</span> (_win_size > <span class="hljs-number">0</span>) {<br> _current_retransmission_timeout <<= <span class="hljs-number">1</span>;<br> _consecutive_retransmission_cnt++;<br> }<br> <span class="hljs-keyword">if</span> (_consecutive_retransmission_cnt <= TCPConfig::MAX_RETX_ATTEMPTS) {<br> _segments_out.<span class="hljs-built_in">push</span>(iter->second);<br> }<br> _retrans_timer.<span class="hljs-built_in">start_new_timer</span>(_current_retransmission_timeout);<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> <span class="hljs-title">TCPSender::consecutive_retransmissions</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{ <span class="hljs-keyword">return</span> _consecutive_retransmission_cnt; }<br><br><span class="hljs-comment">//! \details The segment with zero data and correct `seqno` is useful for `ACK` the other side.</span><br><span class="hljs-comment">// it will never be retransmitted, and doesn't need to keep track.</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">TCPSender::send_empty_segment</span><span class="hljs-params">()</span> </span>{<br> TCPSegment segment;<br> segment.<span class="hljs-built_in">header</span>().seqno = <span class="hljs-built_in">next_seqno</span>();<br> _segments_out.<span class="hljs-built_in">push</span>(segment);<br> <span class="hljs-keyword">return</span>;<br>}<br><br></code></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>独立的重传计时器的确逻辑清晰很多,第一次实现的时候,发送报文的逻辑和计时器的逻辑混在一次,把我绕晕了。。。</li><li>重传计时器默认应该是关闭的,我第一次实现时候</li></ol>]]></content>
<tags>
<tag>CS144</tag>
<tag>TCP/IP</tag>
<tag>Networking</tag>
</tags>
</entry>
<entry>
<title>CS144-Lab2实验笔记</title>
<link href="/202205/cs144-lab2/"/>
<url>/202205/cs144-lab2/</url>
<content type="html"><![CDATA[<h2 id="实验简介"><a href="#实验简介" class="headerlink" title="实验简介"></a>实验简介</h2><p>这个实验主要完成的是,TCP接受端应该如何工作,才能正确地将TCP Payload交付给socket的问题。</p><p>CS144 Lab实现的是这个场景:NIC从链路上收到MAC数据包,交给kernel;kernel解析MAC头后,将这个包传给虚拟的tun设备。CS144 Lab中的其他辅助函数从tun设备中读取IP包,拆包后将完整TCP报文交给<code>tcp_receiver/tcp_sender</code> 这两个我们自己写的程序。</p><p><code>tcp_sender</code> 负责:解析包头、将Payload交付给socket,这样应用程序就能从socket中读取数据了。</p><h2 id="实验思路"><a href="#实验思路" class="headerlink" title="实验思路"></a>实验思路</h2><h3 id="1-序列号与绝对序列号的相互转换"><a href="#1-序列号与绝对序列号的相互转换" class="headerlink" title="1. 序列号与绝对序列号的相互转换"></a>1. 序列号与绝对序列号的相互转换</h3><p>对于这个问题不必考虑SYN/FIN是否计算的问题,因为二者都包含了SYN/FIN</p><p>1.1 收到的TCP Header中<code>seqno</code>是对端表示其TCP报文内携带的Payload的序列号。</p><ul><li><p>将收到的第一个包(SYN包)携带的<code>seqno</code>称为“起始序列号”,通常是一个32位随机值</p></li><li><p>之后收到的包的<code>seqno</code>则表示当前包携带的Payload相对于“起始序列号”的偏移</p></li></ul><p>1.2 绝对序列号指的是当前包的<code>seqno</code>相对于起始序列号,实际的偏移</p><ul><li>这样就可以知道当前包Payload在乱序字节流里面的起始index了(绝对序列号转到stream index的时候才考虑忽略SYN的问题)</li></ul><p>1.3 绝对序列号转换为序列号</p><ul><li>绝对序列号加上起始序列号对2^32取模即可</li></ul><p>1.4 序列号转换为绝对序列号</p><p>(TODO)</p><h3 id="2-实现TCP接收端"><a href="#2-实现TCP接收端" class="headerlink" title="2. 实现TCP接收端"></a>2. 实现TCP接收端</h3><p>2.1 接收端根据收到TCP包头的<code>seqno + SYN_FLAG + FIN_FLAG + Payload</code> 这几个字段,决定本端的<code>ackno + win_size</code><br>如下图所示<br><img src="/img/cs144-pic/Lab2-TCP-hdr.png"></p><p>2.2 根据当前包的<code>seqno</code>得到绝对序列号,再得到<code>stream index</code></p><ul><li>将Payload按照<code>stream index</code>推到乱序字节流中。乱序如何重组为有序的问题Lab1中已经实现了。</li></ul><p>2.3 向对端响应<code>ackno</code></p><ul><li><code>ackno</code> 指的是本端期望收到下一个数据包的序列号。也就是已经收到的数据的<strong>有序</strong>字节长度加1</li></ul><p>2.4 向对端响应<code>window_size</code></p><ul><li><code>window_size</code> 指的是本端的接受窗口大小,对端按照收到的窗口大小来填充payload的字节数。具体到实验就是接收端乱序字节流的最大容量减去有序字节流中的字节数(已经重组但未被应用程序读取这部分)。</li></ul><h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>TCPReceiver类内新增一些变量</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TCPReceiver</span> {</span><br> <span class="hljs-comment">//! Some TCP Flag received in the first and the last segment from other side</span><br> <span class="hljs-keyword">bool</span> _syn{<span class="hljs-literal">false</span>};<br> <span class="hljs-keyword">bool</span> _fin{<span class="hljs-literal">false</span>};<br> WrappingInt32 _isn{<span class="hljs-number">0</span>};<br><br>...<br>}<br></code></pre></td></tr></table></figure><p>具体的实现</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// libsponge/tcp_receiver.cc </span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">TCPReceiver::segment_received</span><span class="hljs-params">(<span class="hljs-keyword">const</span> TCPSegment &seg)</span> </span>{<br> <span class="hljs-keyword">const</span> <span class="hljs-keyword">auto</span> &hdr = seg.<span class="hljs-built_in">header</span>();<br> <span class="hljs-keyword">const</span> <span class="hljs-keyword">auto</span> &data = seg.<span class="hljs-built_in">payload</span>();<br> <span class="hljs-comment">// Not receive SYN yet</span><br> <span class="hljs-keyword">if</span> (!_syn) {<br> <span class="hljs-keyword">if</span> (!hdr.syn) {<br> <span class="hljs-keyword">return</span>;<br> } <span class="hljs-keyword">else</span> {<br> _syn = <span class="hljs-literal">true</span>;<br> <span class="hljs-comment">// the sequence number of the first segment is the initial sequence number</span><br> _isn = hdr.seqno;<br> }<br> }<br> <span class="hljs-comment">// NOW, received SYN from the other side</span><br> <span class="hljs-keyword">if</span> (hdr.fin) {<br> _fin = <span class="hljs-literal">true</span>;<br> }<br> <span class="hljs-comment">// the checkpoint should be the last absolute sequence number</span><br> <span class="hljs-keyword">size_t</span> ckpt = <span class="hljs-built_in">stream_out</span>().<span class="hljs-built_in">bytes_written</span>() + <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">uint64_t</span> absolute_seqno = <span class="hljs-built_in">unwrap</span>(hdr.seqno, _isn, ckpt);<br> <span class="hljs-comment">// In the first segment, the stream index should be 0, or this index should be absolute seqno minus 1</span><br> <span class="hljs-keyword">uint64_t</span> stream_idx = absolute_seqno + <span class="hljs-keyword">static_cast</span><<span class="hljs-keyword">uint64_t</span>>(hdr.syn) - <span class="hljs-number">1</span>;<br> _reassembler.<span class="hljs-built_in">push_substring</span>(data.<span class="hljs-built_in">copy</span>(), stream_idx, _fin);<br>}<br><br><span class="hljs-function">optional<WrappingInt32> <span class="hljs-title">TCPReceiver::ackno</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{<br> <span class="hljs-keyword">if</span> (!_syn) {<br> <span class="hljs-keyword">return</span> nullopt;<br> }<br> <span class="hljs-comment">// written bytes + SYN</span><br> <span class="hljs-keyword">uint64_t</span> absolute_ackno = <span class="hljs-built_in">stream_out</span>().<span class="hljs-built_in">bytes_written</span>() + <span class="hljs-number">1</span>;<br> <span class="hljs-comment">// Only when there is no segment on the fly and receive the FIN flag, then the absolute seq should add 1</span><br> absolute_ackno += (_fin && _reassembler.<span class="hljs-built_in">unassembled_bytes</span>() == <span class="hljs-number">0</span>) ? <span class="hljs-number">1</span> : <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">wrap</span>(absolute_ackno, _isn);<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">size_t</span> <span class="hljs-title">TCPReceiver::window_size</span><span class="hljs-params">()</span> <span class="hljs-keyword">const</span> </span>{<br> <span class="hljs-comment">// the capacity minus the bytes have been reassembled, but not consumed</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>->_capacity - <span class="hljs-keyword">this</span>-><span class="hljs-built_in">stream_out</span>().<span class="hljs-built_in">buffer_size</span>();<br>}<br></code></pre></td></tr></table></figure>]]></content>
<tags>
<tag>CS144</tag>
<tag>TCP/IP</tag>
<tag>Networking</tag>
</tags>
</entry>
<entry>
<title>CS144-Lab1实验笔记</title>
<link href="/202205/cs144-lab1/"/>
<url>/202205/cs144-lab1/</url>
<content type="html"><![CDATA[<h2 id="实验简介"><a href="#实验简介" class="headerlink" title="实验简介"></a>实验简介</h2><p>Lab0中完成了有序字节流的写入和读出,这个实验需要完成乱序字节流的重组、写入和读出。也就是说,Lab1实验手册中的这幅图,蓝色和绿色部分我们已经在Lab0中完成了,我们在Lab1中只需要完成红色部分的重组即可。</p><p><img src="/img/cs144-pic/lab1-stream.png"></p><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>(这个思路有很大的性能优化空间)</p><ol><li>使用map存储乱序字节流,<code>key=(起始下标,结束下标)</code>,均从0开始,<code>value=(字符串)</code>,<code>pair<int,int></code>作为key的时候不需要自己写比较函数。</li><li>每当有新字符串push进来后,在map中确定要插入的位置(应该插入到low_bound返回的之前一个),不断判断当前字符串是否能合并之前一个键值对或者之后的一个。</li><li>如果当前map的第一个元素可以插入到有序字节流中,弹出完整的第一个元素,或者部分第一个元素。</li><li>只有当“当前不存在乱序字节,且已经收到了对端发来的EOF标志”,有序字节流的<code>end_of_input</code>才有效。有一种情况是,收到了对端发来的EOF标志(收到了FIN包),但是中间仍有未收到的片段(空洞),这时候有序字节流不能结束。</li><li>注意一些conner case,比如写入有序字节流的时候当前字符串不一定能全部写入。</li></ol><h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>类内新增变量</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// libsponge/stream_reassembler.hh </span><br><span class="hljs-keyword">size_t</span> _unassembled_bytes{<span class="hljs-number">0</span>};<br><span class="hljs-keyword">size_t</span> _first_unassembled_index{<span class="hljs-number">0</span>};<br><span class="hljs-keyword">bool</span> _eof{<span class="hljs-literal">false</span>};<br><span class="hljs-comment">// key-> <start_idx, end_idx>, value -> str_to_assembled</span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">recv_bytes_t</span> = pair<pair<<span class="hljs-keyword">size_t</span>, <span class="hljs-keyword">size_t</span>>, std::string>;<br>map<pair<<span class="hljs-keyword">size_t</span>, <span class="hljs-keyword">size_t</span>>, std::string> _str_to_assemble;<br><br>ByteStream _output; <span class="hljs-comment">//!< The reassembled in-order byte stream</span><br><span class="hljs-keyword">size_t</span> _capacity; <span class="hljs-comment">//!< The maximum number of bytes</span><br><br><span class="hljs-function"><span class="hljs-keyword">recv_bytes_t</span> <span class="hljs-title">merge_two_unassembled_strs</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">recv_bytes_t</span> &a, <span class="hljs-keyword">const</span> <span class="hljs-keyword">recv_bytes_t</span> &b)</span> <span class="hljs-keyword">const</span></span>;<br></code></pre></td></tr></table></figure><p><code>push_string</code> 方法</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">//! \details This function accepts a substring (aka a segment) of bytes,</span><br><span class="hljs-comment">//! possibly out-of-order, from the logical stream, and assembles any newly</span><br><span class="hljs-comment">//! contiguous substrings and writes them into the output stream in order.</span><br><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">StreamReassembler::push_substring</span><span class="hljs-params">(<span class="hljs-keyword">const</span> string &data, <span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> index, <span class="hljs-keyword">const</span> <span class="hljs-keyword">bool</span> eof)</span> </span>{<br> <span class="hljs-keyword">size_t</span> cur_win_max_idx = _first_unassembled_index + _capacity - _output.<span class="hljs-built_in">buffer_size</span>();<br> <span class="hljs-keyword">if</span> (index >= cur_win_max_idx)<br> <span class="hljs-keyword">return</span>;<br><br> <span class="hljs-keyword">if</span> (eof) {<br> _eof = <span class="hljs-literal">true</span>;<br> }<br> <span class="hljs-comment">// cut the chars which are out of window or have been assembled</span><br> <span class="hljs-keyword">size_t</span> data_start_idx = <span class="hljs-built_in">max</span>(index, _first_unassembled_index);<br> <span class="hljs-keyword">size_t</span> data_end_idx = <span class="hljs-built_in">min</span>(cur_win_max_idx, index + data.<span class="hljs-built_in">size</span>());<br> <span class="hljs-keyword">if</span> (data_end_idx >= data_start_idx) {<br> pair<<span class="hljs-keyword">size_t</span>, <span class="hljs-keyword">size_t</span>> cur_data_start_end_index = <span class="hljs-built_in">make_pair</span>(data_start_idx, data_end_idx);<br> string cur_data = data.<span class="hljs-built_in">substr</span>(data_start_idx - index, data_end_idx - data_start_idx + <span class="hljs-number">1</span>);<br> <span class="hljs-comment">// insert current data to str_to_assemble map</span><br> <span class="hljs-keyword">recv_bytes_t</span> cur_recv_bytes = {cur_data_start_end_index, cur_data};<br> <span class="hljs-keyword">while</span> (!<span class="hljs-keyword">this</span>-><span class="hljs-built_in">empty</span>()) {<br> <span class="hljs-keyword">auto</span> iter = _str_to_assemble.<span class="hljs-built_in">lower_bound</span>(cur_data_start_end_index);<br> <span class="hljs-keyword">bool</span> cur_data_could_merge_right =<br> (iter != _str_to_assemble.<span class="hljs-built_in">end</span>()) && (cur_recv_bytes.first.second >= iter->first.first);<br> <span class="hljs-keyword">if</span> (cur_data_could_merge_right) {<br> cur_recv_bytes = <span class="hljs-built_in">merge_two_unassembled_strs</span>(cur_recv_bytes, *iter);<br> _unassembled_bytes -= iter->second.<span class="hljs-built_in">size</span>();<br> _str_to_assemble.<span class="hljs-built_in">erase</span>(iter);<br> iter = _str_to_assemble.<span class="hljs-built_in">lower_bound</span>(cur_data_start_end_index);<br> }<br><br> <span class="hljs-keyword">bool</span> cur_data_could_merge_left =<br> (iter != _str_to_assemble.<span class="hljs-built_in">begin</span>()) && ((--iter)->first.second >= cur_recv_bytes.first.first);<br> <span class="hljs-keyword">if</span> (cur_data_could_merge_left) {<br> cur_recv_bytes = <span class="hljs-built_in">merge_two_unassembled_strs</span>(*iter, cur_recv_bytes);<br> _unassembled_bytes -= iter->second.<span class="hljs-built_in">size</span>();<br> _str_to_assemble.<span class="hljs-built_in">erase</span>(iter);<br> }<br> <span class="hljs-keyword">if</span> (!cur_data_could_merge_right && !cur_data_could_merge_left) {<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> <span class="hljs-comment">// insert the unassembled str to map</span><br> _str_to_assemble.<span class="hljs-built_in">insert</span>(cur_recv_bytes);<br> _unassembled_bytes += cur_recv_bytes.second.<span class="hljs-built_in">size</span>();<br> }<br><br> <span class="hljs-comment">// if the first chunk start index is smaller than _first_unassembled_index</span><br> <span class="hljs-comment">// write the first chunk to output bytestream</span><br> <span class="hljs-keyword">auto</span> iter = _str_to_assemble.<span class="hljs-built_in">begin</span>();<br> <span class="hljs-keyword">if</span> (!_str_to_assemble.<span class="hljs-built_in">empty</span>() && (iter->first.first <= _first_unassembled_index)) {<br> <span class="hljs-keyword">auto</span> temp_map_head = *iter;<br> _str_to_assemble.<span class="hljs-built_in">erase</span>(iter);<br> <span class="hljs-keyword">size_t</span> written_len = _output.<span class="hljs-built_in">write</span>(temp_map_head.second);<br> _unassembled_bytes -= written_len;<br> <span class="hljs-keyword">if</span> (written_len == temp_map_head.second.<span class="hljs-built_in">size</span>()) {<br> <span class="hljs-comment">// The first chunk was all written to the output stream</span><br> _first_unassembled_index = temp_map_head.first.first + temp_map_head.second.<span class="hljs-built_in">size</span>();<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// Part of first chunk was written to the output stream</span><br> <span class="hljs-keyword">size_t</span> new_data_start_index = temp_map_head.first.first + written_len;<br> _str_to_assemble.<span class="hljs-built_in">insert</span>(<br> {{new_data_start_index, temp_map_head.first.second}, temp_map_head.second.<span class="hljs-built_in">substr</span>(written_len)});<br> _first_unassembled_index = new_data_start_index;<br> }<br> }<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">empty</span>() && _eof) {<br> _output.<span class="hljs-built_in">end_input</span>();<br> }<br>}<br><br><span class="hljs-comment">//! \details This function merge two recv_bytes_type pairs, the start index of a is always smaller than b.</span><br><span class="hljs-function">StreamReassembler::<span class="hljs-keyword">recv_bytes_t</span> <span class="hljs-title">StreamReassembler::merge_two_unassembled_strs</span><span class="hljs-params">(</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> StreamReassembler::<span class="hljs-keyword">recv_bytes_t</span> &a,</span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-keyword">const</span> StreamReassembler::<span class="hljs-keyword">recv_bytes_t</span> &b)</span> <span class="hljs-keyword">const</span> </span>{<br> <span class="hljs-keyword">recv_bytes_t</span> res;<br> res.first.first = a.first.first;<br> <span class="hljs-comment">// choose the bigger one for the end index of merged string.</span><br> <span class="hljs-keyword">if</span> (a.first.second > b.first.second) {<br> res.first.second = a.first.second;<br> res.second = a.second;<br> } <span class="hljs-keyword">else</span> {<br> res.first.second = b.first.second;<br> res.second = a.second.<span class="hljs-built_in">substr</span>(<span class="hljs-number">0</span>, b.first.first - a.first.first) + b.second;<br> }<br> <span class="hljs-keyword">return</span> res;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<tags>
<tag>CS144</tag>
<tag>TCP/IP</tag>
<tag>Networking</tag>
</tags>
</entry>
<entry>
<title>CS144-实验环境搭建和Lab0</title>
<link href="/202205/cs144-lab0/"/>
<url>/202205/cs144-lab0/</url>
<content type="html"><![CDATA[<h2 id="实验环境"><a href="#实验环境" class="headerlink" title="实验环境"></a>实验环境</h2><ol><li>这次打算用 Jetbrains Gateway进行远程开发。按照 CS144 README 进行远端Clion的配置。注意环境变量和编译时传给cmake的宏的设置。<br><img src="/img/cs144-pic/lab0-clion-debug.png"></li><li>安装TcpDump,最好从Github的源码编译安装,5.0版本后的tcpdump可以保存pcap文件中同时输出到标准输出流中。</li></ol><h2 id="实验0"><a href="#实验0" class="headerlink" title="实验0"></a>实验0</h2><p>使用TCPsocket和一个webserver(对端监听80端口)进行通信,类似于curl。</p><p>代码十分简单,按照socket通信流程编写即可。lab0仅完成下图中描述的TCP客户端流程即可</p><blockquote><p><img src="/img/cs144-pic/socket-client&server.png"></p><p>图片来源微信公众号<a href="https://mp.weixin.qq.com/s/Ytw_N5zeLH50ItdgAsv3nA">Linux fd 系列 — socket fd 是什么?</a></p></blockquote><p>下面给出我的实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">get_URL</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &host, <span class="hljs-keyword">const</span> <span class="hljs-built_in">string</span> &path)</span> </span>{<br> <span class="hljs-comment">// Your code here.</span><br><br> <span class="hljs-comment">// You will need to connect to the "http" service on</span><br> <span class="hljs-comment">// the computer whose name is in the "host" string,</span><br> <span class="hljs-comment">// then request the URL path given in the "path" string.</span><br> TCPSocket sock{};<br> sock.connect(Address(host, <span class="hljs-string">"http"</span>));<br> <span class="hljs-function"><span class="hljs-built_in">string</span> <span class="hljs-title">input_str</span><span class="hljs-params">(<span class="hljs-string">"GET "</span> + path + <span class="hljs-string">" HTTP/1.1\r\nHost: "</span> + host + <span class="hljs-string">"\r\n\r\n"</span>)</span></span>;<br> sock.write(input_str);<br><br> <span class="hljs-comment">// Then you'll need to print out everything the server sends back,</span><br> <span class="hljs-comment">// (not just one call to read() -- everything) until you reach</span><br> <span class="hljs-comment">// the "eof" (end of file).</span><br> sock.shutdown(SHUT_WR);<br> <span class="hljs-keyword">while</span> (!sock.eof())<br> <span class="hljs-built_in">cout</span> << sock.read();<br> sock.close();<br> <span class="hljs-comment">// cerr << "Function called: get_URL(" << host << ", " << path << ").\n";</span><br> <span class="hljs-comment">// cerr << "Warning: get_URL() has not been implemented yet.\n";</span><br>}<br></code></pre></td></tr></table></figure><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><ol><li><p>如果使用Clion的话,需要先build All Test,再运行相关测试。测试的那些target只运行测试不进行编译链接</p></li><li><p>进行抓包测试。 我的客户端是先半关闭,再一点点读对端发来的数据。抓到的包也反映了这一点</p><p> 如果使用5.0版本的tcpdump的话,可以使用–print参数。否则只能先保存,再查看。</p></li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo tcpdump -i any -ent host cs144.keithw.org -w ./pcap/lab0.pcap<br></code></pre></td></tr></table></figure><p><img src="/img/cs144-pic/lab0-pcap.png"></p><blockquote><p>这里使用了Vscode Crumbs插件用来在Vscode中看pcap包</p></blockquote><h2 id="附:CS144代码风格"><a href="#附:CS144代码风格" class="headerlink" title="附:CS144代码风格"></a>附:CS144代码风格</h2><p>CS144 使用 C++11 标准完成实验,它对C++代码的风格有着严格的限制:</p><ul><li><p>使用 Resource acquisition is initialization 风格,即 RAII 风格。</p></li><li><p>禁止使用 malloc 和 free 函数</p></li><li><p>禁止使用 new 和 delete 关键字</p></li><li><p>禁止使用原生指针(*)。若有必要,最好使用智能指针(unique_ptr等等)。(该实验没有必要用到指针)。</p></li><li><p>禁止使用模板、线程相关、各类锁机制以及虚函数</p></li><li><p>禁止使用 C 风格字符串(char*) 以及 C 风格字符串处理函数。使用 string 来代替。</p></li><li><p>禁止使用 C 风格强制类型转换。若有必要请使用 static_cast</p></li><li><p>传递参数给函数时,请使用常量引用类型(const Ty& t)</p></li><li><p>尽可能将每个变量和函数方法都声明成 const</p></li><li><p>禁止使用全局变量,以及尽可能让每个变量的作用域最小</p></li><li><p>在完成代码后,务必使用 make format 来标准化代码风格。</p></li></ul>]]></content>
<tags>
<tag>CS144</tag>
<tag>TCP/IP</tag>
<tag>Networking</tag>
</tags>
</entry>
<entry>
<title>Tmux配置与美化</title>
<link href="/202107/tmux-tutorial/"/>
<url>/202107/tmux-tutorial/</url>
<content type="html"><![CDATA[<p>本文介绍了我目前使用的Tmux配置。一来是目前的配置用着还算舒服,写此文安利一下;二来也记录一下键位映射,以做备忘。</p><p><a href="https://github.com/Gwzlchn/dotConfig/tree/master/tmux">我的Tmux配置目录</a></p><h2 id="Oh-My-Tmux"><a href="#Oh-My-Tmux" class="headerlink" title="Oh-My-Tmux"></a>Oh-My-Tmux</h2><p>目前我用的配置是基于<a href="https://github.com/gpakosz/.tmux"><code>Oh-My-Tmux</code></a>项目改造的。在这个项目上我进行了一些配色的改变(原生的黑白红是在欣赏不起来),最终效果如下图1所示,环境为<code>Windows Terminal + WSL2 + Tmux 3.0a</code><br><img src="/img/2021-07-19-tmux-tutorial/tmux-nord.png" alt="图1:Tmux效果图"></p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><ol><li><p>配置文件的安装<br><em><strong>这里是对oh-my-tmux的安装流程复述</strong></em></p><ul><li><p>前置条件</p><ul><li><code>Tmux</code>版本大于等于2.4。Tmux的版本可以通过<code>tmux -V</code>命令查看。</li><li>预装awk、perl、sed工具</li><li><code>$TERM</code>变量必须设置为<code>xterm-256color</code>。这个变量是在<code>~/.bashrc</code>中设置的,<code>echo $TERM</code>可以检查。</li></ul></li><li><p>安装配置</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ <span class="hljs-built_in">cd</span><br>$ git <span class="hljs-built_in">clone</span> https://github.com/Gwzlchn/dotConfig.git<br>$ ln -s -f ~/dotConfig/tmux/.tmux.conf<br>$ cp ~/dotConfig/tmux/.tmux.conf.local .<br></code></pre></td></tr></table></figure></li></ul></li><li><p>字体的安装<br>本配置文件中的一些符号图标的显示依赖<code>Nerd Font</code>字体,所以需要在终端软件所在的系统中安装<code>Nerd Font</code>字体。 </p><p><code>Nerd Font</code>并不是一种字体,而是在原有字体上增加了一些Unicode符号的一个字体补丁。你既可以选择手动为自己目前用的字体打上<code>Nerd Font</code>补丁(参考<a href="https://zhuanlan.zhihu.com/p/150097941">使用nerd-font/font-patcher为字体添加字体图标</a>),也可以在<a href="https://www.nerdfonts.com/font-downloads">Nerd Font Download</a>页面下载打好补丁后的字体。我用的是<code>FiraCode NerdFont</code>,这个字体看起来比较纤细,同时带连字符效果。 </p><p>我目前用的终端是<code>Windows Terminal</code>,Windows安装字体比较简单,点开字体安装即可。之后需要将终端软件配置为刚刚安装的字体。打开<code>Windows Terminal</code>的配置文件<code>settings.json</code>,在<code>profiles.defaults</code>字段下增加<code>"fontFace": xxxx</code>这个属性。如下所示。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs json"> <span class="hljs-string">"profiles"</span>:<br> {<br> <span class="hljs-attr">"defaults"</span>:<br> {<br> <span class="hljs-comment">// Put settings here that you want to apply to all profiles.</span><br> <span class="hljs-comment">// "colorScheme": "Dracula",</span><br> <span class="hljs-comment">//"acrylicOpacity" : 0.4,</span><br> <span class="hljs-attr">"useAcrylic"</span> : <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">"fontSize"</span>:<span class="hljs-number">13</span>,<br> <span class="hljs-attr">"fontFace"</span>: <span class="hljs-string">"FiraCode Nerd Font"</span>,<br> <span class="hljs-attr">"startingDirectory"</span>: <span class="hljs-string">"."</span>,<br> <span class="hljs-attr">"bellStyle"</span>:<span class="hljs-string">"none"</span><br> },<br>}<br></code></pre></td></tr></table></figure></li><li><p>如果完成了上述的安装流程,那么你在<code>Windows Terminal</code>中进入<code>WSL2</code>环境后,输入<code>tmux</code>应该就可以看到图1的效果了。</p></li></ol><h2 id="键位映射"><a href="#键位映射" class="headerlink" title="键位映射"></a>键位映射</h2><p>Tmux另一个迷人之处在于你可以用纯键盘来控制终端窗口的增删查改、甚至是缩放与恢复(<del>1.意味着极大概率会碰到Tmux快捷键与其他软件冲突的情况。2.意味着我经常记不住冷门的快捷键映射</del>。3.意味着你可以套用VIM的键位映射,但这里我没有用,<del>因为我不熟VIM</del>)。</p><p><strong>一些键位符号的约定</strong></p><ol><li><code><prefix></code> 表示前缀键,需要同时按<code>Ctrl + b</code>触发。按下之后状态栏会有提示,表示等待后续按键的输入(Oh-my-tmux效仿Screen,将<code>Ctrl+a</code>也映射成了前缀键,但会和<code>Qemu</code>冲突,故我这里取消了这个映射)</li><li><code><prefix> c</code> 表示新建一个窗口,需要先同时按<code>Ctrl + b</code>,再按<code>c</code>键触发。</li><li><code><prefix> C-c</code> 表示新建一个会话,需要先同时按<code>Ctrl + b</code>,再同时按<code>Ctrl - c</code>键触发。</li><li><code><CR></code>表示回车</li></ol><table><thead><tr><th>键位映射</th><th>动作</th><th>备注</th></tr></thead><tbody><tr><td><code><prefix> e</code></td><td>编辑<code>~/.tmux.conf.local</code>配置文件</td><td></td></tr><tr><td><code><prefix> r</code></td><td>重新加载<code>~/.tmux.conf.local</code>配置</td><td></td></tr><tr><td><code><prefix> C-c</code></td><td>新建一个Session</td><td></td></tr><tr><td><code><prefix> c</code></td><td>新建一个Window</td><td></td></tr><tr><td><code><prefix> $</code></td><td>重命名当前Session</td><td></td></tr><tr><td><code><prefix> ,</code></td><td>重命名当前Window</td><td></td></tr><tr><td><code><prefix> %</code></td><td>新建一个垂直方向的Panel</td><td></td></tr><tr><td><code><prefix> _</code> (下划线)</td><td>同上</td><td></td></tr><tr><td><code><prefix> “</code></td><td>新建一个水平方向的Panel</td><td></td></tr><tr><td><code><prefix> -</code> (减号)</td><td>同上</td><td></td></tr><tr><td><code><prefix> w</code></td><td>查看所有打开的Session和Windows</td><td>可以通过<code>j/k <CR></code>来跳转</td></tr><tr><td><code><prefix> + Up/Left/Down/Right</code></td><td>更改一个Window内Panel大小</td><td>按方向键的时候,<code><prefix></code>不要松开</td></tr><tr><td><code><prefix> h/j/k/l</code></td><td>在一个Window内切换到不同的Panel中</td><td>使用方向键亦可,按方向键的时候,<code><prefix></code>需要松开</td></tr><tr><td><code><prefix> <</code>/<code><prefix> ></code></td><td>交换两个Panel的位置</td><td></td></tr><tr><td><code><prefix> C-h</code>/<code><prefix> C-l</code></td><td>在一个Session内的不同Window中切换</td><td>默认的<code><prefix> n/p</code>切换方式已解绑</td></tr><tr><td><code><prefix> +</code></td><td>将当前Panel最大化成为一个Window,或者恢复一个Window为Panel</td><td><strong>这个是Oh-My-Tmux最有用的功能</strong></td></tr><tr><td><code><prefix> m</code></td><td>Tmux鼠标模式开关</td><td>默认开</td></tr><tr><td><code><prefix> <CR></code>/<code><prefix> [</code></td><td>进入Tmux 复制模式</td><td></td></tr><tr><td>Copy Mode <code>q</code></td><td>退出复制模式</td><td>在复制模式下</td></tr><tr><td>Copy Mode <code>C-Space</code></td><td>开始复制</td><td>同上</td></tr><tr><td>Copy Mode <code>C-w</code></td><td>将选中内容复制到Tmux Buffer中</td><td>同上</td></tr><tr><td><code><prefix> p</code>/<code><prefix> ]</code></td><td>将最新的Tmux Buffer中的内容粘贴到终端中</td><td></td></tr><tr><td><code><prefix> P</code></td><td>选择任意Tmux Buffer中的内容粘贴到终端中</td><td></td></tr><tr><td><code><prefix> b</code></td><td>查看目前所有的Tmux Buffer中的内容</td><td></td></tr></tbody></table><h2 id="美化Tmux"><a href="#美化Tmux" class="headerlink" title="美化Tmux"></a>美化Tmux</h2><p>Oh-My-Tmux给了较大的美化空间,如图2所示。四个方框依次对应的是1.status left, 2.window status, 3.window current status, 4.status right。清楚了这个对应关系,再看<code>~/.tmux.conf.local</code>中的内容就很清楚了。右侧状态栏比较好的一点是,当你使用的SSH登录到远程主机时,username和hostname也会随之改变。</p><p>配色方案我这里选用的是<a href="https://www.nordtheme.com/docs/colors-and-palettes">Nord Color Scheme</a><br><img src="/img/2021-07-19-tmux-tutorial/tmux-statusbar.png" alt="图2:Tmux状态栏效果"></p><p>最后附上我目前用的状态栏配置。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><code class="hljs config"># window status style<br># - built-in variables are:<br># - #{circled_window_index}<br># - #{circled_session_name}<br># - #{hostname}<br># - #{hostname_ssh}<br># - #{hostname_full}<br># - #{hostname_full_ssh}<br># - #{username}<br># - #{username_ssh}<br>tmux_conf_theme_window_status_fg=$nord5 <br>tmux_conf_theme_window_status_bg=$nord1 <br>tmux_conf_theme_window_status_attr="none"<br>tmux_conf_theme_window_status_format="#I #W"<br><br># window current status style<br># - built-in variables are:<br># - #{circled_window_index}<br># - #{circled_session_name}<br># - #{hostname}<br># - #{hostname_ssh}<br># - #{hostname_full}<br># - #{hostname_full_ssh}<br># - #{username}<br># - #{username_ssh}<br># ﲵ ﮊ ﮏ ♥ ﰸ <br>tmux_conf_theme_window_status_current_fg=$nord6 <br>tmux_conf_theme_window_status_current_bg=$nord10 <br>tmux_conf_theme_window_status_current_attr="bold"<br>tmux_conf_theme_window_status_current_format=" #W"<br><br># window activity status style<br>tmux_conf_theme_window_status_activity_fg="default"<br>tmux_conf_theme_window_status_activity_bg="default"<br>tmux_conf_theme_window_status_activity_attr="underscore"<br><br># window bell status style<br>tmux_conf_theme_window_status_bell_fg='#ffff00' <br>tmux_conf_theme_window_status_bell_bg="default"<br>tmux_conf_theme_window_status_bell_attr="blink,bold"<br><br># window last status style<br>tmux_conf_theme_window_status_last_fg="default"<br>tmux_conf_theme_window_status_last_bg="default"<br>tmux_conf_theme_window_status_last_attr="none"<br>tmux_conf_theme_window_status_last_format='#I #W-'<br><br># status left/right content:<br># - separate main sections with "|"<br># - separate subsections with ","<br># - built-in variables are:<br># - #{battery_bar}<br># - #{battery_hbar}<br># - #{battery_percentage}<br># - #{battery_status}<br># - #{battery_vbar}<br># - #{circled_session_name}<br># - #{hostname_ssh}<br># - #{hostname}<br># - #{hostname_full}<br># - #{hostname_full_ssh}<br># - #{loadavg}<br># - #{mouse}<br># - #{pairing}<br># - #{prefix}<br># - #{root}<br># - #{synchronized}<br># - #{uptime_y}<br># - #{uptime_d} (modulo 365 when #{uptime_y} is used)<br># - #{uptime_h}<br># - #{uptime_m}<br># - #{uptime_s}<br># - #{username}<br># - #{username_ssh}<br>tmux_conf_theme_status_left=" #S "<br>#tmux_conf_theme_status_right="#{prefix}#{mouse}#{pairing}#{synchronized}#{?battery_status,#{battery_status},}#{?battery_bar, #{battery_bar},}#{?battery_percentage, #{battery_percentage},} , %R , %d %b | #{username}#{root} | #{hostname} "<br>tmux_conf_theme_status_right='#{prefix}#{pairing}#{synchronized}#{?battery_bar, #{battery_bar},}#{?battery_percentage, #{battery_percentage},}#{?battery_status,#{battery_status},} | %b %d | %R | <br>tmux_conf_theme_status_left_fg=$nord5 # '#e4e4e4,#e4e4e4,#e4e4e4' # black, white , white<br>tmux_conf_theme_status_left_bg=$nord0 #',#00afff' # yellow, pink, white blue<br>tmux_conf_theme_status_left_attr='bold,none,none'<br><br># status right style<br>#tmux_conf_theme_status_right_fg="$tmux_conf_theme_colour_12,$tmux_conf_theme_colour_13,$tmux_conf_theme_colour_14"<br>#tmux_conf_theme_status_right_bg="$tmux_conf_theme_colour_15,$tmux_conf_theme_colour_16,$tmux_conf_theme_colour_17"<br>tmux_conf_theme_status_right_fg=$nord4,$nord6,$nord6,$nord5,$nord5<br>tmux_conf_theme_status_right_bg=$nord1,$nord7,$nord10,$nord2,$nord1 # dark gray, red, white<br>tmux_conf_theme_status_right_attr='bold,none,bold,none,none,none'<br></code></pre></td></tr></table></figure>]]></content>
<tags>
<tag>tmux</tag>
<tag>dotConfig</tag>
<tag>折腾</tag>
</tags>
</entry>
<entry>
<title>[MIT-6.S081/Fall2020] 实验笔记零 Lab0: Preparation</title>
<link href="/202106/6-s081-lab0/"/>
<url>/202106/6-s081-lab0/</url>
<content type="html"><![CDATA[<h2 id="写在前面的话"><a href="#写在前面的话" class="headerlink" title="写在前面的话"></a>写在前面的话</h2><p>为了更好地理解RISC-V指令集,也为了熟悉QEMU,所以选择刷一遍MIT-6.S081的实验。希望不仅仅以做Lab为目的,同时也精读几篇OS领域经典文章。</p><hr><p><strong>可能好多人都卡在环境准备上,但后来我发现:趁早动手做实验,比准备环境更重要~</strong></p><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>参考 6.S081 官方的配置教程:<a href="https://pdos.csail.mit.edu/6.S081/2020/tools.html">Tools</a></p><p>本人用的环境是基于<code>Qemu(KVM)</code>上的<code>Ubuntu 20.04</code>。</p><p>首先安装RISC-V工具链、GDB-Multiarch、QEMU:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu gcc-riscv64-unknown-elf<br><br><span class="hljs-comment"># fix qemu</span><br>sudo apt-get remove qemu-system-misc<br>sudo apt-get install qemu-system-misc=1:4.2-3ubuntu6<br></code></pre></td></tr></table></figure><p>检查一遍自己的环境</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ riscv64-linux-gnu-gcc --version<br>riscv64-linux-gnu-gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0<br>Copyright (C) 2019 Free Software Foundation, Inc.<br>This is free software; see the <span class="hljs-built_in">source</span> <span class="hljs-keyword">for</span> copying conditions. There is NO<br>warranty; not even <span class="hljs-keyword">for</span> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.<br><br>$ qemu-system-riscv64 --version<br>QEMU emulator version 4.2.0 (Debian 1:4.2-3ubuntu6)<br>Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers<br><br>$ riscv64-unknown-elf-gcc --version<br>riscv64-unknown-elf-gcc () 9.3.0<br>Copyright (C) 2019 Free Software Foundation, Inc.<br>This is free software; see the <span class="hljs-built_in">source</span> <span class="hljs-keyword">for</span> copying conditions. There is NO<br>warranty; not even <span class="hljs-keyword">for</span> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.<br></code></pre></td></tr></table></figure><h2 id="测试环境"><a href="#测试环境" class="headerlink" title="测试环境"></a>测试环境</h2><p>能在<code>QEMU</code>中启动<code>xv6</code>,环境就算准备完了,可以动手做实验了~</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ git <span class="hljs-built_in">clone</span> git://g.csail.mit.edu/xv6-labs-2020<br>Cloning into <span class="hljs-string">'xv6-labs-2020'</span>...<br>...<br>$ <span class="hljs-built_in">cd</span> xv6-labs-2020<br>$ git checkout util<br>Branch <span class="hljs-string">'util'</span> <span class="hljs-built_in">set</span> up to track remote branch <span class="hljs-string">'util'</span> from <span class="hljs-string">'origin'</span>.<br>Switched to a new branch <span class="hljs-string">'util'</span><br><br>$ make qemu<br>···<br>xv6 kernel is booting<br><br>hart 2 starting<br>hart 1 starting<br>init: starting sh<br>$ ls<br>. 1 1 1024<br>.. 1 1 1024<br>README 2 2 2059<br>xargstest.sh 2 3 93<br>cat 2 4 24256<br><span class="hljs-built_in">echo</span> 2 5 23080<br>(...other files)<br><br></code></pre></td></tr></table></figure><p>退出 <code>QEMU</code> 快捷键是 <code>Ctrl-a + x</code></p><p>好了,我们现在可以动手做实验了。</p><h2 id="如何在-QEMU-中使用-gdb"><a href="#如何在-QEMU-中使用-gdb" class="headerlink" title="如何在 QEMU 中使用 gdb"></a>如何在 <code>QEMU</code> 中使用 <code>gdb</code></h2><ol><li><p>首先用 <code>tmux</code> 开两个终端窗口。</p></li><li><p>第一个窗口键入<code>make qemu-gdb</code>.</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ make qemu-gdb<br>*** Now run <span class="hljs-string">'gdb'</span> <span class="hljs-keyword">in</span> another window. qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,<span class="hljs-keyword">if</span>=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -gdb tcp::26000<br></code></pre></td></tr></table></figure></li><li><p>在第二个窗口中:<br>3.1. 修改自己 <code>home</code> 目录下的 <code>.gdbinit</code> 文件,允许 <code>gdb</code> 在<code>xv6-labs-2020</code>这个目录启动的时候,加载该文件夹下的 <code>.gdbinit</code> 文件。 </p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"add-auto-load-safe-path <span class="hljs-subst">$(pwd)</span>/.gdbinit "</span> >> ~/.gdbinit<br></code></pre></td></tr></table></figure><p> 3.2. 启动 <code>gdb-multiarch</code></p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ gdb-multiarch <br>GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2<br>Copyright (C) 2020 Free Software Foundation, Inc.<br>License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.ht<br>ml><br>This is free software: you are free to change and redistribute it.<br>There is NO WARRANTY, to the extent permitted by law.<br>Type <span class="hljs-string">"show copying"</span> and <span class="hljs-string">"show warranty"</span> <span class="hljs-keyword">for</span> details.<br>This GDB was configured as <span class="hljs-string">"x86_64-linux-gnu"</span>.<br>Type <span class="hljs-string">"show configuration"</span> <span class="hljs-keyword">for</span> configuration details.<br>For bug reporting instructions, please see:<br><http://www.gnu.org/software/gdb/bugs/>.<br>Find the GDB manual and other documentation resources online at:<br> <http://www.gnu.org/software/gdb/documentation/>.<br><br>For <span class="hljs-built_in">help</span>, <span class="hljs-built_in">type</span> <span class="hljs-string">"help"</span>.<br>Type <span class="hljs-string">"apropos word"</span> to search <span class="hljs-keyword">for</span> commands related to <span class="hljs-string">"word"</span>.<br>The target architecture is assumed to be riscv:rv64<br>warning: No executable has been specified and target does not support<br>determining executable automatically. Try using the <span class="hljs-string">"file"</span> <span class="hljs-built_in">command</span>.<br>0x0000000000001000 <span class="hljs-keyword">in</span> ?? ()<br>(gdb)<br></code></pre></td></tr></table></figure><p>3.3 <strong>给代码打断点</strong> </p><ul><li><code>file user/_ls</code></li><li><code>b main</code></li><li><code>c</code></li><li>之后就是正常的<code>GDB</code> 使用流程了</li></ul><p> <strong>第二个窗口:</strong></p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash">(gdb) file user/_ls<br>Reading symbols from user/_ls...<br>(gdb) b main<br>Breakpoint 1 at 0x27a: file user/ls.c, line 75.<br>(gdb) c<br>Continuing.<br>[Switching to Thread 1.3]<br><br>Thread 3 hit Breakpoint 1, main (argc=0, argv=0x6c <fmtname+108>)<br> at user/ls.c:75<br>75 {<br>(gdb)<br></code></pre></td></tr></table></figure><p> <strong>第一个窗口</strong></p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">(......)<br>xv6 kernel is booting <br> <br>hart 1 starting <br>hart 2 starting <br>init: starting sh <br>$ ls<br>(等待 gdb 发送 <span class="hljs-built_in">continue</span> 信号)<br></code></pre></td></tr></table></figure><p> <img src="/img/6.S081/gdb.png" alt="图1:在QEMU中使用GDB"></p></li><li><p>之后我们就可以正常地通过 <code>GDB</code> 调试程序了~</p></li></ol>]]></content>
<tags>
<tag>MIT</tag>
<tag>Open Course</tag>
<tag>6.S081</tag>
<tag>OS</tag>
<tag>Lab</tag>
</tags>
</entry>
<entry>
<title>使用Github Actions自动部署Hexo博客</title>
<link href="/202106/Hexo-CI/"/>
<url>/202106/Hexo-CI/</url>
<content type="html"><![CDATA[<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>本文将介绍如何利用Github Actions功能自动部署博客。这样本地就不需要安装<code>Node.js</code>以及<code>Hexo</code>包,写完文档直接push到远程仓库,由Github Actions自动部署到博客网站上。由于本网站现阶段托管于<code>github.io</code>,因此我目前的需求是,推送博客文章的更改后,生成对应的静态网页,最后将静态网页内容自动推送到 <code>username.github.io</code> 这个仓库中。</p><h2 id="必要的准备"><a href="#必要的准备" class="headerlink" title="必要的准备"></a>必要的准备</h2><h3 id="测试常规Hexo部署流程"><a href="#测试常规Hexo部署流程" class="headerlink" title="测试常规Hexo部署流程"></a>测试常规Hexo部署流程</h3><p>参考<a href="https://hexo.io/zh-cn/docs/one-command-deployment">Hexo Doc</a>,</p><ol><li><p>安装 hexo-deployer-git</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ npm install hexo-deployer-git --save<br></code></pre></td></tr></table></figure></li><li><p>修改Hexo项目的根目录中的<code>_config.yml</code>文件</p> <figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">deploy:</span><br><span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br><span class="hljs-attr">repo:</span> <span class="hljs-string"><git@github.com:username/username.github.io.git></span><br><span class="hljs-attr">branch:</span> <span class="hljs-string">master</span><br><span class="hljs-attr">name:</span> <span class="hljs-string"><your-username?</span><br><span class="hljs-attr">email:</span> <span class="hljs-string"><your-email></span><br></code></pre></td></tr></table></figure><p> 使用<code>hexo clean && hexo g -d</code> 命令测试常规Hexo部署流程是否正常。</p></li></ol><h3 id="创建两个仓库"><a href="#创建两个仓库" class="headerlink" title="创建两个仓库"></a>创建两个仓库</h3><p><a href="https://hexo.io/zh-cn/docs/one-command-deploymen">Hexo部署文档</a>中有过说明,最好将Hexo项目目录与静态博客网页分别使用两个Git仓库管理,因此我们准备两个仓库。</p><ol><li>创建<code>my-blog</code>仓库存放Hexo项目目录</li><li>创建<code>username.github.io</code>仓库用来存放静态博客页面</li></ol><h3 id="配置密钥"><a href="#配置密钥" class="headerlink" title="配置密钥"></a>配置密钥</h3><ol><li><p>生成一个RSA密钥专供GitHub Actions部署Hexo网页使用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ ssh-keygen -f github-deploy-key<br></code></pre></td></tr></table></figure><p>当前目录下会有<code>github-deploy-key</code>(私钥)和<code>github-deploy-key.pub</code>(公钥)两个文件。</p></li><li><p>配置部署密钥<br> 2.1 复制公钥,设置为<code>username.github.io</code>仓库的部署密钥(<code>Settings -> Deploy keys -> Add deploy key</code>)</p><ul><li>Title 填写 <code>HEXO_DEPLOY_PUB</code></li><li>Key 填写复制的公钥</li><li>勾选<code>Allow write access</code>选项</li></ul><p> 2.2 复制私钥,设置为<code>my-blog</code>项目的密文(<code>Settings -> Secrets -> Add a new secret</code>)</p><ul><li>Name填写<code>HEXO_DEPLOY_PRI</code></li><li>Value填写复制的私钥</li></ul></li></ol><h2 id="编写Workflow"><a href="#编写Workflow" class="headerlink" title="编写Workflow"></a>编写Workflow</h2><p>Github使用workflow定义Github Actions的执行的环境与命令,作用类似于Gitlab中的<code>.gitlab-ci.yml</code></p><ol><li><p>在Hexo项目根目录下新建文件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ mkdir -p .github/workflows<br>$ touch .github/workflows/deploy.yml<br></code></pre></td></tr></table></figure></li><li><p>编辑<code>deploy.yml</code></p> <figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Hexo-CI</span><br><br><span class="hljs-attr">on:</span><br> <span class="hljs-attr">push:</span><br> <span class="hljs-attr">branches:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">master</span><br><br><span class="hljs-attr">env:</span><br><span class="hljs-attr">GIT_USER:</span> <span class="hljs-string">your-github-username</span><br><span class="hljs-attr">GIT_EMAIL:</span> <span class="hljs-string">your-github-email</span><br><span class="hljs-attr">THEME_NAME:</span> <span class="hljs-string">hexo-theme-fluid</span><br><span class="hljs-attr">DEPLOY_REPO:</span> <span class="hljs-string">username/username.github.io</span><br><span class="hljs-attr">DEPLOY_BRANCH:</span> <span class="hljs-string">master</span><br><br><span class="hljs-attr">jobs:</span><br><span class="hljs-attr">build:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">on</span> <span class="hljs-string">node</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node_version</span> <span class="hljs-string">}}</span> <span class="hljs-string">and</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.os</span> <span class="hljs-string">}}</span><br> <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span><br> <span class="hljs-attr">strategy:</span><br> <span class="hljs-attr">matrix:</span><br> <span class="hljs-attr">os:</span> [<span class="hljs-string">ubuntu-latest</span>]<br> <span class="hljs-attr">node_version:</span> [<span class="hljs-number">14.</span><span class="hljs-string">x</span>]<br><br> <span class="hljs-attr">steps:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">ref:</span> <span class="hljs-string">master</span><br><br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">deploy</span> <span class="hljs-string">repo</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">repository:</span> <span class="hljs-string">${{</span> <span class="hljs-string">env.DEPLOY_REPO</span> <span class="hljs-string">}}</span><br> <span class="hljs-attr">ref:</span> <span class="hljs-string">${{</span> <span class="hljs-string">env.DEPLOY_BRANCH</span> <span class="hljs-string">}}</span><br> <span class="hljs-attr">path:</span> <span class="hljs-string">.deploy_git</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node_version</span> <span class="hljs-string">}}</span><br> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span><br> <span class="hljs-attr">with:</span><br> <span class="hljs-attr">node-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node_version</span> <span class="hljs-string">}}</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configuration</span> <span class="hljs-string">environment</span><br> <span class="hljs-attr">env:</span><br> <span class="hljs-attr">HEXO_DEPLOY_PRI:</span> <span class="hljs-string">${{secrets.HEXO_DEPLOY_PRI}}</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string"> sudo timedatectl set-timezone "Asia/Shanghai"</span><br><span class="hljs-string"> mkdir -p ~/.ssh/</span><br><span class="hljs-string"> echo "$HEXO_DEPLOY_PRI" > ~/.ssh/id_rsa</span><br><span class="hljs-string"> chmod 600 ~/.ssh/id_rsa</span><br><span class="hljs-string"> ssh-keyscan github.com >> ~/.ssh/known_hosts</span><br><span class="hljs-string"> git config --global user.name $GIT_USER</span><br><span class="hljs-string"> git config --global user.email $GIT_EMAIL</span><br><span class="hljs-string"></span> <br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span> <span class="hljs-string">and</span> <span class="hljs-string">theme</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br><span class="hljs-string"> npm install</span><br><span class="hljs-string"> npm install --save ${{ env.THEME_NAME }}</span><br><span class="hljs-string"></span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">hexo</span><br> <span class="hljs-attr">run:</span> <span class="hljs-string">|</span><br> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">deploy</span><br></code></pre></td></tr></table></figure></li><li><p>整理Hexo目录,推送更改到<code>my-blog</code>仓库<br><code>.gitignore</code>中应该包含对 <code>public/</code>, <code>.deploy_git/</code> 等由于Hexo生成静态网页的文件夹的排除</p></li><li><p>查看部署结果<br>查看<code>my-blog</code>仓库中Actions,可以看到本次推送触发Actions的执行结果。<br><img src="/img/hexo-ci/action-result.png"></p></li></ol><p><strong>模板参数说明</strong></p><ul><li><code>name</code> 为此 Action 的名字</li><li><code>on</code> 触发条件,目前含义为,当master分支收到推送后,触发Actions</li><li><code>env</code> 为一些环境变量,默认变量参考<a href="https://docs.github.com/cn/actions/reference/environment-variables">环境变量 on Github Docs</a></li><li><code>env.GIT_USER</code> 为 Hexo 编译后使用此 git 用户部署到仓库</li><li><code>env.GIT_EMAIL</code> 为 Hexo 编译后使用此 git 邮箱部署到仓库</li><li><code>env.THEME_NAME</code> 为您的 Hexo 所使用的主题的仓库,这里为 hexo-theme-fluid</li><li><code>env.DEPLOY_REPO</code> 为 Hexo 编译后要部署的仓库,写托管静态网页的仓库</li><li><code>env.DEPLOY_BRANCH</code> 为 Hexo 编译后要部署到的分支,如果是非master分支,参考<a href="https://hexo.io/zh-cn/docs/one-command-deployment#Git">Hexo Doc</a> 和<code>Github</code>仓库中<code>settings->pages->branch</code>处进行更改</li><li><code>jobs</code> 为此 Action 下的任务列表,具体语法参考<a href="https://docs.github.com/cn/actions/reference/workflow-syntax-for-github-actions">Job Vars on Github Docs</a></li><li><code>jobs.{job}.name</code> 任务名称</li><li><code>jobs.{job}.runs-on</code> 任务所需容器,可选值:ubuntu-latest、windows-latest、macos-latest。</li><li><code>jobs.{job}.steps.$.name</code> 步骤名,编译时会会以 LOG 形式输出。</li></ul><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ol><li><a href="https://sanonz.github.io/2020/deploy-a-hexo-blog-from-github-actions/">利用 Github Actions 自动部署 Hexo 博客</a></li><li><a href="https://printempw.github.io/use-github-actions-to-deploy-hexo-blog/">使用 GitHub Actions 自动部署 Hexo 博客</a></li><li><a href="https://docs.github.com/cn/actions/reference/workflow-syntax-for-github-actions">Github Actions Reference</a></li></ol>]]></content>
<tags>
<tag>CI</tag>
<tag>Github</tag>
<tag>Hexo</tag>
</tags>
</entry>
</search>