-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
351 lines (181 loc) · 152 KB
/
atom.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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>LJF的秘密基地</title>
<icon>https://fioepq9.cn/icon.png</icon>
<subtitle>fioepq9.cn</subtitle>
<link href="https://fioepq9.cn/atom.xml" rel="self"/>
<link href="https://fioepq9.cn/"/>
<updated>2022-12-29T15:16:49.822Z</updated>
<id>https://fioepq9.cn/</id>
<author>
<name>fioepq9</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>《GO 专家编程》阅读笔记</title>
<link href="https://fioepq9.cn/2022/12/04/%E3%80%8AGO-%E4%B8%93%E5%AE%B6%E7%BC%96%E7%A8%8B%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<id>https://fioepq9.cn/2022/12/04/%E3%80%8AGO-%E4%B8%93%E5%AE%B6%E7%BC%96%E7%A8%8B%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/</id>
<published>2022-12-04T11:41:57.000Z</published>
<updated>2022-12-29T15:16:49.822Z</updated>
<content type="html"><![CDATA[<h2 id="常见数据结构的实现原理"><a href="#常见数据结构的实现原理" class="headerlink" title="常见数据结构的实现原理"></a>常见数据结构的实现原理</h2><h3 id="chan"><a href="#chan" class="headerlink" title="chan"></a>chan</h3><h4 id="数据结构定义"><a href="#数据结构定义" class="headerlink" title="数据结构定义"></a>数据结构定义</h4><blockquote><p>src/runtime/chan.go:hchan</p></blockquote><figure class="highlight go"><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><span class="line"><span class="keyword">type</span> hchan <span class="keyword">struct</span> {</span><br><span class="line"> qcount <span class="type">uint</span> <span class="comment">// 当前队列中剩余元素个数</span></span><br><span class="line"> dataqsiz <span class="type">uint</span> <span class="comment">// 环形队列长度,即可以存放的元素个数</span></span><br><span class="line"> buf unsafe.Pointer <span class="comment">// 环形队列指针</span></span><br><span class="line"> elemsize <span class="type">uint16</span> <span class="comment">// 每个元素的大小</span></span><br><span class="line"> closed <span class="type">uint32</span> <span class="comment">// 标识关闭状态</span></span><br><span class="line"> elemtype *_type <span class="comment">// 元素类型</span></span><br><span class="line"> sendx <span class="type">uint</span> <span class="comment">// 队列下标,指示元素写入时存放到队列中的位置</span></span><br><span class="line"> recvx <span class="type">uint</span> <span class="comment">// 队列下标,指示元素从队列的该位置读出</span></span><br><span class="line"> recvq waitq <span class="comment">// 等待读消息的goroutine队列</span></span><br><span class="line"> sendq waitq <span class="comment">// 等待写消息的goroutine队列</span></span><br><span class="line"> lock mutex <span class="comment">// 互斥锁,chan不允许并发读写</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>channel 的实现是一个环形队列,它保存了当前队列的剩余元素个数,队列长度,队列指针,队列是否关闭的标志,读出、写入数据的两个下标,等待读消息或者写消息的两个 goroutine 队列,以及一个保证并发安全的互斥锁。</p><h4 id="从-chan-中读数据"><a href="#从-chan-中读数据" class="headerlink" title="从 chan 中读数据"></a>从 chan 中读数据</h4><ul><li>无缓冲的 chan</li></ul><ol><li>首先,判断等待写消息的 goroutine 队列是否为空。</li><li>如果不为空,就从这个队列中取出一个 goroutine,把其中的数据读出,然后把这个 goroutine 唤醒。</li><li>如果为空,就将当前的 goroutine 插入到等待读消息的 goroutine 队列中,当前 goroutine 进入睡眠,等待被唤醒。</li></ol><ul><li>有缓冲的 chan</li></ul><ol><li>首先,判断等待写消息的 goroutine 队列是否为空。</li><li>如果队列不为空,就说明缓冲区已经满了。从缓冲区首部读出一个数据,然后从等待写消息的 goroutine 队列中拿出一个 goroutine,将这个 goroutine 要写的数据写入到缓冲区尾部后唤醒这个 goroutine。</li><li>如果队列为空,且缓冲区不为空,就从缓冲区里取出数据。</li><li>如果缓冲区为空,就将当前的 goroutine 插入到等待读消息的 goroutine 队列中,当前 goroutine 进入睡眠,等待被唤醒。</li></ol><h4 id="往-chan-中写数据"><a href="#往-chan-中写数据" class="headerlink" title="往 chan 中写数据"></a>往 chan 中写数据</h4><ol><li>如果等待读消息的 goroutine 队列不为空,说明缓冲区中没有数据或者没有缓冲区,这个时候直接从 goroutine 队列中取出一个 goroutine,把数据写入后唤醒这个 goroutine。</li><li>如果缓冲区有空余位置,就将数据写入缓冲区。</li><li>如果缓冲区没有空余位置,就将待发送数据写入当前的 goroutine,然后将这个 goroutine 插入到等待写消息的 goroutine 队列中,进入睡眠,等待唤醒。</li></ol><h4 id="chan-的关闭"><a href="#chan-的关闭" class="headerlink" title="chan 的关闭"></a>chan 的关闭</h4><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">0</span>) <span class="comment">// 创建一个无缓冲的 chan</span></span><br><span class="line"><span class="built_in">close</span>(ch) <span class="comment">// 关闭 chan</span></span><br></pre></td></tr></table></figure><h5 id="chan-的关闭会发生什么?"><a href="#chan-的关闭会发生什么?" class="headerlink" title="chan 的关闭会发生什么?"></a>chan 的关闭会发生什么?</h5><p>将等待读、写消息的 goroutine 队列中的 goroutine 全部唤醒,读消息的 goroutine 得到类型零值,写消息的 goroutine 会 panic。</p><blockquote><p>关闭值为 nil 的 chan 或者关闭已经被关闭过的 chan 都会导致当前 goroutine 的 panic。<br>向已经关闭的 chan 读数据,会读到类型零值。<br>向已经关闭的 chan 写数据,会导致当前 goroutine 的 panic。</p></blockquote><h4 id="常见用法"><a href="#常见用法" class="headerlink" title="常见用法"></a>常见用法</h4><h5 id="单向的-chan"><a href="#单向的-chan" class="headerlink" title="单向的 chan"></a>单向的 chan</h5><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">readChan</span><span class="params">(chanName <-<span class="keyword">chan</span> <span class="type">int</span>)</span></span> <span class="comment">// 只允许读取的 chan 声明</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">writeChan</span><span class="params">(chanName <span class="keyword">chan</span><- <span class="type">int</span>)</span></span> <span class="comment">// 只允许写入的 chan 声明</span></span><br></pre></td></tr></table></figure><h5 id="select"><a href="#select" class="headerlink" title="select"></a>select</h5><p>使用 select 监听多个 chan。</p><figure class="highlight go"><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><span class="line"><span class="keyword">select</span> {</span><br><span class="line"><span class="keyword">case</span> x := <- ch1:</span><br><span class="line"> fmt.Println(x)</span><br><span class="line"><span class="keyword">case</span> y := <- ch2:</span><br><span class="line"> fmt.Println(y)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">"nothing"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="range"><a href="#range" class="headerlink" title="range"></a>range</h5><p>通过 range 从 chan 中持续读出数据。</p><blockquote><p>如果向 chan 写入数据的 goroutine 退出时,系统检测到这种情况后,使用 range 的 goroutine 会 panic。(防止 range 的永久阻塞)</p></blockquote><figure class="highlight go"><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><span class="line"><span class="keyword">for</span> x := <span class="keyword">range</span> ch {</span><br><span class="line"> fmt.Println(x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">《GO 专家编程》的个人阅读笔记,不定时更新。</summary>
<content src="https://fioepq9.cn/img/pixiv_37855635.jpg" type="image"/>
<category term="golang" scheme="https://fioepq9.cn/categories/golang/"/>
<category term="golang" scheme="https://fioepq9.cn/tags/golang/"/>
<category term="阅读笔记" scheme="https://fioepq9.cn/tags/%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>leetcode-19 删除链表的倒数第 N 个结点</title>
<link href="https://fioepq9.cn/2022/11/23/leetcode-19-%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%AC-N-%E4%B8%AA%E7%BB%93%E7%82%B9/"/>
<id>https://fioepq9.cn/2022/11/23/leetcode-19-%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%AC-N-%E4%B8%AA%E7%BB%93%E7%82%B9/</id>
<published>2022-11-23T13:44:54.000Z</published>
<updated>2022-12-29T15:16:49.822Z</updated>
<content type="html"><![CDATA[<blockquote><p><a href="https://leetcode.cn/problems/remove-nth-node-from-end-of-list/">https://leetcode.cn/problems/remove-nth-node-from-end-of-list/</a></p></blockquote><h2 id="原题"><a href="#原题" class="headerlink" title="原题"></a>原题</h2><p>给你一个链表,删除链表的倒数第 <code>n</code> 个结点,并且返回链表的头结点。</p><p> <strong>示例 1:</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:head = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>], n = <span class="number">2</span></span><br><span class="line">输出:[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">5</span>]</span><br></pre></td></tr></table></figure><p> <strong>示例 2:</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:head = [<span class="number">1</span>], n = <span class="number">1</span></span><br><span class="line">输出:[]</span><br></pre></td></tr></table></figure><p> <strong>示例 3:</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:head = [<span class="number">1</span>,<span class="number">2</span>], n = <span class="number">1</span></span><br><span class="line">输出:[<span class="number">1</span>]</span><br></pre></td></tr></table></figure><p> <strong>提示:</strong></p><ul><li>链表中结点的数目为 <code>sz</code></li><li><code>1 <= sz <= 30</code></li><li><code>0 <= Node.val <= 100</code></li><li><code>1 <= n <= sz</code></li></ul><h2 id="解题思路"><a href="#解题思路" class="headerlink" title="解题思路"></a>解题思路</h2><p>该题的关键在于获取到要删除结点的前一个结点。</p><blockquote><p>考虑到要删除的结点可能为头结点,建议定义一个哑结点作虚拟头结点来简化处理。</p></blockquote><p>两个结点都从头结点出发,一个结点先走 n 步,然后两个结点同时前进,当第一个结点走到末尾时,第二个结点即为要删除结点。</p><h2 id="答案"><a href="#答案" class="headerlink" title="答案"></a>答案</h2><figure class="highlight go"><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><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * type ListNode struct {</span></span><br><span class="line"><span class="comment"> * Val int</span></span><br><span class="line"><span class="comment"> * Next *ListNode</span></span><br><span class="line"><span class="comment"> * }</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">removeNthFromEnd</span><span class="params">(head *ListNode, n <span class="type">int</span>)</span></span> *ListNode {</span><br><span class="line"> dummy := <span class="built_in">new</span>(ListNode)</span><br><span class="line"> dummy.Next = head</span><br><span class="line"> fir, sec := dummy, dummy</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i <= n && fir != <span class="literal">nil</span>; i++ {</span><br><span class="line"> fir = fir.Next</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> fir != <span class="literal">nil</span> {</span><br><span class="line"> fir = fir.Next</span><br><span class="line"> sec = sec.Next</span><br><span class="line"> }</span><br><span class="line"> sec.Next = sec.Next.Next</span><br><span class="line"> <span class="keyword">return</span> dummy.Next</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote></blockquote>]]></content>
<summary type="html"><blockquote>
<p><a</summary>
<content src="https://fioepq9.cn/img/pixiv_34844544.jpg" type="image"/>
<category term="leetcode" scheme="https://fioepq9.cn/categories/leetcode/"/>
<category term="leetcode" scheme="https://fioepq9.cn/tags/leetcode/"/>
<category term="链表" scheme="https://fioepq9.cn/tags/%E9%93%BE%E8%A1%A8/"/>
</entry>
<entry>
<title>leetcode-2 两数相加</title>
<link href="https://fioepq9.cn/2022/11/23/leetcode-2-%E4%B8%A4%E6%95%B0%E7%9B%B8%E5%8A%A0/"/>
<id>https://fioepq9.cn/2022/11/23/leetcode-2-%E4%B8%A4%E6%95%B0%E7%9B%B8%E5%8A%A0/</id>
<published>2022-11-23T09:58:30.000Z</published>
<updated>2022-12-29T15:16:49.822Z</updated>
<content type="html"><![CDATA[<blockquote><p><a href="https://leetcode.cn/problems/add-two-numbers">https://leetcode.cn/problems/add-two-numbers</a></p></blockquote><h2 id="原题"><a href="#原题" class="headerlink" title="原题"></a>原题</h2><p>给你两个 <strong>非空</strong> 的链表,表示两个非负的整数。它们每位数字都是按照 <strong>逆序</strong> 的方式存储的,并且每个节点只能存储 <strong>一位</strong> 数字。</p><p>请你将两个数相加,并以相同形式返回一个表示和的链表。</p><p>你可以假设除了数字 0 之外,这两个数都不会以 0 开头。</p><p> <strong>示例 1:</strong></p><figure class="highlight go"><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><span class="line">输入:l1 = [<span class="number">2</span>,<span class="number">4</span>,<span class="number">3</span>], l2 = [<span class="number">5</span>,<span class="number">6</span>,<span class="number">4</span>]</span><br><span class="line">输出:[<span class="number">7</span>,<span class="number">0</span>,<span class="number">8</span>]</span><br><span class="line">解释:<span class="number">342</span> + <span class="number">465</span> = <span class="number">807.</span></span><br></pre></td></tr></table></figure><p> <strong>示例 2:</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:l1 = [<span class="number">0</span>], l2 = [<span class="number">0</span>]</span><br><span class="line">输出:[<span class="number">0</span>]</span><br></pre></td></tr></table></figure><p> <strong>示例 3:</strong></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入:l1 = [<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>], l2 = [<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>]</span><br><span class="line">输出:[<span class="number">8</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">9</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>]</span><br></pre></td></tr></table></figure><p> <strong>提示:</strong></p><ul><li>每个链表中的节点数在范围 <code>[1, 100]</code> 内</li><li><code>0 <= Node.val <= 9</code></li><li>题目数据保证列表表示的数字不含前导零</li></ul><h2 id="答案"><a href="#答案" class="headerlink" title="答案"></a>答案</h2><figure class="highlight go"><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><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * type ListNode struct {</span></span><br><span class="line"><span class="comment"> * Val int</span></span><br><span class="line"><span class="comment"> * Next *ListNode</span></span><br><span class="line"><span class="comment"> * }</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">addTwoNumbers</span><span class="params">(l1 *ListNode, l2 *ListNode)</span></span> *ListNode {</span><br><span class="line"> dummy := <span class="built_in">new</span>(ListNode)</span><br><span class="line"> <span class="keyword">var</span> carry <span class="type">int</span></span><br><span class="line"> pop := <span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">int</span> {</span><br><span class="line"> <span class="keyword">var</span> ret <span class="type">int</span></span><br><span class="line"> <span class="keyword">if</span> l1 != <span class="literal">nil</span> {</span><br><span class="line"> ret += l1.Val</span><br><span class="line"> l1 = l1.Next</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> l2 != <span class="literal">nil</span> {</span><br><span class="line"> ret += l2.Val</span><br><span class="line"> l2 = l2.Next</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ret + carry</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> node := dummy; l1 != <span class="literal">nil</span> || l2 != <span class="literal">nil</span> || carry != <span class="number">0</span>; node = node.Next {</span><br><span class="line"> node.Next = <span class="built_in">new</span>(ListNode)</span><br><span class="line"> value := pop()</span><br><span class="line"> node.Next.Val, carry = value % <span class="number">10</span>, value / <span class="number">10</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dummy.Next</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><blockquote>
<p><a</summary>
<content src="https://fioepq9.cn/img/pixiv_35782590.jpg" type="image"/>
<category term="leetcode" scheme="https://fioepq9.cn/categories/leetcode/"/>
<category term="leetcode" scheme="https://fioepq9.cn/tags/leetcode/"/>
<category term="链表" scheme="https://fioepq9.cn/tags/%E9%93%BE%E8%A1%A8/"/>
</entry>
<entry>
<title>k8s 重要概念</title>
<link href="https://fioepq9.cn/2022/11/22/k8s-%E9%87%8D%E8%A6%81%E6%A6%82%E5%BF%B5/"/>
<id>https://fioepq9.cn/2022/11/22/k8s-%E9%87%8D%E8%A6%81%E6%A6%82%E5%BF%B5/</id>
<published>2022-11-21T17:23:57.000Z</published>
<updated>2022-12-29T15:16:49.821Z</updated>
<content type="html"><![CDATA[<h2 id="Cluster"><a href="#Cluster" class="headerlink" title="Cluster"></a>Cluster</h2><p>Cluster 是计算、存储和网络资源的集合。</p><h2 id="Master"><a href="#Master" class="headerlink" title="Master"></a>Master</h2><p>Master 是 Cluster 的大脑,它的主要职责是调度,即决定将应用放在哪里运行。</p><p>为了实现高可用,可以运行多个 Master。</p><h2 id="Node"><a href="#Node" class="headerlink" title="Node"></a>Node</h2><p>Node 的职责是运行容器应用。</p><p>Node 由 Master 管理,Node 负责监控并汇报容器的状态,同时根据 Master 的要求管理容器的生命周期。</p><h2 id="Pod"><a href="#Pod" class="headerlink" title="Pod"></a>Pod</h2><p>Pod 是 Kubernetes 的最小工作单元。</p><p>每个 Pod 包含一个或多个容器。Pod 中的容器会作为一个整体被 Master 调度到一个 Node 上运行。</p><h3 id="Kubernetes-引入-Pod-的目的"><a href="#Kubernetes-引入-Pod-的目的" class="headerlink" title="Kubernetes 引入 Pod 的目的"></a>Kubernetes 引入 Pod 的目的</h3><ol><li>可管理性</li></ol><p>Pod 提供了比容器更高层次的抽象,将它们封装到一个部署单元中。</p><p>Kubernetes 以 Pod 为最小单位进行调度、扩展、共享资源、管理生命周期。</p><ol start="2"><li>通信和资源共享</li></ol><p>Pod 中的所有容器使用同一个网络 namespace,即相同的 IP 地址和 Port 空间。(容器之间可以直接使用 localhost 通信)</p><p>Pod 中的所有容器共享 volume。(当 Kubernetes 挂载 volume 到 Pod, 本质上是将 volume 挂载到 Pod 中的每一个容器)</p><h3 id="哪些容器应该放到一个-Pod-中?"><a href="#哪些容器应该放到一个-Pod-中?" class="headerlink" title="哪些容器应该放到一个 Pod 中?"></a>哪些容器应该放到一个 Pod 中?</h3><p>这些容器联系必须非常紧密,而且需要直接共享资源。(例如:需要直接共享存储的情况)</p><h2 id="Controller"><a href="#Controller" class="headerlink" title="Controller"></a>Controller</h2><p>Controller 是用来管理 Pod 的,它定义了 Pod 的部署特性。</p><p>Kubernetes 提供了多种 Controller,包括 Deployment、ReplicaSet、DaemonSet、StatefulSet、Job 等,以满足不同的业务场景。</p><h3 id="Deployment"><a href="#Deployment" class="headerlink" title="Deployment"></a>Deployment</h3><p>Deployment 是最常用的 Controller,它(通过 ReplicaSet)管理 Pod 的多个副本,并确保 Pod 按照期望的状态运行。</p><h3 id="ReplicaSet"><a href="#ReplicaSet" class="headerlink" title="ReplicaSet"></a>ReplicaSet</h3><p>ReplicaSet 实现了 Pod 的多副本管理。(使用 Deployment 时会自动创建 ReplicaSet,通常不需要直接使用 ReplicaSet)</p><h3 id="DaemonSet"><a href="#DaemonSet" class="headerlink" title="DaemonSet"></a>DaemonSet</h3><p>DaemonSet 用于每个 Node 最多只运行一个 Pod 副本的场景。</p><p>DaemonSet 通常用于运行 daemon。</p><h3 id="StatefulSet"><a href="#StatefulSet" class="headerlink" title="StatefulSet"></a>StatefulSet</h3><p>StatefulSet 能够保证 Pod 的每个副本在整个生命周期中名称是不变的,同时会保证副本按照固定的顺序启动、更新或者删除。</p><h2 id="Service"><a href="#Service" class="headerlink" title="Service"></a>Service</h2><p>Service 定义了外界访问一组特定 Pod 的方式,Service 拥有自己的 IP 和端口,为 Pod 提供了负载均衡。</p><h2 id="Namespace"><a href="#Namespace" class="headerlink" title="Namespace"></a>Namespace</h2><p>Namespace 可以将一个物理的 Cluster 逻辑上划分成多个虚拟 Cluster,每个 Cluster 就是一个 Namespace。</p><p>不同 Namespace 里的 Resource 是完全隔离的。</p><blockquote><p>Kubernetes 默认创建了两个 Namespace:</p><ol><li>default: 创建资源时如果不指定,将被放到这个 Namespace 中。</li><li>kube-system: Kubernetes 自己创建的系统资源将放到这个 Namespace 中。</li></ol></blockquote>]]></content>
<summary type="html">介绍 k8s 的一些基本概念。</summary>
<content src="https://fioepq9.cn/img/pixiv_48466332.png" type="image"/>
<category term="kubernetes" scheme="https://fioepq9.cn/categories/kubernetes/"/>
<category term="k8s" scheme="https://fioepq9.cn/tags/k8s/"/>
<category term="kubernetes" scheme="https://fioepq9.cn/tags/kubernetes/"/>
</entry>
<entry>
<title>bore - Rust 编写的流量转发工具</title>
<link href="https://fioepq9.cn/2022/11/22/bore-Rust-%E7%BC%96%E5%86%99%E7%9A%84%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
<id>https://fioepq9.cn/2022/11/22/bore-Rust-%E7%BC%96%E5%86%99%E7%9A%84%E6%B5%81%E9%87%8F%E8%BD%AC%E5%8F%91%E5%B7%A5%E5%85%B7/</id>
<published>2022-11-21T17:20:38.000Z</published>
<updated>2022-12-29T15:16:49.820Z</updated>
<content type="html"><![CDATA[<blockquote><p>GitHub: <a href="https://github.com/ekzhang/bore">https://github.com/ekzhang/bore</a></p></blockquote><h2 id="前置"><a href="#前置" class="headerlink" title="前置"></a>前置</h2><ol><li>本地机器</li><li>远程机器(服务器)</li><li>本地机器与远程机器可通过 ssh 连通</li></ol><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="是什么?"><a href="#是什么?" class="headerlink" title="是什么?"></a>是什么?</h3><p>bore 是一个使用 Rust 编写的现代化、简单的 TCP 隧道工具。</p><h3 id="能干什么?"><a href="#能干什么?" class="headerlink" title="能干什么?"></a>能干什么?</h3><p>它可以把你的本地端口 A 暴露到服务器的端口 B 上。</p><p>暨访问 < 服务器:B > 时,即在访问 < 本地:A > 的服务。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><blockquote><p>官方:<a href="https://github.com/ekzhang/bore#installation">https://github.com/ekzhang/bore#installation</a></p></blockquote><h3 id="本机-MacOS"><a href="#本机-MacOS" class="headerlink" title="本机(MacOS)"></a>本机(MacOS)</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install ekzhang/bore/bore</span><br></pre></td></tr></table></figure><h3 id="服务器-linux-x86-64"><a href="#服务器-linux-x86-64" class="headerlink" title="服务器(linux x86-64)"></a>服务器(linux x86-64)</h3><p>在 <a href="https://github.com/ekzhang/bore/releases">Release 页面</a> 找到对应的二进制压缩包。</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><span class="line"><span class="comment"># download</span></span><br><span class="line">curl -OL https://github.com/ekzhang/bore/releases/download/v0.4.0/bore-v0.4.0-x86_64-unknown-linux-musl.tar.gz</span><br><span class="line"><span class="comment"># tar</span></span><br><span class="line">tar -zxvf bore-v0.4.0-x86_64-unknown-linux-musl.tar.gz</span><br><span class="line"><span class="comment"># move to PATH</span></span><br><span class="line"><span class="built_in">mv</span> bore /usr/local/bin/bore</span><br><span class="line"><span class="comment"># cleanup</span></span><br><span class="line"><span class="built_in">rm</span> -rf bore-v0.4.0-x86_64-unknown-linux-musl.tar.gz</span><br></pre></td></tr></table></figure><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="服务器"><a href="#服务器" class="headerlink" title="服务器"></a>服务器</h3><blockquote><p>防火墙开放端口</p><ul><li>7835: 用于按需创建新连接。初始化时,客户端在 TCP 控制端口上向服务器发送“Hello”消息,请求代理选定的远程端口。然后服务器以确认响应并开始侦听外部 TCP 连接。详情参阅:<a href="https://github.com/ekzhang/bore#protocol">https://github.com/ekzhang/bore#protocol</a></li><li>任意可用端口: 用于接收流量的端口,该端口的流量会转发到本地机器的端口上。</li></ul></blockquote><ol><li>启动 bore server</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bore server</span><br></pre></td></tr></table></figure><h3 id="本地机器"><a href="#本地机器" class="headerlink" title="本地机器"></a>本地机器</h3><ol><li>启动一个监听端口 8000 的服务(此处使用 docker 启动 nginx)。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -p 8000:80 nginx</span><br></pre></td></tr></table></figure><ol start="2"><li>启动 bore,将本地端口 8000 暴露到服务器的 8080 端口。</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bore <span class="built_in">local</span> 8000 -t <remote-ip> -p 8080</span><br></pre></td></tr></table></figure><h3 id="访问网页"><a href="#访问网页" class="headerlink" title="访问网页"></a>访问网页</h3><p>![image-20221021160954965](/Users/shoplazza/Library/Application Support/typora-user-images/image-20221021160954965.png)</p>]]></content>
<summary type="html">介绍 bore 的基本用法。</summary>
<content src="https://fioepq9.cn/img/pixiv_68296699.png" type="image"/>
<category term="tool" scheme="https://fioepq9.cn/categories/tool/"/>
<category term="tool" scheme="https://fioepq9.cn/tags/tool/"/>
<category term="rust" scheme="https://fioepq9.cn/tags/rust/"/>
</entry>
<entry>
<title>golang 面试题</title>
<link href="https://fioepq9.cn/2022/11/22/golang-%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<id>https://fioepq9.cn/2022/11/22/golang-%E9%9D%A2%E8%AF%95%E9%A2%98/</id>
<published>2022-11-21T17:17:28.000Z</published>
<updated>2022-12-29T15:16:49.821Z</updated>
<content type="html"><![CDATA[<h2 id="内置类型"><a href="#内置类型" class="headerlink" title="内置类型"></a>内置类型</h2><p><strong>string</strong>、bool、数值类、struct、func、数组、<strong>slice</strong>、<strong>map</strong>、<strong>chan</strong>、<strong>interface</strong></p><h3 id="string-的底层实现?"><a href="#string-的底层实现?" class="headerlink" title="string 的底层实现?"></a>string 的底层实现?</h3><p>底层是一个字符串指针加上一个 cap。是一种不可变类型,每次更换内容,实际是更换底层指针。</p><h3 id="slice-的特点"><a href="#slice-的特点" class="headerlink" title="slice 的特点"></a>slice 的特点</h3><ul><li>每个 slice 都指向一个底层数组。</li><li>使用<code>len()</code>计算 slice 长度的时间复杂度为 O(1)。</li><li>使用<code>cap()</code>计算 slice 容量的时间复杂度为 O(1)。</li><li>通过函数传递 slice 时,不会拷贝底层数组。</li><li>通过<code>append()</code>向切片追加元素时可能触发扩容。</li></ul><h4 id="slice-的扩容机制?"><a href="#slice-的扩容机制?" class="headerlink" title="slice 的扩容机制?"></a>slice 的扩容机制?</h4><ol><li>如果原 slice 的容量小于 1024,则新 slice 的容量扩大为原来的 2 倍。</li><li>如果原 slice 的容量大于等于 1024,则新 slice 的容量扩大为原来的 1.25 倍。</li></ol><h3 id="map-什么时候会触发扩容?"><a href="#map-什么时候会触发扩容?" class="headerlink" title="map 什么时候会触发扩容?"></a>map 什么时候会触发扩容?</h3><p>满足下列两个条件之一:</p><ul><li>负载因子 > 6.5 时,即平均每个 bucket 存储键值对达到 6.5 个以上。</li><li>overflow 数量 > $2^{15}$ 时,即 overflow 的数量超过 32768 时。</li></ul><h4 id="map-的扩容机制?"><a href="#map-的扩容机制?" class="headerlink" title="map 的扩容机制?"></a>map 的扩容机制?</h4><h5 id="增量扩容-负载因子过大触发"><a href="#增量扩容-负载因子过大触发" class="headerlink" title="增量扩容(负载因子过大触发)"></a>增量扩容(负载因子过大触发)</h5><ul><li>新建一个新的 buckets,容量为原来的 2 倍。</li><li>采用渐进式 rehash,在访问 map 时触发迁移。(每次搬运 2 个键值对)</li></ul><h5 id="等量扩容-overflow-数量过高触发"><a href="#等量扩容-overflow-数量过高触发" class="headerlink" title="等量扩容(overflow 数量过高触发)"></a>等量扩容(overflow 数量过高触发)</h5><ul><li>新建一个新的 buckets,容量跟原来一样。</li><li>采用渐进式 rehash,在访问 map 时触发迁移。(每次搬运 2 个键值对)</li></ul><h4 id="map-的缩容机制?"><a href="#map-的缩容机制?" class="headerlink" title="map 的缩容机制?"></a>map 的缩容机制?</h4><p>现在没有真正的缩容机制,要想实现缩容,只能手动新建一个 map 然后将旧 map 的键值对进行转移。</p><h3 id="interface-是什么?"><a href="#interface-是什么?" class="headerlink" title="interface 是什么?"></a>interface 是什么?</h3><p>interface 是一组方法签名,所有实现了该签名的类都可以赋值给这个接口类型的变量。</p><p>go 使用 interface 实现了「多态」和「泛型」。</p><h4 id="多态的好处?"><a href="#多态的好处?" class="headerlink" title="多态的好处?"></a>多态的好处?</h4><p>解耦了接口和实现类之间的关系,提高了代码的可维护性和灵活性。</p><h4 id="两个相同接口类型的变量是如何进行比较的?"><a href="#两个相同接口类型的变量是如何进行比较的?" class="headerlink" title="两个相同接口类型的变量是如何进行比较的?"></a>两个相同接口类型的变量是如何进行比较的?</h4><p>会同时比较内部的动态类型和动态值,只有都可比较且都相等的时候,才返回<code>true</code>。</p><h2 id="语言特性"><a href="#语言特性" class="headerlink" title="语言特性"></a>语言特性</h2><h3 id="go-和-c-x2F-c-的区别?"><a href="#go-和-c-x2F-c-的区别?" class="headerlink" title="go 和 c/c++的区别?"></a>go 和 c/c++的区别?</h3><ol><li>go 不支持隐式类型转换,c/c++ 支持。</li><li>go 要求统一的代码风格,并且有 gofmt 这个格式化工具。</li><li>go 通过标识符的首字母是否大写决定可见性,而 c/c++ 通过 private、public 等关键字。</li><li>go 支持多返回值,c/c++ 不支持。</li><li>go 支持匿名函数,c/c++ 只存在类似的 lambda 表达式。</li><li>go 在语言层面支持并发,c/c++ 需要调用外部库。</li><li>go 有自己的垃圾回收器,而 c/c++ 只能手动进行垃圾回收。</li></ol><h3 id="defer-的规则"><a href="#defer-的规则" class="headerlink" title="defer 的规则"></a>defer 的规则</h3><ol><li>延迟函数的参数在 defer 语句出现时就已经确定了。</li><li>延迟函数执行按后进先出顺序执行,即先出现的 defer 最后执行。</li><li>延迟函数可能操作使用 defer 语句的函数的具名返回值。</li></ol><h3 id="go-的多值返回是怎么实现的?"><a href="#go-的多值返回是怎么实现的?" class="headerlink" title="go 的多值返回是怎么实现的?"></a>go 的多值返回是怎么实现的?</h3><p>为了实现多值返回,go 是使用栈空间来返回值的。而常见的 C 语言是通过寄存器来返回值的。go 在调用函数的时候,会在栈中预留返回值的空位,在返回前将具体值赋值。</p><h3 id="for-range-遍历-slice-的过程中不断向-slice-进行-append,会死循环吗?"><a href="#for-range-遍历-slice-的过程中不断向-slice-进行-append,会死循环吗?" class="headerlink" title="for range 遍历 slice 的过程中不断向 slice 进行 append,会死循环吗?"></a>for range 遍历 slice 的过程中不断向 slice 进行 append,会死循环吗?</h3><p>不会。<code>for range</code>是 go 的一个语法糖,它在进入循环之前会先获取<code>len(slice)</code>,然后执行<code>len(slice)</code>次数的循环。</p><h2 id="底层实现"><a href="#底层实现" class="headerlink" title="底层实现"></a>底层实现</h2><h3 id="Golang-的-GC-算法了解吗?"><a href="#Golang-的-GC-算法了解吗?" class="headerlink" title="Golang 的 GC 算法了解吗?"></a>Golang 的 GC 算法了解吗?</h3><p>go 使用的是三色标记法,属于标记清除法。</p><p>这里的三色,对应了垃圾回收过程中对象的三种状态:灰色,代表对象在标记队列中;黑色,代表对象已被标记;白色,代表对象未被标记。</p><p>在垃圾回收开始时,会将根对象标记为灰色,也就是放入标记队列中,然后会从标记队列中取出一个灰色对象,将它引用的对象加入队列中后将它自身变为黑色,直到队列为空。最终黑色对象保留,白色对象被回收。三色标记法实际就是对根对象进行一次 BFS,最后没有遍历到的,即为需要回收垃圾。</p><h4 id="根对象是什么?"><a href="#根对象是什么?" class="headerlink" title="根对象是什么?"></a>根对象是什么?</h4><p>全局变量、执行栈中的变量、寄存器中的变量。</p><h4 id="没有-STW-会出现什么问题?"><a href="#没有-STW-会出现什么问题?" class="headerlink" title="没有 STW 会出现什么问题?"></a>没有 STW 会出现什么问题?</h4><p>内存误回收:考虑一个已经扫描完毕的黑色对象,让它引用一个新建的白色对象。最终这个白色对象会被回收。</p><h4 id="常见的垃圾回收算法"><a href="#常见的垃圾回收算法" class="headerlink" title="常见的垃圾回收算法"></a>常见的垃圾回收算法</h4><ol><li><p>引用计数法:对每个对象维护一个引用计数,当计数为 0 时回收该对象。</p><ul><li>优点:对象可以很快被回收,不会出现内存耗尽或达到某个阈值时才回收。</li><li>缺点:不能很好地处理循环引用,而且实时维护引用计数,空间开销比较大。</li></ul></li><li><p>标记清除法:从根变量开始遍历所有引用的对象并标记,最后回收所有没有被标记的对象。</p><ul><li>优点:可以处理循环引用,并且不用实时维护引用计数,减少了空间上的开销。</li><li>缺点:需要 STW,即暂停程序运行。</li></ul></li><li><p>分代收集法:按照对象生命周期长短划分,生命周期长的放入老年代,而短的放入新生代,不同代有不同的回收算法和回收频率。</p><ul><li>优点:回收性能好。</li><li>缺点:算法复杂。</li></ul></li></ol><h3 id="GMP-模型"><a href="#GMP-模型" class="headerlink" title="GMP 模型"></a>GMP 模型</h3><ul><li>G:指 goroutine。</li><li>M:工作线程,在 go 中被称为 Machine。</li><li>P:处理器,是 go 中定义的一个概念,它包含运行 go 代码的必要资源。</li></ul><h4 id="goroutine-调度策略"><a href="#goroutine-调度策略" class="headerlink" title="goroutine 调度策略"></a>goroutine 调度策略</h4><ul><li>每个 P 维护一个包含 G 的本地队列,P 周期性地从本地队列头部取出 G 调度到 M 中执行一小段时间后将上下文保存下来,然后将 G 放到本地队列尾部。除了本地队列以外,还有一个全局队列。每个 P 都会周期性地查看全局队列。如果一个 P 的本地队列中的 G 已全部执行完毕,那么会从全局队列中拿一批 G 放入本地队列;如果全局队列中也没有 G,那么它会尝试将其它 P 的本地队列中的 G 偷取一半放入自身的本地队列。</li><li>当 G 进入系统调用时,所属的 M 会释放 P,P 会被其它 M 获取(可能是空闲的 M,如果没有空闲的 M,那么新建一个 M),原本的 M 由于系统调用而阻塞。</li></ul><h3 id="内存逃逸"><a href="#内存逃逸" class="headerlink" title="内存逃逸"></a>内存逃逸</h3><p>在一段程序中,每一个函数都会有自己的内存区域存放自己的局部变量、返回地址等,这些内存会由编译器在栈中进行分配,每一个函数都会分配一个栈桢,在函数运行结束后进行销毁,但是有些变量我们想在函数运行结束后仍然使用它,那么就需要把这个变量在堆上分配,这种从”栈”上逃逸到”堆”上的现象就成为内存逃逸。</p><h4 id="逃逸策略"><a href="#逃逸策略" class="headerlink" title="逃逸策略"></a>逃逸策略</h4><ol><li>如果对象在函数外部没有引用,则优先放到栈中。(如果需要内存过大,则放到堆中)</li><li>如果对象在函数外部存在引用,则必定放到堆中。</li></ol>]]></content>
<summary type="html">收集的一些 go 语言方面的面试题。</summary>
<content src="https://fioepq9.cn/img/pixiv_74279906.png" type="image"/>
<category term="面试题" scheme="https://fioepq9.cn/categories/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="golang" scheme="https://fioepq9.cn/tags/golang/"/>
<category term="面试" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95/"/>
<category term="面试题" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
</entry>
<entry>
<title>k8s client-go 使用样例</title>
<link href="https://fioepq9.cn/2022/11/22/k8s-client-go-%E4%BD%BF%E7%94%A8%E6%A0%B7%E4%BE%8B/"/>
<id>https://fioepq9.cn/2022/11/22/k8s-client-go-%E4%BD%BF%E7%94%A8%E6%A0%B7%E4%BE%8B/</id>
<published>2022-11-21T17:12:36.000Z</published>
<updated>2022-12-29T15:16:49.821Z</updated>
<content type="html"><![CDATA[<h2 id="Pod"><a href="#Pod" class="headerlink" title="Pod"></a>Pod</h2><h3 id="连接-Terminal-websocket"><a href="#连接-Terminal-websocket" class="headerlink" title="连接 Terminal (websocket)"></a>连接 Terminal (websocket)</h3><figure class="highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"io"</span></span><br><span class="line"> <span class="string">"context"</span></span><br><span class="line"> <span class="string">"net/http"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"github.com/gorilla/websocket"</span></span><br><span class="line"> corev1 <span class="string">"k8s.io/api/core/v1"</span></span><br><span class="line"> metav1 <span class="string">"k8s.io/apimachinery/pkg/apis/meta/v1"</span></span><br><span class="line"> <span class="string">"k8s.io/client-go/kubernetes"</span></span><br><span class="line"> <span class="string">"k8s.io/client-go/kubernetes/scheme"</span></span><br><span class="line"> <span class="string">"k8s.io/client-go/tools/remotecommand"</span></span><br><span class="line"> <span class="string">"k8s.io/client-go/rest"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 默认已经初始化</span></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line"> RESTConfig *rest.Config</span><br><span class="line"> clientset *kubernetes.Clientset</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> PTYHandler <span class="keyword">interface</span>{</span><br><span class="line"> io.Reader <span class="comment">// stdin</span></span><br><span class="line"> io.Writer <span class="comment">// stdout, stderr</span></span><br><span class="line"> remotecommand.TerminalSizeQueue <span class="comment">// resizer</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Attach</span><span class="params">(ctx context.Context, namespace <span class="type">string</span>, name <span class="type">string</span>, containr <span class="type">string</span>, pty PTYHandler)</span></span> <span class="type">error</span> {</span><br><span class="line"> req := clientset.CoreV1().RESTClient().Post().</span><br><span class="line"> Resource(<span class="string">"pods"</span>).</span><br><span class="line"> Namespace(namespace).</span><br><span class="line"> Name(name).</span><br><span class="line"> SubResource(<span class="string">"exec"</span>).</span><br><span class="line"> VersionedParams(</span><br><span class="line"> &corev1.PodExecOptions{</span><br><span class="line"> <span class="comment">// +optional,</span></span><br><span class="line"> Container: container,</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> Command: []<span class="type">string</span>{<span class="string">"/bin/sh"</span>, <span class="string">"-c"</span>, <span class="string">"test /bin/bash && /bin/bash || /bin/sh"</span>},</span><br><span class="line"> Stdin: <span class="literal">true</span>,</span><br><span class="line"> Stdout: <span class="literal">true</span>,</span><br><span class="line"> Stderr: <span class="literal">true</span>,</span><br><span class="line"> TTY: <span class="literal">true</span>,</span><br><span class="line"> },</span><br><span class="line"> scheme.ParameterCodec,</span><br><span class="line"> )</span><br><span class="line"> executor, err := remotecommand.NewSPDYExecutor(RESTConfig, http.MethodPost, req.URL())</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> err</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> executor.Stream(remotecommand.StreamOptions{</span><br><span class="line"> Stdin: pty,</span><br><span class="line"> Stdout: pty,</span><br><span class="line"> Stderr: pty,</span><br><span class="line"> TerminalSizeQueue: pty,</span><br><span class="line"> Tty: <span class="literal">true</span>,</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> EOT = <span class="string">"\u0004"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// implement PTYHandler</span></span><br><span class="line"><span class="keyword">type</span> TerminalSession <span class="keyword">struct</span>{</span><br><span class="line"> ctx context.Context</span><br><span class="line"> ws *websocket.Conn</span><br><span class="line"> resizer <span class="keyword">chan</span> remotecommand.TerminalSize</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewTerminalSession</span><span class="params">(ctx context.Context, ws *websocket.Conn)</span></span> *TerminalSession {</span><br><span class="line"> <span class="keyword">return</span> &TerminalSession{</span><br><span class="line"> ctx: ctx,</span><br><span class="line"> ws: ws,</span><br><span class="line"> resizer: <span class="built_in">make</span>(<span class="keyword">chan</span> remotecommand.TerminalSize),</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// remotecommand.TerminalSizeQueue</span></span><br><span class="line"><span class="comment">// Next called in a loop from remotecommand as long as the process is running</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *TerminalSession)</span></span> Next() *remotecommand.TerminalSize {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> size := <-s.resizer:</span><br><span class="line"> <span class="keyword">return</span> &size</span><br><span class="line"> <span class="keyword">case</span> <-s.ctx.Done():</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// io.Reader</span></span><br><span class="line"><span class="comment">// Read called in a loop from remotecommand as long as the process is running</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *TerminalSession)</span></span> Read(p []<span class="type">byte</span>) (<span class="type">int</span>, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-s.ctx.Done():</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">copy</span>(p, EOT), <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> _, msg, err := s.ws.ReadMessage()</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">copy</span>(p, EOT), err</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 自行实现 isResizeMessage 函数</span></span><br><span class="line"> <span class="keyword">if</span> cols, rows, ok := isResizeMessage(msg); ok {</span><br><span class="line"> s.resizer <- remotecommand.TerminalSize{Width: cols, Height: rows}</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>, <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">copy</span>(p, msg), <span class="literal">nil</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// io.Writer</span></span><br><span class="line"><span class="comment">// Write called from remotecommand whenever there is any output</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *TerminalSession)</span></span> Write(p []<span class="type">byte</span>) (<span class="type">int</span>, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-s.ctx.Done():</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">len</span>(p), <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> err := s.ws.WriteMessage(websocket.TextMessage, p)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>, err</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">len</span>(p), <span class="literal">nil</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="连接日志"><a href="#连接日志" class="headerlink" title="连接日志"></a>连接日志</h3><figure class="highlight go"><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><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"io"</span></span><br><span class="line"> <span class="string">"context"</span></span><br><span class="line"></span><br><span class="line"> corev1 <span class="string">"k8s.io/api/core/v1"</span></span><br><span class="line"> metav1 <span class="string">"k8s.io/apimachinery/pkg/apis/meta/v1"</span></span><br><span class="line"> <span class="string">"k8s.io/client-go/kubernetes"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 默认已经初始化</span></span><br><span class="line"><span class="keyword">var</span> clientset *kubernetes.Clientset</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Attach</span><span class="params">(ctx context.Context, namespace <span class="type">string</span>, name <span class="type">string</span>, opt corev1.PodLogOptions)</span></span> (io.ReadCloser, <span class="type">error</span>) {</span><br><span class="line"> reader, err := clientset.CoreV1().Pods(namespace).GetLogs(name, &opt).Stream(ctx)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span>, err</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> reader</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Event"><a href="#Event" class="headerlink" title="Event"></a>Event</h2><h3 id="获取-Resource-的-Event-列表"><a href="#获取-Resource-的-Event-列表" class="headerlink" title="获取 Resource 的 Event 列表"></a>获取 Resource 的 Event 列表</h3><figure class="highlight go"><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><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"context"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"k8s.io/client-go/kubernetes"</span></span><br><span class="line"> corev1 <span class="string">"k8s.io/api/core/v1"</span></span><br><span class="line"> metav1 <span class="string">"k8s.io/apimachinery/pkg/apis/meta/v1"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 默认已经初始化</span></span><br><span class="line"><span class="keyword">var</span> clientset *kubernetes.Clientset</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">List</span><span class="params">(ctx context.Context, namespace <span class="type">string</span>, kind <span class="type">string</span>, name <span class="type">string</span>)</span></span> ([]corev1.Event, <span class="type">error</span>) {</span><br><span class="line"> eventList, err := clientset.CoreV1().</span><br><span class="line"> Events(namespace).</span><br><span class="line"> List(</span><br><span class="line"> ctx,</span><br><span class="line"> metav1.ListOptions{</span><br><span class="line"> FieldSelector: fmt.Sprintf(<span class="string">"involvedObject.name=%s"</span>, name),</span><br><span class="line"> TypeMeta: metav1.TypeMeta{Kind: kind},</span><br><span class="line"> },</span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">return</span> eventList.Items, err</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">k8s 的官方 sdk (client-go) 的使用样例。</summary>
<content src="https://fioepq9.cn/img/pixiv_29182021.jpg" type="image"/>
<category term="kubernetes" scheme="https://fioepq9.cn/categories/kubernetes/"/>
<category term="golang" scheme="https://fioepq9.cn/tags/golang/"/>
<category term="k8s" scheme="https://fioepq9.cn/tags/k8s/"/>
<category term="kubernetes" scheme="https://fioepq9.cn/tags/kubernetes/"/>
<category term="sdk" scheme="https://fioepq9.cn/tags/sdk/"/>
</entry>
<entry>
<title>python 常用刷题函数</title>
<link href="https://fioepq9.cn/2022/11/22/python-%E5%B8%B8%E7%94%A8%E5%88%B7%E9%A2%98%E5%87%BD%E6%95%B0/"/>
<id>https://fioepq9.cn/2022/11/22/python-%E5%B8%B8%E7%94%A8%E5%88%B7%E9%A2%98%E5%87%BD%E6%95%B0/</id>
<published>2022-11-21T17:09:48.000Z</published>
<updated>2022-12-29T15:16:49.822Z</updated>
<content type="html"><![CDATA[<h2 id="输入输出"><a href="#输入输出" class="headerlink" title="输入输出"></a>输入输出</h2><figure class="highlight python"><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><span class="line"><span class="comment"># 读取一行,去除头尾空格和换行,以空格划分读取为整数,最后放入list。</span></span><br><span class="line"><span class="built_in">slice</span> = <span class="built_in">list</span>(<span class="built_in">map</span>(<span class="built_in">int</span>, <span class="built_in">input</span>().strip().split()))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 不断读取,直到EOF</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">readUntilEof</span>():</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> <span class="keyword">yield</span> <span class="built_in">input</span>()</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"><span class="keyword">for</span> ipt <span class="keyword">in</span> readUntilEof():</span><br><span class="line"> <span class="comment"># do something</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 格式化字符串</span></span><br><span class="line"><span class="string">"{:0>2d}"</span>.<span class="built_in">format</span>(<span class="number">1</span>) <span class="comment"># 以0填充,右对齐2宽度</span></span><br><span class="line"><span class="string">"{:0<2d}"</span>.<span class="built_in">format</span>(<span class="number">1</span>) <span class="comment"># 以0填充,左对齐2宽度</span></span><br><span class="line"><span class="string">"{:^10d}"</span>.<span class="built_in">format</span>(<span class="number">1</span>) <span class="comment"># 居中10宽度</span></span><br><span class="line"><span class="string">"{0.value}"</span>.<span class="built_in">format</span>(myVal) <span class="comment"># 输出myVal的成员value</span></span><br><span class="line"><span class="string">"{:.2f}"</span>.<span class="built_in">format</span>(<span class="number">3.14159</span>) <span class="comment"># 保留2位小数</span></span><br><span class="line"><span class="string">"{:+.2f}"</span>.<span class="built_in">format</span>(<span class="number">3.14159</span>) <span class="comment"># 带符号保留2位小数</span></span><br><span class="line"><span class="string">"{:b}"</span>.<span class="built_in">format</span>(<span class="number">10</span>) <span class="comment"># 输出2进制,1010</span></span><br><span class="line"><span class="string">"{:d}"</span>.<span class="built_in">format</span>(<span class="number">10</span>) <span class="comment"># 输出10进制,10</span></span><br><span class="line"><span class="string">"{:o}"</span>.<span class="built_in">format</span>(<span class="number">10</span>) <span class="comment"># 输出8进制,12</span></span><br><span class="line"><span class="string">"{:x}"</span>.<span class="built_in">format</span>(<span class="number">10</span>) <span class="comment"># 输出16进制,a</span></span><br><span class="line"><span class="string">"{:#x}"</span>.<span class="built_in">format</span>(<span class="number">10</span>) <span class="comment"># 输出16进制,0xa</span></span><br><span class="line"><span class="string">"{:#X}"</span>.<span class="built_in">format</span>(<span class="number">10</span>) <span class="comment"># 输出16进制,0XA</span></span><br></pre></td></tr></table></figure><h2 id="内置函数"><a href="#内置函数" class="headerlink" title="内置函数"></a>内置函数</h2><figure class="highlight python"><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><span class="line"><span class="comment"># 进制</span></span><br><span class="line"><span class="built_in">bin</span>(number) <span class="comment"># 返回number的2进制表示</span></span><br><span class="line"><span class="built_in">oct</span>(number) <span class="comment"># 返回number的8进制表示</span></span><br><span class="line"><span class="built_in">hex</span>(number) <span class="comment"># 返回number的16进制表示</span></span><br><span class="line"><span class="comment"># 数学</span></span><br><span class="line"><span class="built_in">abs</span>(number) <span class="comment"># 返回number的绝对值</span></span><br><span class="line"><span class="built_in">pow</span>(x, y) <span class="comment"># 返回x的y次方</span></span><br><span class="line"><span class="comment"># 字符</span></span><br><span class="line"><span class="built_in">chr</span>(number) <span class="comment"># 将ASCII值转换为char</span></span><br><span class="line"><span class="built_in">ord</span>(char) <span class="comment"># 将char转换为ASCII值</span></span><br><span class="line"><span class="comment"># 遍历</span></span><br><span class="line"><span class="built_in">enumerate</span>(itertor) <span class="comment"># 返回索引,索引对应的元素</span></span><br><span class="line"><span class="built_in">zip</span>(iterable...) <span class="comment"># 同时遍历n个序列</span></span><br><span class="line"><span class="comment"># 语法糖</span></span><br><span class="line"><span class="built_in">sum</span>(number...) <span class="comment"># 求和</span></span><br><span class="line"><span class="built_in">max</span>(number...) <span class="comment"># 返回最大值</span></span><br><span class="line"><span class="built_in">min</span>(number...) <span class="comment"># 返回最小值</span></span><br><span class="line"><span class="built_in">range</span>(start, stop, [step]) <span class="comment"># 返回[start, stop)内以step为步长的序列</span></span><br><span class="line"><span class="built_in">map</span>(function, iterable) <span class="comment"># 对所有元素调用function并返回</span></span><br><span class="line"><span class="built_in">filter</span>(function, iterable) <span class="comment"># 过滤掉不符合条件的元素</span></span><br></pre></td></tr></table></figure><h2 id="常用库"><a href="#常用库" class="headerlink" title="常用库"></a>常用库</h2><figure class="highlight python"><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><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">import</span> collections</span><br><span class="line"><span class="keyword">import</span> heapq</span><br><span class="line"><span class="keyword">import</span> bisect</span><br></pre></td></tr></table></figure><h2 id="常用数据结构"><a href="#常用数据结构" class="headerlink" title="常用数据结构"></a>常用数据结构</h2><figure class="highlight python"><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><span class="line"><span class="comment"># 内置类型</span></span><br><span class="line"><span class="built_in">list</span>, <span class="built_in">tuple</span>, <span class="built_in">dict</span>, <span class="built_in">set</span></span><br><span class="line"><span class="comment"># collections库内结构</span></span><br><span class="line">collections.deque</span><br><span class="line">collections.Counter</span><br><span class="line">collections.OrderedDict</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">python 在刷算法题时常用的一些函数和库。</summary>
<content src="https://fioepq9.cn/img/pixiv_77186421.png" type="image"/>
<category term="python" scheme="https://fioepq9.cn/categories/python/"/>
<category term="python" scheme="https://fioepq9.cn/tags/python/"/>
<category term="数据结构" scheme="https://fioepq9.cn/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="算法" scheme="https://fioepq9.cn/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>二分查找</title>
<link href="https://fioepq9.cn/2022/11/22/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
<id>https://fioepq9.cn/2022/11/22/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</id>
<published>2022-11-21T16:59:44.000Z</published>
<updated>2022-12-29T15:16:49.822Z</updated>
<content type="html"><![CDATA[<p>二分查找是一种在有序序列中查找特定元素的搜索算法。优点是时间复杂度为 O(log n),缺点是只能在有序序列中使用。</p><h2 id="在升序数组-nums-中查找-target-的索引"><a href="#在升序数组-nums-中查找-target-的索引" class="headerlink" title="在升序数组 nums 中查找 target 的索引"></a>在升序数组 nums 中查找 target 的索引</h2><h3 id="搜索过程"><a href="#搜索过程" class="headerlink" title="搜索过程"></a>搜索过程</h3><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/blog-img/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.svg" alt="二分查找"></p><h3 id="实现代码(go)"><a href="#实现代码(go)" class="headerlink" title="实现代码(go)"></a>实现代码(go)</h3><figure class="highlight go"><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><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Search</span><span class="params">(nums []<span class="type">int</span>, target <span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> lo, hi := <span class="number">0</span>, <span class="built_in">len</span>(nums)<span class="number">-1</span></span><br><span class="line"> <span class="keyword">for</span> lo <= hi {</span><br><span class="line"> mid := (lo + hi) >> <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> target < nums[mid] {</span><br><span class="line"> hi = mid - <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> target > nums[mid] {</span><br><span class="line"> lo = mid + <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> mid</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="在升序数组-nums-中查找-target-的最小索引"><a href="#在升序数组-nums-中查找-target-的最小索引" class="headerlink" title="在升序数组 nums 中查找 target 的最小索引"></a>在升序数组 nums 中查找 target 的最小索引</h2><h3 id="搜索过程-1"><a href="#搜索过程-1" class="headerlink" title="搜索过程"></a>搜索过程</h3><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/blog-img/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E6%9C%80%E5%B0%8F%E7%B4%A2%E5%BC%95.svg" alt="二分查找最小索引"></p><h3 id="实现代码(go)-1"><a href="#实现代码(go)-1" class="headerlink" title="实现代码(go)"></a>实现代码(go)</h3><figure class="highlight go"><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><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">SearchLeft</span><span class="params">(nums []<span class="type">int</span>, target <span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> lo, hi := <span class="number">0</span>, <span class="built_in">len</span>(nums)<span class="number">-1</span></span><br><span class="line"> <span class="keyword">for</span> lo <= hi {</span><br><span class="line"> <span class="keyword">if</span> nums[lo] == target {</span><br><span class="line"> <span class="keyword">return</span> lo</span><br><span class="line"> }</span><br><span class="line"> mid := (lo + hi) >> <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> target < nums[mid] {</span><br><span class="line"> hi = mid - <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> target > nums[mid] {</span><br><span class="line"> lo = mid + <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> hi = mid</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="在升序数组-nums-中查找-target-的最大索引"><a href="#在升序数组-nums-中查找-target-的最大索引" class="headerlink" title="在升序数组 nums 中查找 target 的最大索引"></a>在升序数组 nums 中查找 target 的最大索引</h2><h3 id="搜索过程-2"><a href="#搜索过程-2" class="headerlink" title="搜索过程"></a>搜索过程</h3><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/blog-img/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE%E6%9C%80%E5%A4%A7%E7%B4%A2%E5%BC%95.svg" alt="二分查找最大索引"></p><h3 id="实现代码(go)-2"><a href="#实现代码(go)-2" class="headerlink" title="实现代码(go)"></a>实现代码(go)</h3><figure class="highlight go"><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><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">SearchRight</span><span class="params">(nums []<span class="type">int</span>, target <span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"> lo, hi := <span class="number">0</span>, <span class="built_in">len</span>(nums)<span class="number">-1</span></span><br><span class="line"> <span class="keyword">for</span> lo <= hi {</span><br><span class="line"> <span class="keyword">if</span> nums[hi] == target {</span><br><span class="line"> <span class="keyword">return</span> hi</span><br><span class="line"> }</span><br><span class="line"> mid := (lo+hi)>><span class="number">1</span> + (lo+hi)&<span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> target < nums[mid] {</span><br><span class="line"> hi = mid - <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> target > nums[mid] {</span><br><span class="line"> lo = mid + <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> lo = mid</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">二分查找是一种在有序序列中查找特定元素的搜索算法。优点是时间复杂度为 O(log n),缺点是只能在有序序列中使用。</summary>
<content src="https://fioepq9.cn/img/pixiv_60155475.png" type="image"/>
<category term="算法" scheme="https://fioepq9.cn/categories/%E7%AE%97%E6%B3%95/"/>
<category term="算法" scheme="https://fioepq9.cn/tags/%E7%AE%97%E6%B3%95/"/>
<category term="二分查找" scheme="https://fioepq9.cn/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
<category term="二分法" scheme="https://fioepq9.cn/tags/%E4%BA%8C%E5%88%86%E6%B3%95/"/>
</entry>
<entry>
<title>操作系统面试题</title>
<link href="https://fioepq9.cn/2022/11/22/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<id>https://fioepq9.cn/2022/11/22/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E9%9D%A2%E8%AF%95%E9%A2%98/</id>
<published>2022-11-21T16:54:15.000Z</published>
<updated>2022-12-29T15:16:49.823Z</updated>
<content type="html"><![CDATA[<h2 id="控制流"><a href="#控制流" class="headerlink" title="控制流"></a>控制流</h2><h3 id="进程、线程、协程之间的区别?"><a href="#进程、线程、协程之间的区别?" class="headerlink" title="进程、线程、协程之间的区别?"></a>进程、线程、协程之间的区别?</h3><ol><li><p>进程是一个运行中的程序实例,是 CPU 资源调度的基本单位;线程是运行在进程上下文的逻辑控制流,是程序执行的基本单位;协程是用户态的轻量级线程,是线程内部调度的基本单位。线程依赖于进程,一个进程里至少存在一个线程;协程依赖于线程,一个线程里至少存在一个协程。</p></li><li><p>进程持有的资源有:进程 ID、页表(提供了一个独立于其它进程的地址空间)、文件描述符表、代码段、数据段、环境变量、寄存器中的内容(通用寄存器、flag 寄存器、程序计数器)、内核栈、用户栈;</p><p>线程持有的资源有:线程 ID、寄存器中的内容(通用寄存器、flag 寄存器、程序计数器)、内核栈、用户栈;</p><p>协程持有的资源有:寄存器中的内容(通用寄存器、flag 寄存器、程序计数器)、用户栈。</p></li><li><p>切换开销上:进程 > 线程 > 协程。切换,实质上就是将当前上下文保存,然后取出新的上下文,显然持有的资源越多,切换的开销也就越大;同时,进程切换的时候还会刷新快表,导致快表缓存失效,也就导致新进程刚启动的时候运行速率较慢。进程、线程切换时都要陷入内核态,而协程不需要。进程、线程的切换由操作系统调度,而协程由用户自己调度。</p></li><li><p>通信方面上:进程间的通信主要依靠 IPC;而线程、协程间的通信,因为同一进程下的线程、协程共享内存,通信可以直接通过内存进行。</p></li></ol><h4 id="同一进程中的线程可以共享哪些数据?"><a href="#同一进程中的线程可以共享哪些数据?" class="headerlink" title="同一进程中的线程可以共享哪些数据?"></a>同一进程中的线程可以共享哪些数据?</h4><p>进程 ID、页表、文件描述符表、代码段、数据段、环境变量。</p><h4 id="线程独占哪些资源?"><a href="#线程独占哪些资源?" class="headerlink" title="线程独占哪些资源?"></a>线程独占哪些资源?</h4><p>线程 ID、寄存器中的内容、内核栈、用户栈。</p><h3 id="进程是如何进行切换的?"><a href="#进程是如何进行切换的?" class="headerlink" title="进程是如何进行切换的?"></a>进程是如何进行切换的?</h3><p>进程的切换是由操作系统进行的。操作系统首先会触发一个系统调用,进入到内核态;在内核态中,将当前进程的上下文保存,然后通过进程调度算法从阻塞态的进程队列中取出一个进程,恢复新进程的上下文,然后退出内核态,将控制权移交给新进程。</p><h4 id="进程的状态有哪些?"><a href="#进程的状态有哪些?" class="headerlink" title="进程的状态有哪些?"></a>进程的状态有哪些?</h4><h5 id="五态模型"><a href="#五态模型" class="headerlink" title="五态模型"></a>五态模型</h5><ul><li>新建态:进程刚刚被创建,等待系统完成创建进程的所有必要信息。</li><li>就绪态:一个进程得到了除控制权以外的所需资源,一旦得到控制权就可以执行。</li><li>运行态:一个进程在运行的过程中。</li><li>阻塞态:一个进程正在等待某一事件的发生,或者说等待一个信号而暂时停止运行。</li><li>终止态:进程已结束运行,回收除进程控制块(PCB)之后的其他资源,并等待其他进程从 PCB 中收集相关信息。</li></ul><h4 id="进程调度策略有哪些?"><a href="#进程调度策略有哪些?" class="headerlink" title="进程调度策略有哪些?"></a>进程调度策略有哪些?</h4><h5 id="批处理系统"><a href="#批处理系统" class="headerlink" title="批处理系统"></a>批处理系统</h5><ol><li>先来先服务(FCFS)</li></ol><p>按照请求的顺序进行调度。这是一种非抢占式的算法,开销小,没有饥饿问题。可能会导致短作业的响应时间过长。</p><ol start="2"><li>最短作业优先(SJF)</li></ol><p>按估计运行时间最短的顺序进行调度。也是一种非抢占式的算法,吞吐量高。但如果一直有短作业到来,会导致长作业的饥饿。</p><ol start="3"><li>最短剩余时间优先(SRTN)</li></ol><p>按估计剩余运行时间的顺序进行调度,是最短作业优先的抢占式版本,吞吐量高。跟最短作业优先有一样的问题,可能导致长作业饥饿。</p><h5 id="交互式系统"><a href="#交互式系统" class="headerlink" title="交互式系统"></a>交互式系统</h5><ol><li>时间片轮转(Round Robin)</li></ol><p>将所有就绪进程按 FCFS 的原则排成队列,用完时间片的进程回到队列尾部。这种算法很好地兼顾了响应时间和处理时间,但时间片的大小选择比较困难。如果时间片过小,进程切换频繁,开销太大;如果时间片太长,实时性就得不到保证。</p><ol start="2"><li>优先级调度算法</li></ol><p>为所有进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程得不到调度,可以随时间增加等待进程的优先级。</p><ol start="3"><li>多级反馈队列调度算法(Multilevel Feedback Queue)</li></ol><p>维护多个就绪进程队列,优先级递减,时间片递增。只有优先级更高的队列为空时才会调度低优先级队列中的队列。所有进程一开始都位于最高优先级队列,每次执行完一个时间片,就移动到下个队列。为了避免长作业饥饿问题,可以每隔一段时间,就重新将进程放入最高优先级队列。</p><h4 id="进程间通信的方式(IPC)?"><a href="#进程间通信的方式(IPC)?" class="headerlink" title="进程间通信的方式(IPC)?"></a>进程间通信的方式(IPC)?</h4><ol><li>管道(匿名管道、有名管道)</li><li>消息队列</li><li>共享内存</li><li>信号量</li><li>信号</li><li>套接字</li></ol><h5 id="讲讲你对同步和互斥的理解?"><a href="#讲讲你对同步和互斥的理解?" class="headerlink" title="讲讲你对同步和互斥的理解?"></a>讲讲你对同步和互斥的理解?</h5><ul><li>同步:多个控制流因为合作而使得控制流的执行有一定的先后顺序。</li><li>互斥:同一时间只有一个控制流能够执行。</li></ul><h3 id="什么是死锁?"><a href="#什么是死锁?" class="headerlink" title="什么是死锁?"></a>什么是死锁?</h3><p>多个控制流之间互相持有所需资源,并等待其它控制流释放资源,导致的循环等待的情况。</p><h4 id="死锁产生的必要条件?"><a href="#死锁产生的必要条件?" class="headerlink" title="死锁产生的必要条件?"></a>死锁产生的必要条件?</h4><ul><li>互斥:一个资源一次只能被一个控制流持有。</li><li>占有并等待:一个控制流至少占有一个资源,并在等待另一个被其它控制流占有的资源。</li><li>非抢占:已经分配给一个控制流的资源不能被强制性抢占,只能由控制流完成任务后自行释放。</li><li>循环等待:若干控制流之间形成一种头尾相接的环形等待资源关系,该环路中的每个控制流等在等待上一个控制流所占有的资源。</li></ul><h4 id="死锁的处理方法?"><a href="#死锁的处理方法?" class="headerlink" title="死锁的处理方法?"></a>死锁的处理方法?</h4><h5 id="鸵鸟策略"><a href="#鸵鸟策略" class="headerlink" title="鸵鸟策略"></a>鸵鸟策略</h5><p>直接忽略死锁。因为解决死锁的问题的代价很高,当发生死锁时不会对用户造成多大影响,或者发生死锁的概率很低时,可以采用鸵鸟策略。</p><h5 id="死锁预防"><a href="#死锁预防" class="headerlink" title="死锁预防"></a>死锁预防</h5><p>基本思想是破坏形成死锁的四个必要条件。</p><ul><li>破坏互斥条件:允许某些资源同时被多个控制流持有。这个做法的问题是有些资源本身不具有这种属性,实用性有限。</li><li>破坏占有并等待条件:实现资源预先分配策略,即一个控制执行任务之前,必须一次性申请所需资源,否则不允许。这个做法的问题是很多时候我们无法在执行前得知一个控制流具体需要哪些资源。同时这个也会降低资源的利用率,减少了并发量。</li><li>破坏非抢占条件:允许控制流强行抢占其它控制流占有的资源。抢占会带来很多额外的开销。</li><li>破坏循环等待条件:对资源进行编号,所有控制流对资源的申请必须按序提出,只有占有了低级资源的控制流才能申请高级资源。这样避免了占有高级资源的控制流再去申请低级资源,破坏了循环环路。</li></ul><h5 id="死锁避免"><a href="#死锁避免" class="headerlink" title="死锁避免"></a>死锁避免</h5><p>动态检查资源分配状态,确保系统处于安全状态,只有处于安全状态时才会进行资源的分配。安全状态指的是:存在某种对控制流的资源分配顺序,使得每一个控制流都能运行。</p><h5 id="死锁解除"><a href="#死锁解除" class="headerlink" title="死锁解除"></a>死锁解除</h5><p>当检测到死锁的时候,让某些控制流回滚到足以解除死锁的地步,控制流回滚时释放资源。这要求系统维护控制流的历史信息,并设置回滚点。</p><h3 id="虚拟内存是什么?"><a href="#虚拟内存是什么?" class="headerlink" title="虚拟内存是什么?"></a>虚拟内存是什么?</h3><p>虚拟内存是一种对真实物理内存的抽象,它将物理内存看成是磁盘的缓存,为每个进程提供了独立的地址空间,保护了每个进程的地址空间不被其他进程破坏。</p><p>将一个进程内的虚拟地址转化成对应的物理地址,叫做地址翻译。计算机的地址翻译是软硬件结合的,os 负责为每个进程维护页表,硬件 MMU 通过快表、页表执行地址翻译。MMU 如果从页表中发现该页不在内存中,就会触发缺页异常,根据页面置换算法从已缓存的页中选择一页进行置换(如果牺牲页被修改过,就复制其回磁盘)。</p><h4 id="虚拟内存的优点"><a href="#虚拟内存的优点" class="headerlink" title="虚拟内存的优点"></a>虚拟内存的优点</h4><ul><li>每个进程拥有独立的地址空间,这简化了内存管理,让进程间互不影响(通过在页表中设置许可位,提供访问控制)。</li><li>将物理内存扩充成了更大的逻辑内存。</li><li>通过将相应的虚拟页映射到同一物理页,可以实现共享库代码段。</li></ul><h4 id="页面置换算法有哪些?"><a href="#页面置换算法有哪些?" class="headerlink" title="页面置换算法有哪些?"></a>页面置换算法有哪些?</h4><ol><li>【无法实现】最佳置换法(OPT)</li></ol><p>每次选择淘汰的页面是以后永不使用,或者在最长时间内不再被访问的页面。</p><ol start="2"><li>先进先出置换法(FIFO)</li></ol><p>每次选择淘汰的页面是最早进入内存的页面。</p><ol start="3"><li>最近最久未使用置换法(LRU)</li></ol><p>每次选择淘汰的页面是最近最久未使用的页面。</p><ol start="4"><li>时钟置换法(Clock)</li></ol><p>是 LRU 算法的近似实现。维护位组成的循环队列,每个位代表一个页。当某页被访问时,其访问位置为 1。当需要淘汰一个页面时,只需检查页的访问位。如果是 0,就选择该页换出;如果是 1,则将它置为 0,暂不换出,继续检查下一个页面。</p><h4 id="抖动指的是什么现象?"><a href="#抖动指的是什么现象?" class="headerlink" title="抖动指的是什么现象?"></a>抖动指的是什么现象?</h4><p>指页面不断地换进换出,导致 CPU 利用率低下。通常成因是经常使用的内存大小超出了物理内存大小。</p><h4 id="碎片指的是什么现象?"><a href="#碎片指的是什么现象?" class="headerlink" title="碎片指的是什么现象?"></a>碎片指的是什么现象?</h4><ul><li>内部碎片:在一个已分配块比有效载荷大时发生的。也即分配了但不会被使用的空间。</li><li>外部碎片:当空闲内存合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以来处理这个请求时发生的。</li></ul><h2 id="硬件结构"><a href="#硬件结构" class="headerlink" title="硬件结构"></a>硬件结构</h2><h3 id="冯诺依曼模型由哪些部分组成?分别对应计算机的哪些硬件?"><a href="#冯诺依曼模型由哪些部分组成?分别对应计算机的哪些硬件?" class="headerlink" title="冯诺依曼模型由哪些部分组成?分别对应计算机的哪些硬件?"></a>冯诺依曼模型由哪些部分组成?分别对应计算机的哪些硬件?</h3><p>「运算器」、「控制器」、「存储器」、「输入设备」、「输出设备」。</p><ul><li><p>「运算器」对应 CPU 中的逻辑运算单元</p></li><li><p>「控制器」对应 CPU 中的控制单元。</p></li><li><p>「存储器」对应内存、磁盘、CPU 中的寄存器、L1/L2/L3 Cache 等存储设备。</p></li><li><p>「输入设备」对应键盘、鼠标等。</p></li><li><p>「输出设备」对应显示器等。</p></li></ul><h3 id="32-位和-64-位-CPU-的最主要区别是什么?"><a href="#32-位和-64-位-CPU-的最主要区别是什么?" class="headerlink" title="32 位和 64 位 CPU 的最主要区别是什么?"></a>32 位和 64 位 CPU 的最主要区别是什么?</h3><p>一次能计算多少字节的数据。</p><ul><li>32 位 CPU 一次可以计算 4 个字节。</li><li>64 位 CPU 一次可以计算 8 个字节。</li></ul><h3 id="为什么有了内存还需要寄存器?"><a href="#为什么有了内存还需要寄存器?" class="headerlink" title="为什么有了内存还需要寄存器?"></a>为什么有了内存还需要寄存器?</h3><p>内存离 CPU 的物理距离太远了,而寄存器在 CPU 中,且紧挨着 CPU 的控制单元和逻辑运算单元,运算速度更快。</p><h4 id="常见的寄存器种类有哪些?"><a href="#常见的寄存器种类有哪些?" class="headerlink" title="常见的寄存器种类有哪些?"></a>常见的寄存器种类有哪些?</h4><ul><li>「通用寄存器」,用来存放需要进行运算的数据。</li><li>「程序计数器」,用来存储 CPU 将要执行的下一条指令「所在的内存地址」。</li><li>「指令寄存器」,用来存放当前即将执行的指令,在指令执行完成之前,都存储在这里。</li></ul><h3 id="总线是用来干什么的?"><a href="#总线是用来干什么的?" class="headerlink" title="总线是用来干什么的?"></a>总线是用来干什么的?</h3><p>总线用于 CPU 和内存以及其他设备之间的通信。</p><h4 id="总线分为哪几种?"><a href="#总线分为哪几种?" class="headerlink" title="总线分为哪几种?"></a>总线分为哪几种?</h4><ul><li>「地址总线」,用于指定 CPU 将要操作的内存地址。</li><li>「数据总线」,用于读写内存的数据。</li><li>「控制总线」,用于发送和接收信号,比如中断、设备复位等信号。CPU 收到信号后进行响应时,也需要控制总线。</li></ul><h4 id="CPU-是如何通过总线读写内存数据的?"><a href="#CPU-是如何通过总线读写内存数据的?" class="headerlink" title="CPU 是如何通过总线读写内存数据的?"></a>CPU 是如何通过总线读写内存数据的?</h4><ol><li>首先要通过「地址总线」来指定内存的地址。</li><li>再通过「数据总线」来传输数据。</li></ol><h3 id="CPU-执行程序的过程?(一个指令周期,CPU-做了什么?)"><a href="#CPU-执行程序的过程?(一个指令周期,CPU-做了什么?)" class="headerlink" title="CPU 执行程序的过程?(一个指令周期,CPU 做了什么?)"></a>CPU 执行程序的过程?(一个指令周期,CPU 做了什么?)</h3><ol><li>CPU 读取「程序计数器」的值(即读取下一条指令的内存地址),然后 CPU 的「控制单元」操作「地址总线」指定需要访问的内存地址,接着通知内存设备准备数据,数据准备好后通过「数据总线」将指令传输给 CPU,CPU 收到数据后存入「指令寄存器」。</li><li>CPU 分析「指令寄存器」中的指令,确定指令的类型和参数:如果是计算类型的指令,就将指令交由「逻辑运算单元」运算;如果是存储类型的指令,则交由「控制单元」执行。</li><li>CPU 执行完指令后,「程序计数器」的值自增,执行下一条指令。自增的值的大小,由 CPU 的位宽决定,32 位 CPU 的指令需要 4 个字节存放,因此自增 4;而 64 位 CPU 则自增 8。</li></ol><h3 id="64-位-CPU-相比-32-位-CPU-的优势在哪?64-位-CPU-的计算性能一定比-32-位-CPU-高吗?"><a href="#64-位-CPU-相比-32-位-CPU-的优势在哪?64-位-CPU-的计算性能一定比-32-位-CPU-高吗?" class="headerlink" title="64 位 CPU 相比 32 位 CPU 的优势在哪?64 位 CPU 的计算性能一定比 32 位 CPU 高吗?"></a>64 位 CPU 相比 32 位 CPU 的优势在哪?64 位 CPU 的计算性能一定比 32 位 CPU 高吗?</h3><p>64 位 CPU 相比 32 位 CPU 的优势主要体现在两个方面:</p><ol><li>64 位 CPU 可以一次计算 32 位~64 位的数字,而 32 位 CPU 如果要计算超过 32 位的数字,就需要分多步骤进行计算,效率就没那么高。但是大部分应用程序很少会使用超过 32 位的数字。只有运算大数字的时候,64 位 CPU 的优势才能体现出来,否则和 32 位 CPU 的计算性能相差不大。</li><li>64 位 CPU 可以寻址更大的内存空间。32 位 CPU 最大的寻址地址是 4G=$2^{32}$,而 64 位 CPU 的最大寻址地址是 4E=$2^{64}$。</li></ol><h3 id="你知道软件的-32-位和-64-位之间的区别吗?32-位的软件可以运行在-64-位机器上吗?反过来呢?"><a href="#你知道软件的-32-位和-64-位之间的区别吗?32-位的软件可以运行在-64-位机器上吗?反过来呢?" class="headerlink" title="你知道软件的 32 位和 64 位之间的区别吗?32 位的软件可以运行在 64 位机器上吗?反过来呢?"></a>你知道软件的 32 位和 64 位之间的区别吗?32 位的软件可以运行在 64 位机器上吗?反过来呢?</h3><p>64 位和 32 位的软件,实际上代表指令是 64 位还是 32 位的。</p><p>如果想要 32 位指令在 64 位机器上执行,只需要一套兼容机制,就可以做到了。</p><p>但如果想要 64 位指令在 32 位机器上执行,就比较困难了,因为 32 位的寄存器存不下 64 位的指令。</p><h3 id="讲讲你对-L1、L2、L3-Cache-的理解?"><a href="#讲讲你对-L1、L2、L3-Cache-的理解?" class="headerlink" title="讲讲你对 L1、L2、L3 Cache 的理解?"></a>讲讲你对 L1、L2、L3 Cache 的理解?</h3><p>L1 Cache 通常分为「数据缓存」和「指令缓存」,两者大小通常一致。</p><p>L1 Cache 和 L2 Cache 都是每个 CPU 核心独有的,而 L3 Cache 是多个 CPU 核心共享的。</p><p>CPU 访问 L1 Cache 需要 2-4 个时钟周期,访问 L2 Cache 需要 10-20 个时钟周期,访问 L3 Cache 需要 20-60 个时钟周期,访问寄存器一般少于一个时钟周期,访问内存需要 200-300 个时钟周期。</p><h4 id="什么时候同步-Cache-和内存?"><a href="#什么时候同步-Cache-和内存?" class="headerlink" title="什么时候同步 Cache 和内存?"></a>什么时候同步 Cache 和内存?</h4><h5 id="写直达(Write-Through):把数据同时写入内存和-Cache-中。"><a href="#写直达(Write-Through):把数据同时写入内存和-Cache-中。" class="headerlink" title="写直达(Write Through):把数据同时写入内存和 Cache 中。"></a>写直达(Write Through):把数据同时写入内存和 Cache 中。</h5><ul><li><p>如果数据已经在 Cache 中,先更新 Cache,再更新内存。</p></li><li><p>如果数据没有在 Cache 中,直接更新内存。</p></li></ul><p>问题:无论数据在不在 Cache 里面,每次写操作都会操作内存,影响性能。</p><h5 id="写回(Write-Back):当发生写操作时,仅仅更新缓存,只有当缓存失效时,才更新内存。"><a href="#写回(Write-Back):当发生写操作时,仅仅更新缓存,只有当缓存失效时,才更新内存。" class="headerlink" title="写回(Write Back):当发生写操作时,仅仅更新缓存,只有当缓存失效时,才更新内存。"></a>写回(Write Back):当发生写操作时,仅仅更新缓存,只有当缓存失效时,才更新内存。</h5><ul><li>如果数据在缓存中,则更新缓存并标记。</li><li>如果数据不在缓存中,并且对应缓存区域已被标记,则将缓存区域中的数据写回到内存中,再写入缓存。</li></ul><p>如果我们大量操作都命中缓存,那么大部分时间里 CPU 都不需要读写内存,自然性能比写直达高很多。</p><h4 id="如何保证缓存一致性?"><a href="#如何保证缓存一致性?" class="headerlink" title="如何保证缓存一致性?"></a>如何保证缓存一致性?</h4><ol><li>写传播(Write Propagation):某个 CPU 核心里的 Cache 数据更新时,必须要传播到其他核心的 Cache。</li><li>事务的串行化(Transaction Serialization):某个 CPU 核心里对数据的操作顺序,必须在其他核心看起来顺序是一样的。</li></ol><h5 id="总线嗅探"><a href="#总线嗅探" class="headerlink" title="总线嗅探"></a>总线嗅探</h5><p>当 CPU 核进行写操作时,通过总线把这个事件广播给其他所有核心,每个 CPU 核心都会监听总线上的广播事件,并检查是否有相同的数据在自己的 L1/L2 Cache 中,如果存在,则此时也更新 L1/L2 Cache。</p><p>问题:只保证了更新事件能被其他 CPU 核心知晓,并不能保证事务串行化。</p><h5 id="MESI-协议"><a href="#MESI-协议" class="headerlink" title="MESI 协议"></a>MESI 协议</h5><p>Modified(已修改)、Exclusive(独占)、Shared(共享)、Invalidated(已失效)。</p><p>使用上述的四个状态标记 Cache 中的数据。</p><p>Modifed:表示 Cache 中的数据已经修改,但还没有同步到内存中。</p><p>Exclusive:Cache 和内存是一致的,此时数据可以自由写入,而不用通知其他 CPU 核心。更新数据的同时标记为 Modified。</p><p>Shared:Cache 和内存是一致的,数据写入的时候,要先广播一个信号,要求其他核心中的对应 Cache 标记为 Invalidated,然后再更新当前 Cache 的数据,更新同时标记为 Modified。</p><h4 id="谈谈你对伪共享的理解?"><a href="#谈谈你对伪共享的理解?" class="headerlink" title="谈谈你对伪共享的理解?"></a>谈谈你对伪共享的理解?</h4><p>伪共享指的是多个 CPU 核心同时读写一个 Cache Line 的不同变量时,而导致的 CPU Cache 失效的现象。</p><h5 id="避免伪共享的方法"><a href="#避免伪共享的方法" class="headerlink" title="避免伪共享的方法"></a>避免伪共享的方法</h5><ul><li>在 Linux 内核中存在<code>__cacheline_aligned_in_smp</code>宏定义,采用这个宏定义可以使得变量在 Cache Line 中是对齐的。</li></ul><h3 id="linux-中的-task-调度算法"><a href="#linux-中的-task-调度算法" class="headerlink" title="linux 中的 task 调度算法"></a>linux 中的 task 调度算法</h3><p>在 linux 中,根据任务的优先级以及响应要求,将 task 主要分为两种,其中优先级的数值越小,优先级越高。</p><ul><li>实时任务(优先级:0~99),对系统的响应时间要求很高,即要尽可能快地执行实时任务。</li><li>普通任务(优先级:100~139),响应时间没有很高的要求。</li></ul><p>linux 为了保障高优先级的任务能够尽可能早地被执行,分为 3 种调度类:</p><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/img/2022/04/14/20220414-221737.png" alt="调度类"></p><p>Deadline、Realtime 这两个调度类,应用于实时任务,调度策略如下:</p><ul><li>DEADLINE:安装 deadline 进行调度,距离当前时间点最近 deadline 的任务会被优先调度。</li><li>FIFO:对于相同优先级的任务,按照先来先服务原则,但是优先级更高的任务可以抢占低优先级的任务。</li><li>RR:对于相同优先级的任务,轮流运行一个时间片,但是优先级更高的任务可以抢占低优先级的任务。</li></ul><p>Fair 调度类应用于普通任务,有两种调度策略:</p><ul><li>NORMAL:普通任务使用的调度策略。</li><li>BATCH:后台任务的调度策略,不会和终端交互,因此在不影响其他需要交互的任务的前提下,可以适当降低它的优先级。</li><li>CFS(完全公平调度):为每个任务维护一个虚拟运行时间,优先选择虚拟运行时间少的任务。虚拟运行时间 = 时间运行时间 * NICE_0_LOAD / 权重。</li></ul><h4 id="CPU-运行队列"><a href="#CPU-运行队列" class="headerlink" title="CPU 运行队列"></a>CPU 运行队列</h4><p>每个 CPU 都有自己的运行队列,Deadline 运行队列 dl_rq,实时任务运行队列 rt_rq,CFS 运行队列 cfs_rq,其中 cfs_rq 的数据结构为红黑树,最左结点即为下次被调度的任务。</p><p>CPU 选择任务时,会按 Deadline > Realtime > Fair 的优先级顺序进行选择,因此实时任务总是会比普通任务优先被执行。</p><h3 id="什么是软中断?"><a href="#什么是软中断?" class="headerlink" title="什么是软中断?"></a>什么是软中断?</h3><p>linux 为了解决中断处理程序执行过长和中断丢失的问题,将中断过程分成了两个阶段,分别是「硬中断」和「软中断」。</p><ul><li>「硬中断」用来快速处理中断,一般会暂时关闭中断请求,主要负责处理跟硬件紧密相关或者时间敏感的事情。「硬中断」会打断 CPU 正在执行的任务,然后立即执行中断处理程序。</li><li>「软中断」用来延迟处理「硬中断」未完成的工作,一般以「内核线程」的形式运行。每一个 CPU 核心都对应一个软中断内核线程,名字通常为<code>ksoftirqd/「CPU编号」</code>。</li></ul>]]></content>
<summary type="html">收集的一些操作系统方面的面试题。</summary>
<content src="https://fioepq9.cn/img/pixiv_62608184.jpg" type="image"/>
<category term="面试题" scheme="https://fioepq9.cn/categories/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="面试" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95/"/>
<category term="面试题" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="操作系统" scheme="https://fioepq9.cn/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
</entry>
<entry>
<title>数据结构与算法面试题</title>
<link href="https://fioepq9.cn/2022/11/22/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<id>https://fioepq9.cn/2022/11/22/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95%E9%9D%A2%E8%AF%95%E9%A2%98/</id>
<published>2022-11-21T16:18:42.000Z</published>
<updated>2022-12-29T15:16:49.823Z</updated>
<content type="html"><![CDATA[<h2 id="树"><a href="#树" class="headerlink" title="树"></a>树</h2><h3 id="AVL-树"><a href="#AVL-树" class="headerlink" title="AVL 树"></a>AVL 树</h3><p>O(log2 n)的时间复杂度进行搜索、插入、删除操作。</p><h3 id="红黑树"><a href="#红黑树" class="headerlink" title="红黑树"></a>红黑树</h3><ul><li>红黑性质:<ul><li>每个结点或是红色的,或是黑色的。</li><li>根结点是黑色的。</li><li>null 结点视为黑色。</li><li>如果一个结点是红色的,则它的两个子结点都是黑色的。</li><li>对每个结点,从该结点到其后代叶结点的简单路径上,均包含相同数目的黑色结点。</li><li>【左倾红黑树】红色的结点只能是左结点。</li></ul></li><li>引理<ul><li>一颗有 n 个结点的红黑树的高度至多为 2log(n+1)</li></ul></li><li>优点<ul><li>红黑树是牺牲了严格的高度平衡的优越条件为代价,它只要求部分地达到平衡要求,结合变色,降低了对旋转的要求,从而提高了性能。红黑树能够以 O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在<strong>三次旋转</strong>之内解决。</li></ul></li><li>时间复杂度:均为 O(log2 n)</li></ul><h3 id="B-x2F-B-树"><a href="#B-x2F-B-树" class="headerlink" title="B/B+树"></a>B/B+树</h3><p>B/B+树是为磁盘或其他直接存取的辅助存储设备而设计的一种平衡搜索树。在降低磁盘 I/O 操作数方面要更好一些。</p><h4 id="m-阶-B-树的特点"><a href="#m-阶-B-树的特点" class="headerlink" title="m 阶 B 树的特点"></a>m 阶 B 树的特点</h4><ol><li>每个结点最多有 m-1 个键。</li><li>根结点至少有一个键,非根结点至少有 m/2 个键。</li><li>每个结点中的键升序排列,每个键的左子树中的所有键都小于它,右子树中的所有键都大于它。</li><li>所有叶子结点位于同一层,即根节点到每个叶子结点的高度相同。</li><li>每个结点都存有数据。</li></ol><h4 id="m-阶-B-树的特点-1"><a href="#m-阶-B-树的特点-1" class="headerlink" title="m 阶 B+树的特点"></a>m 阶 B+树的特点</h4><ol><li>每个结点最多有 m-1 个键。</li><li>根结点至少有一个键,非根结点至少有 m/2 个键。</li><li>每个结点中的键升序排列,每个键的左子树中的所有键都小于它,右子树中的所有键都大于等于它。</li><li>所有叶子结点位于同一层,即根节点到每个叶子结点的高度相同。</li><li>只有叶子节点保存数据,同时所有叶子节点构成一个链表。</li></ol>]]></content>
<summary type="html">收集的一些数据结构与算法方面的面试题。</summary>
<content src="https://fioepq9.cn/img/pixiv_36633503.jpg" type="image"/>
<category term="面试题" scheme="https://fioepq9.cn/categories/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="面试" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95/"/>
<category term="面试题" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="数据结构" scheme="https://fioepq9.cn/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<category term="算法" scheme="https://fioepq9.cn/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>计算机网络面试题</title>
<link href="https://fioepq9.cn/2022/11/21/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<id>https://fioepq9.cn/2022/11/21/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%9D%A2%E8%AF%95%E9%A2%98/</id>
<published>2022-11-21T15:57:06.000Z</published>
<updated>2022-12-29T15:16:49.823Z</updated>
<content type="html"><![CDATA[<h2 id="计算机网络体系结构"><a href="#计算机网络体系结构" class="headerlink" title="计算机网络体系结构"></a>计算机网络体系结构</h2><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/img/2022/04/01/20220401-200257.png" alt="计算机网络体系结构"></p><h2 id="应用层:HTTP、HTTPS、DNS"><a href="#应用层:HTTP、HTTPS、DNS" class="headerlink" title="应用层:HTTP、HTTPS、DNS"></a>应用层:HTTP、HTTPS、DNS</h2><h3 id="HTTP-是什么?"><a href="#HTTP-是什么?" class="headerlink" title="HTTP 是什么?"></a>HTTP 是什么?</h3><p>HTTP,即「超文本传输协议」,是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。</p><h3 id="超文本的最关键点是什么?"><a href="#超文本的最关键点是什么?" class="headerlink" title="超文本的最关键点是什么?"></a>超文本的最关键点是什么?</h3><p>最关键的是「超链接」,它能够从一个超文本跳转到另一个超文本。</p><h4 id="HTTP-的请求报文包含什么?"><a href="#HTTP-的请求报文包含什么?" class="headerlink" title="HTTP 的请求报文包含什么?"></a>HTTP 的请求报文包含什么?</h4><p>请求报文是由请求方法、请求 URI、协议版本、可选的请求首部字段和内容实体构成的。</p><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/img/2022/03/26/20220326-171734.png" alt="HTTP请求报文"></p><h5 id="GET-和-POST-有什么区别?"><a href="#GET-和-POST-有什么区别?" class="headerlink" title="GET 和 POST 有什么区别?"></a>GET 和 POST 有什么区别?</h5><ul><li>根据 RFC 规范,GET 的语义是从服务器获取指定的资源,POST 的语义是根据请求负荷对指定资源做出处理。</li><li>GET 请求的参数一般写在 URL 中,由于 URL 规定只能支持 ASCII 字符,所以 GET 的参数也只允许 ASCII 字符,同时浏览器一般会对 URL 的长度有限制。POST 请求的参数一般写在报文的 body 中,body 中的数据可以是任意格式的,只要客户端与服务端协商好即可,而且浏览器不对 body 大小做限制。</li><li>GET 方法是安全且幂等的,安全指的是不会修改服务器上的资源,幂等指的是多次执行相同的操作得到的结果也是相同的。POST 方法一般用于新增或提交数据,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。</li><li>可以对 GET 请求的数据做缓存,而且在浏览器中 GET 请求可以保存为书签。浏览器一般不会缓存 POST 请求,也不能将 POST 请求保存为书签。</li></ul><h4 id="HTTP-的响应报文包含什么?"><a href="#HTTP-的响应报文包含什么?" class="headerlink" title="HTTP 的响应报文包含什么?"></a>HTTP 的响应报文包含什么?</h4><p>响应报文由协议版本、状态码、用以解释状态码的原因短语、可选的响应首部字段以及实体主体构成。</p><p><img src="https://raw.githubusercontent.com/ltlin9/note-img/master/img/2022/03/26/20220326-171946.png" alt="HTTP响应报文"></p><h5 id="HTTP-常见的状态码有哪些?"><a href="#HTTP-常见的状态码有哪些?" class="headerlink" title="HTTP 常见的状态码有哪些?"></a>HTTP 常见的状态码有哪些?</h5><p><code>1xx</code>类状态码属于提示信息,是协议处理中的一种中间状态,实际使用较少。</p><p><code>2xx</code>类状态码表示请求成功被处理。</p><ul><li>「200 OK」:表示一切正常。</li><li>「204 No Content」:与 200 OK 基本相同,但返回的响应报文中没有 body。</li><li>「206 Partial Content」:通常应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据是资源的一部分。</li></ul><p><code>3xx</code>类状态码表示请求的资源发生了变动,需要使用新的 URL 进行访问。</p><ul><li>「301 Moved Permanently」:表示永久重定向,如果保存原 URL 为书签了,这个时候应该修改。</li><li>「302 Found」:表示临时重定向,这一次访问需要使用新的 URL。</li><li>「304 Not Modified」:表示资源未修改,用于告知客户端可以继续使用缓存资源。</li></ul><p><code>4xx</code>类状态码表示请求报文有误,服务器无法处理。</p><ul><li>「400 Bad Request」:表示请求报文有误,是一个笼统的错误码。</li><li>「403 Forbidden」:表示请求的资源被服务器禁止访问。</li><li>「404 Not Found」:表示请求的资源在服务器上未找到。</li></ul><p><code>5xx</code>类状态码表示请求报文正确,但服务器内部处理时发生了错误。</p><ul><li>「500 Internal Server Error」:表示服务器内部处理发生了错误,是一个笼统的错误码。</li><li>「501 Not Implemented」:表示客户端请求的功能还不支持,类似于“敬请期待”的意思。</li><li>「502 Bad Gateway」:通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问上游服务器时收到了无效响应。</li><li>「503 Service Unavailable」:表示因为临时的服务器维护或者过载,当前无法处理请求。</li><li>「504 Gateway Timeout」:通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,但访问上游服务器时超时。</li></ul><h4 id="HTTP-的报文首部和报文主体如何区别?"><a href="#HTTP-的报文首部和报文主体如何区别?" class="headerlink" title="HTTP 的报文首部和报文主体如何区别?"></a>HTTP 的报文首部和报文主体如何区别?</h4><p>存在一个 CR+LF 组成的标准空行,进行分隔。</p><h3 id="HTTP-缓存有哪些实现方式?"><a href="#HTTP-缓存有哪些实现方式?" class="headerlink" title="HTTP 缓存有哪些实现方式?"></a>HTTP 缓存有哪些实现方式?</h3><p>HTTP 缓存有两种实现方式,分别是「强制缓存」和「协商缓存」。</p><h4 id="强制缓存"><a href="#强制缓存" class="headerlink" title="强制缓存"></a>强制缓存</h4><p>「强制缓存」指的是只要浏览器判断缓存没有过期,就直接使用浏览器的本地缓存,决定权在浏览器。「强制缓存」使用 HTTP 响应首部中的<code>Cache-Control</code>字段实现。</p><p>当浏览器第一次访问服务器资源时,服务器会在返回资源的同时,在响应首部加上<code>Cache-Control</code>字段,这个字段中设置了缓存过期时间;当浏览器再次访问相同资源时,会先通过请求资源的时间与<code>Cache-Control</code>中设置的过期时间大小计算出缓存是否过期,过期才会重新请求服务器。</p><h4 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h4><p>「协商缓存」就是浏览器与服务器进行协商,根据协商结果来判断是否使用本地缓存。只有在未命中「强制缓存」时,才能发起「协商缓存」请求。「协商缓存」可以基于两种首部实现。</p><ul><li>请求首部的<code>If-Modified-Since</code>字段和响应首部的<code>Last-Modified</code>字段,<code>Last-Modified</code>字段标记了资源的最后修改时间,当缓存过期需要再次向服务器发送请求时,在首部<code>If-Modified-Since</code>字段中携带<code>Last-Modified</code>字段的值,服务器收到后会与被请求资源的最后修改时间进行对比,如果资源修改过,就返回「200 OK」;如果资源无新修改,就返回「304 Not Modified」。</li><li>请求首部的<code>If-None-Match</code>字段和响应首部的<code>ETag</code>字段,<code>ETag</code>字段是资源的唯一标识,当缓存过期需要再次向服务器发送请求时,在首部<code>If-None-Match</code>字段中携带<code>ETag</code>字段的值,服务器收到后会进行比对,如果资源修改过,就返回「200 OK」;如果资源无新修改,就返回「304 Not Modified」。</li></ul><h3 id="HTTP-有哪些优缺点?"><a href="#HTTP-有哪些优缺点?" class="headerlink" title="HTTP 有哪些优缺点?"></a>HTTP 有哪些优缺点?</h3><p>优点:</p><ol><li>「简单」:HTTP 基本的报文格式就是 header + body,头部信息也是简单的 key-value 对,易于理解,学习成本低。</li><li>「灵活、易于扩展」:HTTP 协议中的各类请求方法、URI、状态码、首部字段没有被固定死,允许开发人员自定义和扩充。</li><li>「应用广泛」:HTTP 的应用范围相当广泛,从 PC 端的浏览器到手机上的各种 APP,天然具有跨平台的优越性。</li></ol><p>双刃剑:</p><ol><li>「无状态」<ul><li>好处:不需要额外的资源记录状态信息,减轻了服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。</li><li>坏处:进行有关联性的操作时非常麻烦,例如【登录-下单-结算-支付】。</li><li>通常使用 Cookie 技术来解决无状态的弊端。</li></ul></li><li>「明文传输」<ul><li>好处:通过浏览器 F12 控制台或者使用抓包工具抓包可以肉眼查看,为调试工作带来了极大的便利性。</li><li>坏处:信息容易被窃取,如果信息包含账号、密码等重要隐私,就容易泄漏。</li></ul></li></ol><p>缺点:「不安全」</p><ul><li>通信过程使用明文,内容可能会被窃听。</li><li>不会验证通信方的身份,可能遭遇伪装。</li><li>无法证明报文的完整性,报文可能遭到篡改。</li></ul><h3 id="从输入网址到获得页面的过程?"><a href="#从输入网址到获得页面的过程?" class="headerlink" title="从输入网址到获得页面的过程?"></a>从输入网址到获得页面的过程?</h3><ol><li>浏览器对 URL 进行解析,获取到使用的协议、域名、URI。</li><li>浏览器根据 URL 解析后获得的信息生成 HTTP 请求信息。</li><li>浏览器查询自身对域名的缓存,未命中就继续查询操作系统的缓存,如果还是未命中就查询 hosts 文件。最后如果没有查询到域名的对应 IP 地址,浏览器就会使用 DNS 协议进行域名解析。</li></ol><ul><li>域名解析过程:<ol><li>客户端首先向本地 DNS 服务器(在 TCP/IP 设置中填写的 DNS 服务器地址)发出一个 DNS 请求。</li><li>本地域名服务器收到请求后:如果缓存里能找到映射关系,就直接返回;如果没有,本地域名服务器会去询问根域名服务器。</li><li>根域名服务器收到请求后,返回域名对应的顶级域名服务器地址。</li><li>本地域名服务器收到顶级域名服务器地址后,会去询问顶级域名服务器。</li><li>顶级域名服务器收到请求后,返回域名对应的权威名称服务器地址。</li><li>本地域名服务器收到权威名称服务器地址后,会去询问权威名称服务器。</li><li>权威名称服务器收到请求后,就会返回域名对应的 IP 地址。</li><li>本地域名服务器收到 IP 地址后,将它进行缓存,然后返回给客户端。</li></ol></li></ul><ol start="4"><li>浏览器获取到域名对应的 IP 地址后,首先要跟服务器请求建立 TCP 连接,也就是进行 TCP 三次握手。三次握手的目的是保证双方都有发送和接收的能力。</li><li>TCP 连接建立完成后,浏览器就会向服务器发送 HTTP 请求。</li><li>服务器接收到请求后,将浏览器请求的资源返回给浏览器。</li><li>最后浏览器根据请求获取到的资源、数据渲染页面。</li></ol><h3 id="HTTP-和-HTTPS-有哪些区别?"><a href="#HTTP-和-HTTPS-有哪些区别?" class="headerlink" title="HTTP 和 HTTPS 有哪些区别?"></a>HTTP 和 HTTPS 有哪些区别?</h3><ul><li>HTTP 传输数据是明文传输的,存在安全方面的问题。HTTPS 在 TCP 和 HTTP 之间加入了 SSL/TLS 安全协议,添加了加密机制。</li><li>HTTP 只需要进行 TCP 三次握手后就可以开始传输数据;HTTPS 在 TCP 三次握手后,还需要进行 SSL/TLS 的握手过程,才可以进行数据的传输。</li><li>HTTP 的知名端口号是 80,HTTPS 的知名端口是 443。</li><li>HTTPS 需要向 CA(证书权威机构)购买数字证书,用于保证服务器的身份可信。</li></ul><h3 id="HTTPS-解决了-HTTP-的哪些问题?"><a href="#HTTPS-解决了-HTTP-的哪些问题?" class="headerlink" title="HTTPS 解决了 HTTP 的哪些问题?"></a>HTTPS 解决了 HTTP 的哪些问题?</h3><ol><li>HTTPS 通过「混合加密」的方式实现了信息的机密性,解决了窃听的风险。</li><li>HTTPS 通过「摘要算法」的方式实现了信息的完整性,它为数据生成了独一无二的指纹,解决了信息被篡改的风险。</li><li>HTTPS 将服务器公钥放入到数字证书中,由 CA 机构颁发的数字证书保证服务器公钥的可靠性,解决了冒充的风险。</li></ol><h4 id="混合加密是什么?"><a href="#混合加密是什么?" class="headerlink" title="混合加密是什么?"></a>混合加密是什么?</h4><p>HTTPS 采用的是「对称加密」和「非对称加密」相结合的「混合加密」方式:</p><ul><li>在通信建立前采用非对称加密的方式交互「对称密钥」,后续不再使用非对称加密。</li><li>在通信过程中全程使用对称加密的「对称密钥」对数据进行加密。</li></ul><h4 id="采用混合加密的原因?"><a href="#采用混合加密的原因?" class="headerlink" title="采用混合加密的原因?"></a>采用混合加密的原因?</h4><ul><li>「对称加密」只使用一个密钥,运算速度快,但密钥必须保密,无法做到安全的密钥交换。</li><li>「非对称加密」使用公钥和私钥组成的密钥对,公钥可以任意分发而私钥需要保密,解决了密钥交换问题,但加密解密的开销比较大。</li></ul><h4 id="摘要算法是如何验证数据的完整性的?"><a href="#摘要算法是如何验证数据的完整性的?" class="headerlink" title="摘要算法是如何验证数据的完整性的?"></a>摘要算法是如何验证数据的完整性的?</h4><p>客户端在发送数据前,会使用「摘要算法」计算出数据明文的指纹,然后将「数据明文+指纹」一同加密发送给服务器。服务器解密后,使用相同的「摘要算法」计算出数据明文的指纹,通过比对指纹,验证数据的完整性。</p><h3 id="SSL-x2F-TLS-的握手过程?"><a href="#SSL-x2F-TLS-的握手过程?" class="headerlink" title="SSL/TLS 的握手过程?"></a>SSL/TLS 的握手过程?</h3><ol><li>Client Hello</li></ol><p>首先,由客户端向服务器发起加密通信请求,即<code>Client Hello</code>请求。</p><p>在这一步中,客户端主要向服务器发送以下信息:</p><ul><li>客户端支持的 SSL/TLS 协议版本。</li><li>客户端生成的随机数<code>Client Random</code>。</li><li>客户端支持的密码套件列表。</li></ul><ol start="2"><li>Server Hello</li></ol><p>服务器收到客户端的请求后,向客户端发出响应,即<code>Server Hello</code>。</p><p>服务器响应的内容如下:</p><ul><li>服务器确认使用的 SSL/TLS 协议版本、服务器生成的随机数<code>Server Random</code>、服务器确认使用的密码套件列表。</li><li>服务器的 CA 数字证书,里面包含服务器公钥。</li></ul><ol start="3"><li>客户端回应</li></ol><p>客户端收到服务器的回应后,首先通过内嵌在浏览器或者操作系统中的 CA 公钥,确认服务器数字证书的真实性。</p><p>如果证书没有问题,客户端会从数字证书中取出服务器公钥,然后使用它加密报文,向服务器发送以下信息:</p><ul><li>发送一个随机数<code>pre-master key</code>。</li><li>加密通信算法改变通知,表示随后的信息使用「会话密钥」进行加密。</li><li>客户端握手结束通知,表示客户端的握手阶段已经结束。同时把之前所有信息根据「摘要算法」生成指纹发送给服务器进行校验。</li></ul><p>客户端发送完信息后,会使用<code>Client Random</code>、<code>Server Random</code>、<code>pre-master key</code>三个随机数根据协商的加密算法生成本次通信的「会话密钥」。</p><ol start="4"><li>服务器回应</li></ol><p>服务器收到报文后,使用私钥进行解密,然后根据摘要指纹验证握手信息,验证完毕后,使用<code>Client Random</code>、<code>Server Random</code>、<code>pre-master key</code>三个随机数根据协商的加密算法生成本次通信的「会话密钥」。然后,向客户端发送最后的握手信息:</p><ul><li>加密通信算法改变通知,表示随后的信息使用「会话密钥」进行加密。</li><li>服务器握手结束通知,表示服务器的握手阶段已经结束。同时把之前所有信息根据「摘要算法」生成指纹发送给客户端进行校验。</li></ul><h3 id="DNS-是什么?"><a href="#DNS-是什么?" class="headerlink" title="DNS 是什么?"></a>DNS 是什么?</h3><p>「DNS」是因特网上作为<strong>域名和 IP 地址相互映射</strong>的一个<strong>分布式数据库</strong>,它属于应用层协议,使用 UDP 进行传输。</p><h2 id="传输层:TCP-和-UDP"><a href="#传输层:TCP-和-UDP" class="headerlink" title="传输层:TCP 和 UDP"></a>传输层:TCP 和 UDP</h2><h3 id="什么是三次握手?"><a href="#什么是三次握手?" class="headerlink" title="什么是三次握手?"></a>什么是三次握手?</h3><ol><li>第一次握手:客户端发送一个 SYN 置位,序列号为随机生成的客户端初始序列号的报文给服务端,随后客户端进入 SYN_SENT 状态。</li><li>第二次握手:服务端收到客户端的 SYN 报文后,根据 SYN 置位可知客户端在请求建立连接。然后服务端发送一个 ACK 置位,SYN 置位,确认号为客户端初始序列号+1,序列号为随机生成的服务端初始序列号的报文给客户端。随后服务端进入 SYN_RCVD 状态。</li><li>第三次握手:客户端检查收到的报文 ACK 是否置位,确认号是否为客户端初始序列号+1。客户端根据报文的 SYN 标志位被置位可知服务端同意建立连接,检查正确后,发送一个 ACK 置位,确认号为服务端初始序列号+1 的报文给服务端,随后客户端进入 ESTABLISHED 状态。服务端收到报文后检查 ACK 是否置位,确认号是否为服务端初始序列号+1,检查正确后服务端也进入 ESTABLISHED 状态。</li></ol><h4 id="TCP-建立连接可以两次握手吗?为什么?"><a href="#TCP-建立连接可以两次握手吗?为什么?" class="headerlink" title="TCP 建立连接可以两次握手吗?为什么?"></a>TCP 建立连接可以两次握手吗?为什么?</h4><p>不可以,有两个原因。</p><ol><li>第一个,是当一个因超时等原因已经失效的连接请求报文段传送到了 Server。本来这是已失效的报文段,但 Server 会认为这是 Client 再次发出的新的连接请求,于是就向 Client 发出 ACK 报文段同意请求。由于是两次握手,新的连接在服务端看来已经建立了,Server 会一直维持连接等待 Client 发送数据,白白浪费资源。</li><li>第二个是,只有两次握手的话,Server 无法确认 Client 正确接收到了第二次握手的报文,也就无法保证 Client 和 Server 之间成功交换了初始序列号。</li></ol><h4 id="可以采用四次握手吗?为什么?"><a href="#可以采用四次握手吗?为什么?" class="headerlink" title="可以采用四次握手吗?为什么?"></a>可以采用四次握手吗?为什么?</h4><p>可以。但是会降低传输效率。</p><ul><li>四次握手是指:将第二次握手的报文段拆分成 ACK 报文和 SYN 报文。出于优化的目的,这是可以合并的。</li></ul><h4 id="第三次握手中,如果客户端的-ACK-报文未送达服务端,会怎么样?"><a href="#第三次握手中,如果客户端的-ACK-报文未送达服务端,会怎么样?" class="headerlink" title="第三次握手中,如果客户端的 ACK 报文未送达服务端,会怎么样?"></a>第三次握手中,如果客户端的 ACK 报文未送达服务端,会怎么样?</h4><ul><li>Server:由于 Server 没有收到 ACK 确认报文,因此会重发之前 SYN+ACK 报文(默认重发 5 次,之后自动关闭连接,进入 CLOSED 状态),Client 收到 SYN+ACK 报文后会重发 ACK 报文。</li><li>Client 端有两种情况。<ul><li>一种是 Server 超时重发过程中,Client 端向服务端发送了包含 ACK 标志位的数据报文,服务端读取确认号后,根据确认号大于服务端初始序列号+1,可以进入 ESTABLISHED 状态。</li><li>另一种情况是,Server 已经进入 CLOSED 状态了,Client 向服务端发送数据,服务端会以 RST 包应答。</li></ul></li></ul><h4 id="如果已经建立了连接,但客户端出现了故障怎么办?"><a href="#如果已经建立了连接,但客户端出现了故障怎么办?" class="headerlink" title="如果已经建立了连接,但客户端出现了故障怎么办?"></a>如果已经建立了连接,但客户端出现了故障怎么办?</h4><p>服务端每收到一次客户端的请求后都会重新复位一个计时器,时间通常是设置为 2 小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔 75 秒钟发送一次。若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。</p><h4 id="初始序列号是什么?-序列号的作用是什么?"><a href="#初始序列号是什么?-序列号的作用是什么?" class="headerlink" title="初始序列号是什么?(序列号的作用是什么?)"></a>初始序列号是什么?(序列号的作用是什么?)</h4><p>TCP 是一种全双工的连接,它在两个方向都有一个初始序列号。在同一个方向上,发送端会以初始序列号作为原点,对要传输的数据进行编号。接收端通过序列号可以确认数据是否合法,同时发送端根据接收端发送的 ACK 报文中的确认号可以确认哪些数据已被接收。</p><h3 id="什么是四次挥手?"><a href="#什么是四次挥手?" class="headerlink" title="什么是四次挥手?"></a>什么是四次挥手?</h3><ol><li>第一次挥手:Client 发送一个 FIN 置位,并有相应序列号的报文给 Server,随后进入 FIN_WAIT_1 状态。此时,Client 不再发送数据,但仍可以接收数据。</li><li>第二次挥手:Server 收到 FIN 后,发送一个 ACK 置位,确认号为收到序列号+1 的报文,进入 CLOSE_WAIT 状态。Client 接收到 ACK 报文后,进入 FIN_WAIT_2 状态。</li><li>第三次挥手:Server 发送一个 FIN 置位,并填写了对应序列号的报文给 Client,随后进入 LAST_ACK 状态。</li><li>第四次挥手:Client 收到服务器的 FIN 报文后,进入 TIME_WAIT 状态;接着发送一个 ACK 置位,确认号为三次挥手的序列号+1 的报文给 Server;Server 接收到后,确认 ACK 标志位和确认号后,进入 CLOSED 状态。客户端等待 2*MSL(报文段最长寿命)时间后,也进入 CLOSED 状态。</li></ol><h4 id="为什么不能把服务端发送的-ACK-和-FIN-合并,变成三次挥手?(CLOSE-WAIT-状态的意义是什么?)"><a href="#为什么不能把服务端发送的-ACK-和-FIN-合并,变成三次挥手?(CLOSE-WAIT-状态的意义是什么?)" class="headerlink" title="为什么不能把服务端发送的 ACK 和 FIN 合并,变成三次挥手?(CLOSE_WAIT 状态的意义是什么?)"></a>为什么不能把服务端发送的 ACK 和 FIN 合并,变成三次挥手?(CLOSE_WAIT 状态的意义是什么?)</h4><p>因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送。</p><h4 id="如果第二次挥手时服务器的-ACK-没有送达客户端,会怎么样?"><a href="#如果第二次挥手时服务器的-ACK-没有送达客户端,会怎么样?" class="headerlink" title="如果第二次挥手时服务器的 ACK 没有送达客户端,会怎么样?"></a>如果第二次挥手时服务器的 ACK 没有送达客户端,会怎么样?</h4><p>客户端没有收到 ACK 确认,会重新发送 FIN 请求。</p><h4 id="客户端-TIME-WAIT-状态的意义是什么?"><a href="#客户端-TIME-WAIT-状态的意义是什么?" class="headerlink" title="客户端 TIME_WAIT 状态的意义是什么?"></a>客户端 TIME_WAIT 状态的意义是什么?</h4><p>第四次挥手时,客户端发送给服务器的 ACK 有可能丢失,TIME_WAIT 状态就是用来重发可能丢失的 ACK 报文。如果 Server 没有收到 ACK,就会重发 FIN,如果 Client 在 2<em>MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2</em>MSL,防止 Server 没有收到 ACK 而不断重发 FIN。</p><h3 id="TCP-的流量控制是如何实现的?"><a href="#TCP-的流量控制是如何实现的?" class="headerlink" title="TCP 的流量控制是如何实现的?"></a>TCP 的流量控制是如何实现的?</h3><p>TCP 是使用滑动窗口协议实现流量控制的。</p><p>接收端会维护一个接收窗口,根据自身剩余缓冲区大小动态调整,在返回 ACK 报文时将接收窗口大小放在 TCP 报文首部的窗口字段中告知发送端。发送窗口的大小不能超过接收窗口的大小,只有当发送端发送的数据收到确认后,才能右移发送窗口。</p><p>发送窗口的上限为接收窗口和拥塞窗口中的较小值。接收窗口表明了接收方的接收能力,拥塞窗口表明了网络的传送能力。</p><h4 id="什么是零窗口?(接收窗口为-0-时会怎么样?)"><a href="#什么是零窗口?(接收窗口为-0-时会怎么样?)" class="headerlink" title="什么是零窗口?(接收窗口为 0 时会怎么样?)"></a>什么是零窗口?(接收窗口为 0 时会怎么样?)</h4><p>如果接收方没有能力接收数据,就会将接收窗口设置为 0,这时发送方必须暂停发送数据,但是会启动一个持续计时器(persistence timer),到期后发送一个大小为 1 字节的探测数据包,以查看接收窗口状态。如果接收方能够接收数据,就会在返回的报文中更新接收窗口大小,恢复数据传送。</p><h3 id="TCP-的拥塞控制是怎么实现的?"><a href="#TCP-的拥塞控制是怎么实现的?" class="headerlink" title="TCP 的拥塞控制是怎么实现的?"></a>TCP 的拥塞控制是怎么实现的?</h3><p>拥塞控制主要由四个算法组成:慢启动、拥塞避免、快速重传、快速恢复。</p><ul><li>慢启动:</li></ul><p>在刚建立 TCP 连接或者出现丢包的时候,发送端处于慢启动状态,这个时候会把拥塞窗口设置为 1 个 MSS。每接收到一个 ACK 报文,就增加 1 个 MSS。这样每经过一个 RTT,拥塞窗口的大小就会加倍。</p><ul><li>拥塞避免:</li></ul><p>当拥塞窗口的大小达到慢启动阈值(ssthresh)时,开始执行拥塞避免算法。每经过 1 个 RTT,拥塞窗口大小不再指数增长,而是线性增长。</p><ul><li>快速重传:</li></ul><p>接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不是等到自己发送数据时捎带确认。</p><p>发送方只要一连收到三个重复确认就立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。</p><ul><li>快速恢复:</li></ul><p>当发送方连续收到三个重复确认时,就把慢启动阈值减半,同时将拥塞窗口的大小置为慢启动阈值的大小,然后执行拥塞避免算法。</p><p>不执行慢开始算法的原因是:因为如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方认为现在网络可能没有出现拥塞。</p><h3 id="TCP-如何保证可靠性?"><a href="#TCP-如何保证可靠性?" class="headerlink" title="TCP 如何保证可靠性?"></a>TCP 如何保证可靠性?</h3><ol><li>TCP 首部的校验和</li><li>应答机制:接收端收到数据之后会根据序列号,会发送一个 ACK 确认报文。</li><li>超时重发机制:发送端发出数据后,会启动一个定时器,超时未收到 ACK 报文,就会重发。</li><li>流量控制:确保接收端能完整接收发送端的数据而不会缓冲区溢出。</li><li>拥塞控制:当网络拥塞时,通过拥塞控制减少数据的发送,防止包丢失。</li></ol><h3 id="TCP-与-UDP-的区别"><a href="#TCP-与-UDP-的区别" class="headerlink" title="TCP 与 UDP 的区别"></a>TCP 与 UDP 的区别</h3><ol><li><p>连接<br>TCP 是面向连接的传输层协议,传输数据前要先建立连接。<br>UDP 是无连接的,直接就可以传输数据。</p></li><li><p>服务对象<br>TCP 是一对一的两点服务,即一条连接只有两个端点。<br>UDP 支持一对一、一对多、多对一、多对多的交互通信。</p></li><li><p>可靠性<br>TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。<br>UDP 是尽最大努力交付数据,不保证可靠性。</p></li><li><p>拥塞控制、流量控制<br>TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。<br>UDP 则没有,即使网络非常拥堵,也不会影响 UDP 的发送速率。</p></li><li><p>首部开销<br>TCP 的首部较长,最小是 20 个字节,如果使用了额外的选项,还会进一步增长。<br>UDP 的首部只有 8 个字节,并且是固定不变的,开销较小。</p></li><li><p>传输方式<br>TCP 是流式传输,没有边界,但保证顺序和可靠性。<br>UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。</p></li><li><p>分片方式<br>TCP 的数据大小如果大于 MSS,则会在传输层进行分片,如果传输过程中丢失了一个分片,只需要重传这个丢失的分片。<br>UDP 的数据大小如果大于 MTU,则会在 IP 层进行分片。</p></li></ol><h4 id="什么时候选择-TCP,什么时候选择-UDP?"><a href="#什么时候选择-TCP,什么时候选择-UDP?" class="headerlink" title="什么时候选择 TCP,什么时候选择 UDP?"></a>什么时候选择 TCP,什么时候选择 UDP?</h4><p>对某些实时性要求比较高的情况,选择 UDP,比如游戏,媒体通信,实时视频流(直播),即使出现传输错误也可以容忍;其它大部分情况下使用 TCP,因为要求传输的内容可靠,不出现丢失。</p><h4 id="HTTP-可以使用-UDP-吗?"><a href="#HTTP-可以使用-UDP-吗?" class="headerlink" title="HTTP 可以使用 UDP 吗?"></a>HTTP 可以使用 UDP 吗?</h4><p>可以,HTTP/3 就是使用基于 UDP 的 QUIC 协议。</p><h2 id="网络层:IP-和-ARP"><a href="#网络层:IP-和-ARP" class="headerlink" title="网络层:IP 和 ARP"></a>网络层:IP 和 ARP</h2><h3 id="什么是-ARP-协议?"><a href="#什么是-ARP-协议?" class="headerlink" title="什么是 ARP 协议?"></a>什么是 ARP 协议?</h3><p><strong>ARP 协议完成了 IP 地址与物理地址的映射</strong>。每一个主机都设有一个 <strong>ARP 高速缓存</strong>,里面有<strong>所在的局域网</strong>上的各主机和路由器的 IP 地址到硬件地址的映射表。当源主机要发送数据包到目的主机时,会先检查自己的 ARP 高速缓存中有没有目的主机的 MAC 地址,如果有,就直接将数据包发到这个 MAC 地址,如果没有,就向<strong>所在的局域网</strong>发起一个 ARP 请求的广播包(在发送自己的 ARP 请求时,同时会带上自己的 IP 地址到硬件地址的映射),收到请求的主机检查自己的 IP 地址和目的主机的 IP 地址是否一致,如果一致,则先保存源主机的映射到自己的 ARP 缓存,然后给源主机发送一个 ARP 响应数据包。源主机收到响应数据包之后,先添加目的主机的 IP 地址与 MAC 地址的映射,再进行数据传送。如果源主机一直没有收到响应,表示 ARP 查询失败。</p><p>如果所要找的主机和源主机不在同一个局域网上,那么就要通过 ARP 找到一个位于本局域网上的某个路由器的硬件地址,然后把分组发送给这个路由器,让这个路由器把分组转发给下一个网络。剩下的工作就由下一个网络来做。</p>]]></content>
<summary type="html">收集的一些计算机网络方面的面试题。</summary>
<content src="https://fioepq9.cn/img/pixiv_37855635.jpg" type="image"/>
<category term="面试题" scheme="https://fioepq9.cn/categories/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="面试" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95/"/>
<category term="面试题" scheme="https://fioepq9.cn/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<category term="计算机网络" scheme="https://fioepq9.cn/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
</entry>
</feed>