-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
592 lines (321 loc) · 160 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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>嵌入式工程猫的博客</title>
<subtitle>嵌入式工程猫的博客</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://blog.vvzero.com/"/>
<updated>2024-10-06T14:44:05.667Z</updated>
<id>https://blog.vvzero.com/</id>
<author>
<name>嵌入式工程猫</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>维护 Nginx 时,什么时候应该用 reload,什么时候应该用 restart?</title>
<link href="https://blog.vvzero.com/2024/10/06/when-to-restart-and-not-reload-nginx/"/>
<id>https://blog.vvzero.com/2024/10/06/when-to-restart-and-not-reload-nginx/</id>
<published>2024-10-06T14:15:51.000Z</published>
<updated>2024-10-06T14:44:05.667Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文是“攻玉计划”的一部分,翻译自 <a href="https://stackoverflow.com/questions/13525465/when-to-restart-and-not-reload-nginx/20215497">https://stackoverflow.com/questions/13525465/when-to-restart-and-not-reload-nginx/20215497</a></p></blockquote><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>使用 Nginx 时,什么情况下 reload 无法满足需求,而必须要使用 restart 命令呢?</p><p>如果 Nginx 进程占用了过多内存,是不是应该用 restart 来重启?如果修改了 Nginx 的核心配置,或者某些插件的配置,是不是也需要使用 restart 命令?</p><p>修改 Nginx 配置后,用户既可以使用 restart 也可以使用 reload 来使配置生效,见 Ubuntu 上 <code>/etc/init.d/nginx -h</code> 的输出。</p><p>那么,哪种方案更优呢?</p><h2 id="回答-1"><a href="#回答-1" class="headerlink" title="回答 1"></a>回答 1</h2><p>reload 比 restart 更安全,因为如果使用 reload 命令,在旧的进程终止之前,Nginx 会先解析配置文件,如果配置文件有问题,那么就会退出重启流程。</p><p>也就是说,如果你的配置文件有问题,比如存在语法错误,那么使用 restart 命令后,Nginx 会先停止,然后就无法再启动了。</p><p>reload 命令如果成功执行,同样会终止旧的进程,所以如果存在内存泄漏问题,也一样可以清除掉。</p><h2 id="回答-2-amp-3"><a href="#回答-2-amp-3" class="headerlink" title="回答 2 & 3"></a>回答 2 & 3</h2><p>我遇到过一种情形,如果我修改了监听的 IP 地址,也就是配置文件里的 listen 字段,那就必须使用 restart 命令。</p><p>从 1.6.x 版本开始,如果仅把监听的 IP 地址从一个改为另一个,那么 reload 可以生效,但如果把监听地址从 <code>listen *:80</code> 修改为 <code>listen x.x.x.x:80</code>,依然需要 restart。</p><p>我只验证了 IPv4 的场景,IPv6 应该类似。</p><blockquote><p>译者按:译者今天遇到过一样的问题,我修改了 <code>listen 80; </code> 到 <code>listen 172.0.0.1:80</code>,结果发现 reload 不生效,必须 restart。</p></blockquote>]]></content>
<summary type="html">
<blockquote>
<p>本文是“攻玉计划”的一部分,翻译自 <a href="https://stackoverflow.com/questions/13525465/when-to-restart-and-not-reload-nginx/20215497">https
</summary>
<category term="攻玉计划" scheme="https://blog.vvzero.com/categories/%E6%94%BB%E7%8E%89%E8%AE%A1%E5%88%92/"/>
<category term="Nginx" scheme="https://blog.vvzero.com/tags/Nginx/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
</entry>
<entry>
<title>批量修改 qbittorrent-nox 内种子的 tracker 地址</title>
<link href="https://blog.vvzero.com/2024/09/13/batch-edit-tracker-urls-in-qbittorrent-nox/"/>
<id>https://blog.vvzero.com/2024/09/13/batch-edit-tracker-urls-in-qbittorrent-nox/</id>
<published>2024-09-13T12:40:18.000Z</published>
<updated>2024-09-13T12:52:04.190Z</updated>
<content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>馒头 PT 站的默认 tracker 出了问题,我到现在都没搞懂是被墙了还是我自己设备的问题,反正就是无法访问默认的 .cc 域名,但 .io 的域名是可以的。</p><p>但手动修改 .cc 倒 .io ,也太麻烦了,我有上百个种子。</p><p>我是在一台 Ubuntu 服务器上运行的原版 qbittorrent-nox,用 webui 访问。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>qbittorrent-nox 会在当前用户的家目录中的 <code>~/.local/share/qBittorrent/BT_backup</code> 目录内存放所有正在使用的种子(虽然我也不清楚为啥名字里有 <code>backup</code>),vim 看了一眼,就是很正常的种子文件格式。</p><p>所以,理论上,直接批量替换这些种子文件里面的 tracker URL,就能解决问题。</p><p>说干就干,<strong>先停掉 qb 的服务,然后备份 <code>BT_backup</code>,再在 <code>BT_backup</code> 内执行 <code>find . -type f -exec sed -i 's/example.cc/example.io/' {} \;</code> ,最后重启 qb 服务</strong>,果然解决问题。</p>]]></content>
<summary type="html">
<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>馒头 PT 站的默认 tracker 出了问题,我到现在都没搞懂是被墙了还是我自己设备的问题,反正就是无法访问默认的 .cc 域名,但 .i
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
<category term="qbittorrent" scheme="https://blog.vvzero.com/tags/qbittorrent/"/>
<category term="PT" scheme="https://blog.vvzero.com/tags/PT/"/>
</entry>
<entry>
<title>把 vim 的缩进设为 4 个字符,并且 tab 自动转空格</title>
<link href="https://blog.vvzero.com/2024/05/07/vim-set-auto-indent-to-4-spaces/"/>
<id>https://blog.vvzero.com/2024/05/07/vim-set-auto-indent-to-4-spaces/</id>
<published>2024-05-07T12:36:47.000Z</published>
<updated>2024-05-07T13:06:36.891Z</updated>
<content type="html"><![CDATA[<p>修改本用户的 <code>~/.vimrc</code> 文件,添加以下内容。如果要想 sudo vim 也生效,那 <code>/root/.vimrc</code> 也要改。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">filetype plugin indent on</span><br><span class="line">" show existing tab with 4 spaces width</span><br><span class="line">set tabstop=4</span><br><span class="line">" when indenting with '>', use 4 spaces width</span><br><span class="line">set shiftwidth=4</span><br><span class="line">" On pressing tab, insert 4 spaces</span><br><span class="line">set expandtab</span><br></pre></td></tr></table></figure><p>上面的注释已经解释了含义。</p>]]></content>
<summary type="html">
<p>修改本用户的 <code>~/.vimrc</code> 文件,添加以下内容。如果要想 sudo vim 也生效,那 <code>/root/.vimrc</code> 也要改。</p>
<figure class="highlight plaintext"><table>
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
<category term="Vim" scheme="https://blog.vvzero.com/tags/Vim/"/>
</entry>
<entry>
<title>不用 snap,在 Ubuntu 上安装 certbot</title>
<link href="https://blog.vvzero.com/2024/05/07/Ubuntu-install-certbot-without-snap/"/>
<id>https://blog.vvzero.com/2024/05/07/Ubuntu-install-certbot-without-snap/</id>
<published>2024-05-07T12:36:30.000Z</published>
<updated>2024-05-07T13:03:53.651Z</updated>
<content type="html"><![CDATA[<blockquote><p>Certbot 官网居然只提供 snap 方式安装,而 snap 是我在 Ubuntu 上最不喜欢的东西</p></blockquote><p>那就直接用 pip 安装吧~</p><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">sudo apt install certbot python3-certbot-nginx</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>Certbot 官网居然只提供 snap 方式安装,而 snap 是我在 Ubuntu 上最不喜欢的东西</p>
</blockquote>
<p>那就直接用 pip 安装吧~</p>
<figure class="highlight bash">
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="网络安全" scheme="https://blog.vvzero.com/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
<category term="Ubuntu" scheme="https://blog.vvzero.com/tags/Ubuntu/"/>
</entry>
<entry>
<title>在 Ubuntu 中启用 swap</title>
<link href="https://blog.vvzero.com/2024/05/07/Ubuntu-enable-swap-file/"/>
<id>https://blog.vvzero.com/2024/05/07/Ubuntu-enable-swap-file/</id>
<published>2024-05-07T12:35:55.000Z</published>
<updated>2024-05-07T13:01:36.331Z</updated>
<content type="html"><![CDATA[<p>首先,swap 多大比较好?如果你有一个 2G 内存的服务器,偶尔内存有点吃紧,那就再开 2G 的 swap 吧。其他的情况,随缘。如果内存不紧张,就不用开。</p><p>依次执行:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">sudo fallocate -l 2G /swapfile <span class="comment"># 在根目录下创建一个 2G 大小的 swap 文件</span></span><br><span class="line">sudo <span class="built_in">chmod</span> 600 /swapfile <span class="comment"># 修改权限配置</span></span><br><span class="line">sudo mkswap /swapfile <span class="comment"># 把这个文件作为 swap</span></span><br><span class="line">sudo swapon /swapfile <span class="comment"># 启用 swap</span></span><br><span class="line"></span><br><span class="line">sudo vim /etc/fstab <span class="comment"># 编辑 fstab 以自动挂载 swap 文件</span></span><br><span class="line"><span class="comment"># 添加以下内容</span></span><br><span class="line">/swapfile swap swap defaults 0 0</span><br><span class="line"></span><br><span class="line">sudo swapon --show <span class="comment"># 查看 swap 是否已经开启成功</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>首先,swap 多大比较好?如果你有一个 2G 内存的服务器,偶尔内存有点吃紧,那就再开 2G 的 swap 吧。其他的情况,随缘。如果内存不紧张,就不用开。</p>
<p>依次执行:</p>
<figure class="highlight bash"><table><t
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
<category term="Ubuntu" scheme="https://blog.vvzero.com/tags/Ubuntu/"/>
</entry>
<entry>
<title>让 Nginx 反向代理的程序获取客户端真实 IP</title>
<link href="https://blog.vvzero.com/2024/05/07/Nginx-get-real-ip/"/>
<id>https://blog.vvzero.com/2024/05/07/Nginx-get-real-ip/</id>
<published>2024-05-07T12:35:45.000Z</published>
<updated>2024-05-07T13:01:47.501Z</updated>
<content type="html"><![CDATA[<p>Nginx 配置添加以下内容:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">location / {</span><br><span class="line"> proxy_pass http://127.0.0.1:8000;</span><br><span class="line"> proxy_set_header X-Real-IP $remote_addr;</span><br><span class="line"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其含义就是在反代时,在客户端发起的请求报文上添加 <code>X-Real-IP</code> 和 <code>X-Forwarded-For</code> 两个 HTTP 头。<code>X-Real-IP</code> 表示表观客户端地址,<code>X-Forwarded-For</code>,顾名思义,就是“为谁代理”的意思,这个可以用来嵌套式传输客户端真实地址。</p><p>被代理的服务,只要能正确解析这两个头,就能获得客户端真实地址。</p>]]></content>
<summary type="html">
<p>Nginx 配置添加以下内容:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="li
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Nginx" scheme="https://blog.vvzero.com/tags/Nginx/"/>
</entry>
<entry>
<title>在 Linux 中显示所有正在监听的 TCP 端口</title>
<link href="https://blog.vvzero.com/2024/05/07/Linux-show-all-listening-ports/"/>
<id>https://blog.vvzero.com/2024/05/07/Linux-show-all-listening-ports/</id>
<published>2024-05-07T12:35:26.000Z</published>
<updated>2024-05-07T13:01:43.321Z</updated>
<content type="html"><![CDATA[<h2 id="netstat"><a href="#netstat" class="headerlink" title="netstat"></a>netstat</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo netstat -tulpn | grep LISTEN</span><br></pre></td></tr></table></figure><p>其中 <code>-t</code> 表示显示 TCP,<code>-u</code> 表示显示 UDP,<code>-l</code> 表示显示监听的端口,<code>-p</code> 表示显示对应的程序名,<code>-n</code> 表示不去查询 IP 对应的主机名。</p><h2 id="lsof"><a href="#lsof" class="headerlink" title="lsof"></a>lsof</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo lsof -i -P -n | grep LISTEN</span><br></pre></td></tr></table></figure><p>其中 <code>-i</code> 表示显示 IP 协议,<code>-P</code> 表示把端口号保留为数字形式,<code>-n</code> 表示不去查询 IP 对应的主机名。</p>]]></content>
<summary type="html">
<h2 id="netstat"><a href="#netstat" class="headerlink" title="netstat"></a>netstat</h2><figure class="highlight plaintext"><table><tr><td cl
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
<category term="网络通信" scheme="https://blog.vvzero.com/tags/%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1/"/>
</entry>
<entry>
<title>再一次理解 C++ 中的 extern "C"</title>
<link href="https://blog.vvzero.com/2023/07/03/learn-extern-c-in-cpp-again/"/>
<id>https://blog.vvzero.com/2023/07/03/learn-extern-c-in-cpp-again/</id>
<published>2023-07-03T14:18:55.000Z</published>
<updated>2023-07-03T14:20:22.932Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文是“攻玉计划”的一部分,翻译自 <a href="https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c">https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c</a> 中 Ciro Santilli 的回答</p></blockquote><h2 id="通过反汇编了解-extern-“C”-的作用"><a href="#通过反汇编了解-extern-“C”-的作用" class="headerlink" title="通过反汇编了解 extern “C” 的作用"></a>通过反汇编了解 extern “C” 的作用</h2><p>main.cpp</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">()</span> </span>{}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">g</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> {</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">ef</span><span class="params">()</span> </span>{}</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">eg</span><span class="params">()</span></span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* Prevent g and eg from being optimized away. */</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">h</span><span class="params">()</span> </span>{ <span class="built_in">g</span>(); <span class="built_in">eg</span>(); }</span><br></pre></td></tr></table></figure><p>将上述代码编译为 <a href="https://stackoverflow.com/questions/26294034/how-to-make-an-executable-elf-file-in-linux-using-a-hex-editor/30648229#30648229">ELF</a> 格式的二进制,然后反汇编:</p><figure class="highlight plaintext"><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">g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp</span><br><span class="line">readelf -s main.o</span><br></pre></td></tr></table></figure><p>摘取一部分输出:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"> 8: 0000000000000000 7 FUNC GLOBAL DEFAULT 1 _Z1fv</span><br><span class="line"> 9: 0000000000000007 7 FUNC GLOBAL DEFAULT 1 ef</span><br><span class="line">10: 000000000000000e 17 FUNC GLOBAL DEFAULT 1 _Z1hv</span><br><span class="line">11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_</span><br><span class="line">12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv</span><br><span class="line">13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg</span><br></pre></td></tr></table></figure><p>可见:</p><ul><li><code>ef</code> 和 <code>eg</code> 在符号表中的名称与其在原先代码中的名称一致</li><li>其他符号都被修饰过了,我们可以用 <code>c++filt</code> 工具还原其本来的样子:</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ c++filt _Z1fv</span><br><span class="line">f()</span><br><span class="line">$ c++filt _Z1hv</span><br><span class="line">h()</span><br><span class="line">$ c++filt _Z1gv</span><br><span class="line">g()</span><br></pre></td></tr></table></figure><p>所以,在以下两种情况下,我们需要使用 <code>extern "C"</code>:</p><ul><li>在 C++ 中调用 C 代码:告诉 <code>g++</code> 可能会遇到由 <code>gcc</code> 生成的未修饰过的符号名</li><li>在 C 中调用 C++ 代码:让 <code>g++</code> 生成未修饰的符号名,以供 <code>gcc</code> 调用</li></ul><h2 id="某些不能在-extern-“C”-中使用的代码"><a href="#某些不能在-extern-“C”-中使用的代码" class="headerlink" title="某些不能在 extern “C” 中使用的代码"></a>某些不能在 extern “C” 中使用的代码</h2><p>显然,任何需要使用 C++ 名称修饰的语言特性,都不能写在 <code>extern "C"</code> 中:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> {</span><br><span class="line"> <span class="comment">// 函数重载</span></span><br><span class="line"> <span class="comment">// 报错:f 的声明有冲突</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">(<span class="type">int</span> i)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 模板</span></span><br><span class="line"> <span class="comment">// 报错:模板不能用于 C 的链接</span></span><br><span class="line"> <span class="keyword">template</span> <<span class="keyword">class</span> <span class="title class_">C</span>> <span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">(C i)</span> </span>{ }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="在-C-中调用-C-代码的最小可运行代码样例"><a href="#在-C-中调用-C-代码的最小可运行代码样例" class="headerlink" title="在 C++ 中调用 C 代码的最小可运行代码样例"></a>在 C++ 中调用 C 代码的最小可运行代码样例</h2><p>在 C++ 中调用 C 代码很简单:每个 C 函数都只有一个未修饰的符号名,所以不需要额外的操作。</p><p>main.cpp</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cassert></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"c.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">assert</span>(<span class="built_in">f</span>() == <span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>c.h</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> C_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> C_H</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 这里,ifdef 可以让这个头文件既可以用于 C++ 工程,也能用于 C 工程,</span></span><br><span class="line"><span class="comment"> * 因为 C 标准里面没有 extern "C" 的定义 */</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __cplusplus</span></span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> {</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">()</span></span>;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __cplusplus</span></span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>c.c</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"c.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">f</span><span class="params">(<span class="type">void</span>)</span> { <span class="keyword">return</span> <span class="number">1</span>; }</span><br></pre></td></tr></table></figure><p>运行:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">g++ -c -o main.o -std=c++98 main.cpp</span><br><span class="line">gcc -c -o c.o -std=c89 c.c</span><br><span class="line">g++ -o main.out main.o c.o</span><br><span class="line">./main.out</span><br></pre></td></tr></table></figure><p>如果没有 <code>extern "C"</code> 的话,链接器会报错:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">main.cpp:6: undefined reference to `f()'</span><br></pre></td></tr></table></figure><p>因为 <code>g++</code> 会寻找一个修饰过的 <code>f</code>,但是 <code>gcc</code> 并不会编译出修饰过的符号名。</p><h2 id="在-C-中调用-C-代码的最小可运行代码样例-1"><a href="#在-C-中调用-C-代码的最小可运行代码样例-1" class="headerlink" title="在 C 中调用 C++ 代码的最小可运行代码样例"></a>在 C 中调用 C++ 代码的最小可运行代码样例</h2><p>在 C 中调用 C++ 代码稍微困难一点:我们需要手动管理所有暴露给 C 的函数接口,并且使它们在编译时不被修饰。</p><p>代码如下:</p><p>main.c</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><assert.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cpp.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> {</span><br><span class="line"> assert(f_int(<span class="number">1</span>) == <span class="number">2</span>);</span><br><span class="line"> assert(f_float(<span class="number">1.0</span>) == <span class="number">3</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>cpp.h</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> CPP_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CPP_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __cplusplus</span></span><br><span class="line"><span class="comment">// 这两个重载的函数不能暴露给 C 的编译群,否则会报错</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">(<span class="type">int</span> i)</span></span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">(<span class="type">float</span> i)</span></span>;</span><br><span class="line"><span class="keyword">extern</span> <span class="string">"C"</span> {</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f_int</span><span class="params">(<span class="type">int</span> i)</span></span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f_float</span><span class="params">(<span class="type">float</span> i)</span></span>;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> __cplusplus</span></span><br><span class="line">}</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>cpp.cpp</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"cpp.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">(<span class="type">int</span> i)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> i + <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">(<span class="type">float</span> i)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> i + <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f_int</span><span class="params">(<span class="type">int</span> i)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">f</span>(i);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">f_float</span><span class="params">(<span class="type">float</span> i)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">f</span>(i);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">gcc -c -o main.o -std=c89 -Wextra main.c</span><br><span class="line">g++ -c -o cpp.o -std=c++98 cpp.cpp</span><br><span class="line">g++ -o main.out main.o cpp.o</span><br><span class="line">./main.out</span><br></pre></td></tr></table></figure><p>如果不加 <code>extern "C"</code> 的话,会报错:</p><figure class="highlight plaintext"><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">main.c:6: undefined reference to `f_int'</span><br><span class="line">main.c:7: undefined reference to `f_float'</span><br></pre></td></tr></table></figure><p>因为 <code>g++</code> 会生成修饰后的符号名,但 <code>gcc</code> 无法理解。</p><h2 id="当我在-C-中包含标准库-C-头文件的时候,extern-“C”-在哪?"><a href="#当我在-C-中包含标准库-C-头文件的时候,extern-“C”-在哪?" class="headerlink" title="当我在 C++ 中包含标准库 C 头文件的时候,extern “C” 在哪?"></a>当我在 C++ 中包含标准库 C 头文件的时候,extern “C” 在哪?</h2><ul><li>C++ 版本的 C 头文件,比如 <code>cstdio</code> 可能靠 <code>#pragma GCC system_header</code> 这个编译宏实现的,根据 <a href="https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html">https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html</a> :在某些平台,例如 RS/6000 AIX 上编译 C++ 代码时,GCC 会隐式地将所有系统头文件用 extern “C” 包含起来。但我并不完全确定。</li><li>POSIX 标准的头文件,比如 <code>/usr/include/unistd.h</code> 会使用 <code>__BEGIN_DECLS</code> 宏,而 <code>__BEGIN_DECLS</code> 会定义为 <code>extern "C" {</code>。详见 <a href="https://stackoverflow.com/questions/8087438/do-i-need-an-extern-c-block-to-include-standard-posix-c-headers/8087539#8087539">https://stackoverflow.com/questions/8087438/do-i-need-an-extern-c-block-to-include-standard-posix-c-headers/8087539#8087539</a>。</li></ul>]]></content>
<summary type="html">
<blockquote>
<p>本文是“攻玉计划”的一部分,翻译自 <a href="https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c">https://stackove
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="C" scheme="https://blog.vvzero.com/tags/C/"/>
<category term="C++" scheme="https://blog.vvzero.com/tags/C/"/>
</entry>
<entry>
<title>航模舵机控制及其 PWM 调制的进一步理解</title>
<link href="https://blog.vvzero.com/2023/06/30/deep-into-RC-plane-s-servo-control-and-PWM/"/>
<id>https://blog.vvzero.com/2023/06/30/deep-into-RC-plane-s-servo-control-and-PWM/</id>
<published>2023-06-30T05:23:24.000Z</published>
<updated>2023-06-30T12:35:16.772Z</updated>
<content type="html"><![CDATA[<p>起因是这样的,前段时间,我同时开始玩履带车和固定翼航模。履带车的动力是直流电机加驱动板,PWM 调制,0-100% 占空比控制电机从静止到全速转动。</p><p>航模的话,虽说十年前我就开始接触了,但一直都是浮于表面的玩,能动就行。玩航模必须要有遥控器和接收机,接收机也是输出 PWM 信号的,连接舵机就能控制其在指定角度范围内运动,连接电调就能控制电机转速。</p><p>于是,我想当然的,航模接收机 <del>应该也是输出 0-100% 占空比的 PWM 信号,0 就是舵机一个极限角度,50% 就是中位,100% 就是另一个极限角度。</del></p><p>然而事实并不是,我把履带车的驱动板接上航模的接收机,不管我怎么推拉摇杆,履带车始终以一个非常低的速度运动。</p><p>这就引起了我的好奇。</p><h2 id="先简单看下航模接收机的-PWM-信号图形"><a href="#先简单看下航模接收机的-PWM-信号图形" class="headerlink" title="先简单看下航模接收机的 PWM 信号图形"></a>先简单看下航模接收机的 PWM 信号图形</h2><p>手头没有示波器,就拿一个简单的逻辑分析仪应付一下:</p><p>我只测量 1 通道数据(通常是副翼/横滚通道)。</p><p>这是摇杆在中点:</p><p><img src="/static/blog_images/deep-into-RC-plane-s-servo-control-and-PWM/0.png"></p><p>摇杆在最左侧:</p><p><img src="/static/blog_images/deep-into-RC-plane-s-servo-control-and-PWM/1.png"></p><p>摇杆在最右侧:</p><p><img src="/static/blog_images/deep-into-RC-plane-s-servo-control-and-PWM/2.png"></p><p>真有趣,占空比始终在 5%~10% 左右。说明航模遥控器的 PWM 信号肯定不是简单靠占空比来控制舵机的。不然放着 10%-100% 这么大的空间不用,有点暴殄天物。</p><h2 id="航模-PWM-信号的真实解释"><a href="#航模-PWM-信号的真实解释" class="headerlink" title="航模 PWM 信号的真实解释"></a>航模 PWM 信号的真实解释</h2><p>我查了很多论坛,一开始感觉很奇怪,他们(比如 <a href="https://www.societyofrobots.com/robotforum/index.php?topic=4299.0">这个</a> )基本上只谈 PWM 的脉冲时间长度为 1-2ms,以及频率“一般”为 50Hz,却几乎不谈占空比。</p><p>我看了一会突然醒悟了,因为 PWM 不是本来就叫“脉冲宽度调制”吗?只不过以前我都用它来调光、控制电机,所以才关注占空比,但 PWM 本身应该去关注“宽度”啊。</p><p>所以,就像 <a href="https://electronics.stackexchange.com/questions/176739/why-do-rc-applications-use-such-a-small-pwm-duty-cycle">这里</a> 回答的一样,航模舵机本身并不关注占空比是多少,只关注每个周期内,高电平的时间是多长。1ms 就代表最小角度,2ms 就是最大角度,1.5ms 就是中位。</p><p>因为一般频率是 50Hz,也就是周期为 20ms,所以占空比看起来才是 5%-10%。</p><h2 id="更多的解释"><a href="#更多的解释" class="headerlink" title="更多的解释"></a>更多的解释</h2><p>至于为什么选择 50Hz,解释是:这个频率是舵机的工作频率而已,舵机要按照这个周期动作。太快舵机会吃不消,太慢的话,响应就慢了。</p><p>也有 200Hz 的舵机,但脉冲宽度也是 1-2ms,于是占空比就变成了 20%-40%。</p><p>当然也有部分舵机会用 0.5-1ms 以及 2-2.5ms 这个区间的脉冲宽度,以达到更大的舵量。</p><p>而对于电调而言,信号也是一样的,沿用舵机的信号就很方便。</p><p>另外为什么选择 1-2ms?因为很久以前的遥控器就这么规定了,不多说。</p>]]></content>
<summary type="html">
<p>起因是这样的,前段时间,我同时开始玩履带车和固定翼航模。履带车的动力是直流电机加驱动板,PWM 调制,0-100% 占空比控制电机从静止到全速转动。</p>
<p>航模的话,虽说十年前我就开始接触了,但一直都是浮于表面的玩,能动就行。玩航模必须要有遥控器和接收机,接收机也是
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="航模" scheme="https://blog.vvzero.com/tags/%E8%88%AA%E6%A8%A1/"/>
<category term="舵机" scheme="https://blog.vvzero.com/tags/%E8%88%B5%E6%9C%BA/"/>
</entry>
<entry>
<title>如何自签名带 SAN 字段的 SSL/TLS 证书</title>
<link href="https://blog.vvzero.com/2023/01/20/self-signed-certificate-with-SAN/"/>
<id>https://blog.vvzero.com/2023/01/20/self-signed-certificate-with-SAN/</id>
<published>2023-01-20T11:10:46.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在本站之前一篇文章 <a href="https://blog.vvzero.com/2021/01/24/Become-a-CA-and-generate-self-signed-certificate/">如何成为 CA,并签发自己的证书</a> 中,我们介绍了如何做一个“正规”的自签名证书。</p><p>但是,这个方法对于现代的浏览器不太管用了,因为 Chrome、Firefox 等浏览器已经不再判定证书的 CN (Common Name) 字段与域名是否一致了,而是改用判定 SAN (Subject Alternative Name) 字段。具体为什么这么做以及 SAN 的含义,网上有很多解释,很重要但是这里不谈,只谈如何在自签名证书中正确配置 SAN 字段。</p><p>网上的教程有很多,但很多没有说清楚,或者缺少一些关键步骤和参数,导致实际情况下出各种问题,这里我尽量整理了一个完善的版本。</p><h2 id="准备步骤"><a href="#准备步骤" class="headerlink" title="准备步骤"></a>准备步骤</h2><p>首先,CA 的证书与文首提到的文章一致,如果之前配置过,那就不需要再生成了。</p><p>然后,生成待签发证书的私钥,这一步也与上述文章相同:</p><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">openssl genrsa -out example.key 4096</span><br></pre></td></tr></table></figure><h2 id="关键在于-CSR-的创建"><a href="#关键在于-CSR-的创建" class="headerlink" title="关键在于 CSR 的创建"></a>关键在于 CSR 的创建</h2><p>之前创建 CSR 的时候会手动输各个字段,但其中不包含 SAN 字段,所以我们需要一个配置文件来配置 SAN 字段(当然直接包含在命令行里面也行,但很麻烦)。</p><p>建议新建一个目录叫 <code>certreq</code>,然后在里面新建一个文件叫 <code>req.conf</code>,内容如下:</p><figure class="highlight ini"><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="section">[req]</span></span><br><span class="line"><span class="attr">distinguished_name</span> = req_distinguished_name</span><br><span class="line"><span class="attr">req_extensions</span> = req_ext</span><br><span class="line"><span class="attr">prompt</span> = <span class="literal">no</span></span><br><span class="line"></span><br><span class="line"><span class="section">[req_distinguished_name]</span></span><br><span class="line"><span class="attr">C</span> = <修改为实际值></span><br><span class="line"><span class="attr">ST</span> = <修改为实际值></span><br><span class="line"><span class="attr">L</span> = <修改为实际值></span><br><span class="line"><span class="attr">O</span> = <修改为实际值> </span><br><span class="line"><span class="attr">OU</span> = <修改为实际值></span><br><span class="line"><span class="attr">CN</span> = <修改为实际值></span><br><span class="line"></span><br><span class="line"><span class="section">[req_ext]</span></span><br><span class="line"><span class="attr">subjectAltName</span> = @alt_names</span><br><span class="line"></span><br><span class="line"><span class="section">[alt_names]</span></span><br><span class="line"><span class="comment"># 这里就是 SAN 字段,以下修改为实际值</span></span><br><span class="line"><span class="attr">DNS.1</span> = vvzero.com</span><br><span class="line"><span class="attr">DNS.2</span> = *.vvzero.com</span><br><span class="line"><span class="attr">IP.1</span> = <span class="number">1.1</span>.<span class="number">1.1</span></span><br><span class="line"><span class="attr">IP.2</span> = <span class="number">1.1</span>.<span class="number">1.2</span></span><br><span class="line"><span class="attr">IP.3</span> = <span class="number">1.1</span>.<span class="number">1.3</span></span><br></pre></td></tr></table></figure><p>然后生成 CSR 文件:</p><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">openssl req -new -key example.key -out example.csr -config req.conf</span><br></pre></td></tr></table></figure><h2 id="检查-CSR-文件中是否存在-SAN-字段"><a href="#检查-CSR-文件中是否存在-SAN-字段" class="headerlink" title="检查 CSR 文件中是否存在 SAN 字段"></a>检查 CSR 文件中是否存在 SAN 字段</h2><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">openssl req -noout -text -<span class="keyword">in</span> example.csr | grep -A 1 <span class="string">"Subject Alternative Name"</span></span><br></pre></td></tr></table></figure><p>如果打印出了你的配置,则代表创建成功。如果没有输出,请检查有没有操作错误。</p><h2 id="用-CA-的私钥签发证书"><a href="#用-CA-的私钥签发证书" class="headerlink" title="用 CA 的私钥签发证书"></a>用 CA 的私钥签发证书</h2><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">openssl x509 -req -days 365 -<span class="keyword">in</span> example.csr -CA /path/to/CAPrivate.pem -CAkey /path/to/CAPrivate.key -CAcreateserial -out example.crt -extensions req_ext -extfile req.conf</span><br></pre></td></tr></table></figure><p>注意其中的 <code>-extensions req_ext -extfile req.conf</code> 是关键,很多教程没有加这个参数,导致生成的证书丢了 SAN 字段。</p><h2 id="验证证书的-SAN-字段"><a href="#验证证书的-SAN-字段" class="headerlink" title="验证证书的 SAN 字段"></a>验证证书的 SAN 字段</h2><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">openssl x509 -text -noout -<span class="keyword">in</span> example.crt | grep -A 1 <span class="string">"Subject Alternative Name"</span></span><br></pre></td></tr></table></figure><p>同上,需要打印出正确的 SAN 配置,才算成功。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在本站之前一篇文章 <a href="https://blog.vvzero.com/2021/01/24/Become-a-CA-and-
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="证书" scheme="https://blog.vvzero.com/tags/%E8%AF%81%E4%B9%A6/"/>
<category term="HTTPS" scheme="https://blog.vvzero.com/tags/HTTPS/"/>
</entry>
<entry>
<title>深入理解以太网网线原理</title>
<link href="https://blog.vvzero.com/2022/09/18/Ethernet-Wiring/"/>
<id>https://blog.vvzero.com/2022/09/18/Ethernet-Wiring/</id>
<published>2022-09-18T07:30:34.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<blockquote><p>译者按:大部分人都知道,百兆以太网只用了 RJ45 端口中的 2 对 4 根线,分别为 TX、RX 的差分信号。<br>千兆以太网用了 RJ45 端口中的全部 4 对 8 根线,但是这 4 对 8 根线是怎么定义的?哪些属于 TX,哪些属于 RX?<br>我也不知道,而且以前居然没有认真去了解过,所以我决定找一篇与此相关的文章翻译一下,分享给大家。</p></blockquote><blockquote><p>本文是“攻玉计划”的一部分,翻译自 <a href="https://www.practicalnetworking.net/stand-alone/ethernet-wiring/">https://www.practicalnetworking.net/stand-alone/ethernet-wiring/</a></p></blockquote><p>当我们谈到“以太网”的时候,我们可能会讨论各种概念,包括所有线缆规格(10BASE-T, 100BASE-TX, 1000BASE-T 等等)。这些协议规定了导线上的电平(即 0/1 信号)是如何传递的,也规定了如何将电平信号解析为数据帧。</p><p>本来,此文只想简单介绍一下交叉线和直通线之间的基本区别,但基于我们的 <a href="https://www.practicalnetworking.net/about/">原则</a>,我们觉得应该更深入一些。</p><p>首先,我们会先介绍一些术语,并消除一些歧义,然后回答一些基本的问题:我们为什么要用交叉线或者直通线?到底什么是双绞线?一个个比特位是如何在线上传播的?最后,我们会综合这些概念,并探讨一下千兆以太网的相关标准。</p><h2 id="术语解释"><a href="#术语解释" class="headerlink" title="术语解释"></a>术语解释</h2><p>即使你刚接触网络通信不久,也应该听说了很多网线相关的概念,例如“以太网”“双绞线”“RJ45”“屏蔽线”“非屏蔽线”等。</p><p>但这些概念代表了什么含义?互相之间又有什么异同?有没有什么概念被误用了?坦白而言,这些概念经常被误用,不妨看看:</p><h3 id="8P8C"><a href="#8P8C" class="headerlink" title="8P8C"></a>8P8C</h3><p>这是网线两端接口的物理标准,表示它有 8 个卡口位(Position)和 8 个触点(Contacts)。这也定义了此塑料透明接口的外形设计和尺寸。</p><p><img src="/static/blog_images/Ethernet-Wiring/1.png"></p><h3 id="RJ45"><a href="#RJ45" class="headerlink" title="RJ45"></a>RJ45</h3><p>标准插座接口(Registered Jack)第 45 号标准定义了线缆中导线的个数以及线序,并规定使用 8P8C 的物理接口。</p><p>特别地,RJ45 定义了两种线序标准:T568a 和 T568b:</p><p><img src="/static/blog_images/Ethernet-Wiring/2.png"></p><p>请注意,两个标准唯一的实际区别是第 2 对线和第 3 对线的颜色不同。</p><blockquote><p>很多人经常用 RJ45 来指代 8P8C 插口,但这是不对的。还有另一种叫 RJ61 的类似标准,也使用了 8P8C 的插口,但其内部的线序不一样。标准插座接口定义家族中还有很多其他 RJxx 的接口,但接线定义和物理尺寸都不一样。</p></blockquote><h3 id="双绞线"><a href="#双绞线" class="headerlink" title="双绞线"></a>双绞线</h3><p>双绞线是一种组合线缆,包含了 8 根独立的导线,其中每两根作为一对,每对的两根线互相绞绕在一起。由此得到 4 对导线,每对导线作为一个数据传输通道。</p><p>导线成对出现这一概念很重要,我们在后文中会讲到,简而言之,这有助于减少电磁干扰(EMI)。</p><p>通常,双绞线有两种规格:<strong>屏蔽线</strong>及<strong>非屏蔽线</strong>。</p><p><img src="/static/blog_images/Ethernet-Wiring/3.png"></p><p>注意,不管哪种规格,网线中都有 4 对导线,也就是 4 个独立的数据通道。</p><h3 id="非屏蔽线"><a href="#非屏蔽线" class="headerlink" title="非屏蔽线"></a>非屏蔽线</h3><p>非屏蔽线(Unshielded Twisted Pair)(UTP)在实际工程部署中更为常见。它对外部的电磁噪声没有额外的防护,但得益于双绞线的固有特性,其数据传输也非常可靠。我们将在后文详细阐述。</p><p>非屏蔽线更便宜,物理韧性更好,也更软。这些优点使得非屏蔽线在大多数场合更受欢迎。</p><h3 id="屏蔽线"><a href="#屏蔽线" class="headerlink" title="屏蔽线"></a>屏蔽线</h3><p>屏蔽线(Shielded Twisted Pair)(STP)在每对双绞线、以及全部 4 对导线最外侧都包有额外的金属屏蔽壳,这有助于隔离信号传输时的电磁噪声。</p><p>但同时,如果屏蔽壳的某个地方出现了破损,或者屏蔽壳在网线两端没有都良好接地,它自身可能会成为一个天线,并且会因为空间中随处可见的无线电波(比如 Wi-Fi 信号)而给信号传输带来额外的电磁噪声。</p><p>更为甚者,屏蔽线必须与带屏蔽的 8P8C 插头一起使用,才能实现全链路端到端的屏蔽功能。</p><p>显然,屏蔽线肯定更贵,也比非屏蔽线更脆弱,因为如果屏蔽线被过度弯曲的话,其屏蔽壳很容易破损。因此,屏蔽线的使用场合比非屏蔽线少得多。</p><p>屏蔽线通常只会用在对电磁屏蔽高度敏感的场合,例如,网线紧挨着发电机或者重型机械的输电线等。</p><h3 id="以太网"><a href="#以太网" class="headerlink" title="以太网"></a>以太网</h3><p>就像我们之前说的,以太网(Ethernet)是一系列标准的合集,其中之一就是不同的接线规格:10BASE-T,100BASE-TX,1000BASE-T 等等。</p><p>以太网协议也定义了每个比特(1 和 0)如何在线缆上传输,以及如何将这些比特流组合为有意义的数据帧。例如,以太网规定每帧数据的前 56 个比特必须是交替出现的 1 和 0(即“前导码”),接下来 8 个比特必须是 10101011(即帧起始标志),再接下来 48 个比特是目标 MAC 地址,然后是 48 个比特的源 MAC 地址……直到整个数据帧被全部传输完毕。</p><p>接下来,我们将讨论以太网标准中不同规格的接线规格。</p><h3 id="BASE-T-相关术语"><a href="#BASE-T-相关术语" class="headerlink" title="BASE T* 相关术语"></a>BASE T* 相关术语</h3><p>本节讲述的概念都与网线内部的导线如何使用相关。例如,哪些用来发送数据,哪些用来接收数据,如何发送信号,以及电压等级。</p><p>BASE T* 这一概念有三个组成部分,所以在我们讲述特定的标准之前,先来单独了解一下它们,以 100 BASE-T 为例:</p><h4 id="100BASE-T-中的“100”"><a href="#100BASE-T-中的“100”" class="headerlink" title="100BASE-T 中的“100”"></a>100BASE-T 中的“100”</h4><p>开头的数字表示网线每秒可以传输多少“兆(百万)”比特,即 Mbps。100Mbps 的网线理论上每秒可传输 100,000,000 个比特,大概每秒 12.5 兆字节(MBps),注意大写的 B 和小写的 b 分别代表字节和比特。</p><p>这一速率的网线有时被称为“快速以太网”,这是相较于 10Mbps 的“普通以太网”以及 1000Mbps 的“吉比特以太网”而言的。</p><h4 id="100BASE-T-中的“BASE”"><a href="#100BASE-T-中的“BASE”" class="headerlink" title="100BASE-T 中的“BASE”"></a>100BASE-T 中的“BASE”</h4><p>base 这个概念是“基带”(baseband)信号的缩写,对应的概念是“宽带”(broadband)信号。这些概念刚出现的时候,其区别是:基带在介质中传输数字信号,宽带在介质中传输模拟信号。</p><p>数字信号和模拟信号的区别在于其可被解析的值个数。</p><p>模拟信号可以表示无数种不同的值,例如,我们可以用一根线上某个特定的电压值来表示一个绿色的像素点,而另一个电压值来表示红色的像素点,以此类推,这样,这根线就能传输一张图片上的每一个像素点。</p><p>数字信号可以表示有限个不同的值,通常就两个:1 和 0。如果上述的图片用一根数字信号线来传输的话,我们会传输一系列 1 和 0 的信号流。接收端可以解析这些二进制数据为一系列数字,例如基于 <a href="https://zh.wikipedia.org/wiki/%E4%B8%89%E5%8E%9F%E8%89%B2%E5%85%89%E6%A8%A1%E5%BC%8F">RGB 颜色编码</a>,就能构造出每一个像素点。</p><p>也就是说,数字信号和模拟信号的主要区别就是,模拟信号线上可获得无穷多中不同的值,而数字信号线上,要么是 0,要么是 1,不可能出现第三种情况。</p><p>如此一来,数字信号传输具有更高的容错率,因为导线上的电压范围只被分为了两种情况(1 或者 0)。</p><blockquote><p>译者按:原文在此处举了更多的例子来详细阐述“模拟信号”和“数字信号”的区别,但译者认为过于冗余,故略去这部分篇幅。</p></blockquote><h4 id="100BASE-T-中的“-T”"><a href="#100BASE-T-中的“-T”" class="headerlink" title="100BASE-T 中的“-T”"></a>100BASE-T 中的“-T”</h4><p>“-T”表示其为双绞线(Twisted Pair)。相似的标准还有“-2”及“-5”,表示其是最大长度为 200 和 500 米的同轴电缆,以及“-SR”和“-LR”,表示其为短距离(Short Range)和长距离(Long Range)光纤。</p><p>以上解释了 BASE T* 相关术语的三个独立部分,我们现在可以探讨下快速以太网的两个重要规范(对于吉比特以太网的相关规范,我们会在后文继续探讨):</p><h4 id="100BASE-T4"><a href="#100BASE-T4" class="headerlink" title="100BASE-T4"></a>100BASE-T4</h4><p>100BASE-T4 使用了网线中全部 4 对 8 根线。其中一对仅仅用于发送信号(TX),一对仅仅用于接收信号(RX)。剩下两对既可以用于 RX 也可以用于 TX,这通过网线两端设备的协商来决定具体用途。</p><p>T4 是双绞线早期的标准之一,但由于其过于复杂且必要性不强,如今已很少使用。</p><h4 id="100BASE-TX"><a href="#100BASE-TX" class="headerlink" title="100BASE-TX"></a>100BASE-TX</h4><p>100BASE-TX 只使用了网线中的 2 对 4 根线,其中一对用于 TX,另一对用于 RX,剩下两根线没有使用。你完全可以做一根只有 4 根线的网线以实现 100BASE-TX 的所有功能,只要插口触点位置正确即可(位号1,2,3,6),但通常网线铺设过程中,另外 4 根线也保留了下来,用于占位,并适配未来可能的场景升级。</p><p>100BASE-TX (包括全部 8 根线)是如今最常用的快速以太网标准。但是,它通常被简写成了 100BASE-T。再强调一下,T 只表示其为双绞线,而 TX 才表示其使用了 1&2 及 3&6 两对线。</p><p>以上介绍可从实用性和技术性的角度帮读者理解相关概念。而在实际情况中,即使你不理解原理,直接使用这些产品也非常简单,就算犯一些小错误,也是允许的。</p><h2 id="为什么使用交叉线"><a href="#为什么使用交叉线" class="headerlink" title="为什么使用交叉线"></a>为什么使用交叉线</h2><p>网上能找到很多“交叉线”及“直通线”应用场景的相关教程,但他们一般很少解释其原理。本节我们会深入探讨一下相关概念。</p><p>100BASE-TX 及 10BASE-T 标准中定义的网线,都包含 8 根导线,两两以双绞线的形式结合为 4 对。</p><p>在这四对线中,实际只用到两对:第 2、3 对。每根线都是单工的介质,也就是说,信号只能按照指定的单方向传输。</p><p>为了实现全双工通讯,某对线将始终沿某个方向传输数据,而另一对线将始终沿相反的方向传输数据。</p><p><img src="/static/blog_images/Ethernet-Wiring/4.png"></p><p>网络接口卡(Network Interface Card)(NIC)的配置会决定哪对线用于发送数据,哪对线用于接收数据。</p><p>使用第 2 对线(1 号和 2 号引脚)发送数据(TX)、且使用第 3 对线(3 号和 6 号引脚)接收(RX)数据的的 NIC 被称作介质相关接口(Media Dependent Interface)(MDI),与之相反,使用第 3 对线作为 RX、第 2 对线作为 TX 的 NIC 被称为交叉模式介质相关接口(Media Dependent Interface Crossover)(MDI-X)。</p><h3 id="电脑之间直连通讯"><a href="#电脑之间直连通讯" class="headerlink" title="电脑之间直连通讯"></a>电脑之间直连通讯</h3><p>假设一台电脑使用 MDI 模式的 NIC ,那么它就总是用第 2 对线发送数据,用第 3 对线接收数据。但如果两台用网线连接在一起的电脑都用第 2 对线发送数据,那么就会产生冲突。与此同时,两台电脑也都无法从第 3 对线上接收到数据。</p><p>因此,网线对需要交叉一下,以便从一台电脑的第 2 对线发送的数据,会被另一台电脑的第 3 对线接收到,反之亦然。</p><p>下图是一个简单的示意(无需在意示意图中线的颜色,这只是为了区分两个不通的路径而已):</p><p><img src="/static/blog_images/Ethernet-Wiring/5.png"></p><p>注意,两台电脑都在独立的通道上发送数据,并且依靠交叉线机制(如图所示中间的 X),两台电脑都能接收到对方发送的数据。</p><p>因此,<strong>两台电脑直连后,必须使用交叉线才能通讯</strong>。</p><h3 id="电脑之间通过交换机通讯"><a href="#电脑之间通过交换机通讯" class="headerlink" title="电脑之间通过交换机通讯"></a>电脑之间通过交换机通讯</h3><p>交换机使得同一网络下两台电脑的通讯变得更简单。交换机的 NIC 都采用 MDI-X 标准,也就是说,交换机总是在第 3 对线上发送数据,在第 2 对线上接收数据(与电脑的 NIC 相反)。</p><p>也就是说,交换机内部有一个交叉的机制,网线本身也就不需要交叉了:</p><p><img src="/static/blog_images/Ethernet-Wiring/6.png"></p><p>可见,<strong>连接在交换机上的电脑可以直接使用直通线</strong>,让交换机处理线序交叉即可。端到端的通讯路径也是一样的:每个设备都在自己的 TX 线上发送数据,在 RX 线上接收数据。</p><h3 id="电脑之间通过两个串联的交换机"><a href="#电脑之间通过两个串联的交换机" class="headerlink" title="电脑之间通过两个串联的交换机"></a>电脑之间通过两个串联的交换机</h3><p>我们刚刚讨论了,两台电脑直连,需要使用交叉线;类似的,两台交换机之间也需要交叉线:</p><p><img src="/static/blog_images/Ethernet-Wiring/7.png"></p><p>在这种情况下,端到端的通讯路径也与上述方式无异。</p><h3 id="路由器与集线器"><a href="#路由器与集线器" class="headerlink" title="路由器与集线器"></a>路由器与集线器</h3><p>那么,路由器和集线器呢?他们用了怎样的 NIC ?</p><p>实际情况是,路由器与电脑类似,使用了 MDI 标准(第 2 对线是 TX,第 3 对线是 RX),因此,你可以将上述图片中的任意电脑换成路由器,通讯路径分析也是一样的。</p><p>而集线器与交换机类似,使用 MDI-X 标准。</p><blockquote><p>译者按:此处的“路由器”是狭义上仅具有“路由”功能的设备,不等于常见的家用无线路由器</p></blockquote><h3 id="以太网线序图"><a href="#以太网线序图" class="headerlink" title="以太网线序图"></a>以太网线序图</h3><p>前文讲到,RJ45 的导线颜色有两种标准:T568a 和 T568b。双绞线两侧所使用的标准决定了其是交叉线还是直通线。</p><p>要想做一根直通线,只要保证线两端的标准一致就行了,都是 T568a 或者都是 T568b:</p><p><img src="/static/blog_images/Ethernet-Wiring/8.png"></p><p>要想做一根交叉线,只需其中一端为 T568a,另一端为 T568b 即可。</p><p><img src="/static/blog_images/Ethernet-Wiring/9.png"></p><p>注意,第 1 对线和第 4 对线没有使用(蓝色对和棕色对)。理论上你的网线中可以去掉这几根线,但是去掉之后剩下的线排列起来有些困难。</p><p>另外,这两对线因为用不到,所以无需交叉。但是,吉比特以太网标准需要用到全部 8 根线,所以为了一致性,通常所有网线对都被交叉。我们会在后文讨论吉比特以太网。</p><p>最后需要注意的是,数据信号本身并不在乎导线的颜色,只要它们连在了正确的接口上就能通讯。但能用不代表就是一个好主意,颜色乱接的话,后续维护起来就是噩梦。</p><h3 id="助记图"><a href="#助记图" class="headerlink" title="助记图"></a>助记图</h3><p>综上所述,我们可以把交叉线和直通线的用法画作一张图:</p><p><img src="/static/blog_images/Ethernet-Wiring/10.png"></p><p>之所以这么摆放,是因为这样画起来更方便。我们把 L1、L2 层的设备画在左右两侧,L3 层设备画在上下两边,然后两两连接。关于网络协议分层请参见 <a href="https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B">OSI 模型</a>。</p><p>小结一下:</p><ul><li>L1/L2 层设备互相连接,需要<strong>交叉线</strong>;</li><li>L1/L2 层设备与 L3 层设备连接,需要<strong>直通线</strong>;</li><li>L3 层设备互相连接,需要<strong>交叉线</strong>。</li></ul><p>或者更简单:</p><ul><li>同则交叉</li><li>异则直通</li></ul><h3 id="自动-MDI-X"><a href="#自动-MDI-X" class="headerlink" title="自动 MDI-X"></a>自动 MDI-X</h3><p>即使知道了什么时候该用直通线,什么时候该用交叉线,对于网络工程师来说,布线也常常是个头疼的事情。</p><p>于是,出现了一个新技术,可以自动分析两台设备的接口模式,并决定是否要交叉 TX/RX。这个技术叫做“自动 MDI-X”。</p><p><strong>使用自动 MDI-X 技术,任意两台设备之间都可以通过直通线连接,并让两端动态确定是否需要交叉 TX 和 RX。</strong></p><p>自动 MDI-X 是 100BASE-T 实现中的一个可选功能,而在所有吉比特以太网设备中是必须的。</p><h4 id="自动-MDI-X-的工作原理"><a href="#自动-MDI-X-的工作原理" class="headerlink" title="自动 MDI-X 的工作原理"></a>自动 MDI-X 的工作原理</h4><p>那么,自动 MDI-X 是如何实现的?两端的设备如何确定哪对线是 TX 或 RX?如果有必要的话,哪一边的设备会交换 TX 和 RX?本节会介绍其内部工作原理。</p><p>记住,交叉线的目的是让一方的 TX 连接到另一方的 RX。也就是说,一方的 NIC 必须用 MDI 标准,另一方必须是 MDI-X 标准。自动 MDI-X 是这样实现这一功能的:</p><p>双方都先生成 1-2047 中的一个随机数,如果随机数是奇数,那么这一方会将自己的 NIC 配置为 MDI-X 模式;如果是偶数,则配置为 MDI 模式。而后双方就开始在其所选择的 TX 线上发送连接脉冲信号。</p><p>如果双方都能在自己的 RX 线上收到对方的连接脉冲,那么就代表协商完成,因为双方都能在 TX 线上发,在 RX 线上收。</p><p>如果双方都不能收到对方的连接脉冲,那么它们肯定都随机到了奇数或都随机到了偶数。因此,它们中的某一方必须将自身的 TX 和 RX 交换。</p><p>但是双方不能同时交换 TX 和 RX,因为这样一来依然是冲突的。因此,我们设计了一个系统,以随机的时间间隔切换 TX/RX 对,直到双方成功协商。</p><p>前文提到随机生成的数字(1-2047)会循环变化,以便双方能选择一个新的标准(MDI 或者 MDI-X)。但是这个数字不能每次加 1,因为这样的话,双方都会从奇数变为偶数,或者偶数变为奇数。换句话说,如果双方一开始都选择了 MDI 模式,如果同时加 1,它们都会切换为 MDI-X 模式,依然无法协商。</p><p>所以,这个随机数使用了叫“线性反馈移位寄存器”的设备以实现循环变化。</p><p>线性反馈移位寄存器(Linear-Feedback Shift Register)(LFSR)是一种算法,它会循环遍历某个范围内的所有数字,而且在每一个循环内不会重复。这些数字以一种可预测的、但随机的顺序循环出现(也就是说,它们不按照大小顺序依次出现,但出现的位置是确定的)。</p><p>举个例子,如果双方随机的初始值分别为 1000 和 2000,那么它们在 LFSR 序列中下一个数字的奇偶性是完全随机的。但如果双方随机到了同一个初始值,那么它们之后随机出来的数字依然是一样的。</p><p>这个过程会一直持续下去,直到双方成功协商。</p><p>现在问题来了,万一双方随机到了相同的数字,然后循环的时间间隔也一样呢?我们可以简单计算一下出现这种情况的几率:</p><p>双方随机到相同数字的几率是 1/2047,双方选择相同时间间隔的几率是 1/4,也就是说,双方同时切换 MDI/MDI-X 标准的几率是 1/8188。</p><p>循环每大概 62ms 运行一遍,也就是说,每秒有大概 16 个循环(每次循环开始时都会重新随机一次)。那么双方在 1 秒之内始终是相同的循环时间的几率是 1/4,294,967,296 (42 亿分之一,1/2^32)。因此,二者结合,双方在一秒内始终随机到相同的随机数、且时间间隔也一样的几率是 1/8,791,798,054,912 (8.7万亿),这种事情几乎不可能发生,就算发生了,你再等一秒就行了。</p><h2 id="为什么使用双绞线"><a href="#为什么使用双绞线" class="headerlink" title="为什么使用双绞线"></a>为什么使用双绞线</h2><p>在网络的物理连线上使用双绞线似乎毋庸置疑。但是,为什么呢?是什么源于让双绞线在网络布线选择中处于主导地位?</p><p>有两个主要的原因,且都与电磁干扰(Electromagnetic Interference)(EMI)相关:</p><ol><li>使用双绞线可以极大减少导线向外辐射电磁干扰;</li><li>使用双绞线可以减少外部电磁干扰对导线本身的影响。</li></ol><p>如果网线需要长距离与其他各种线缆捆绑在一起布置(比如数据中心或者配电箱),以上两个特性都是非常重要的。</p><h3 id="减少-EMI-向外辐射"><a href="#减少-EMI-向外辐射" class="headerlink" title="减少 EMI 向外辐射"></a>减少 EMI 向外辐射</h3><p>只要导线中有电流信号,那就一定会辐射 EMI,进而影响到周围的线缆——也就是通常所说的“串扰”。EMI 辐射可以通过额外的屏蔽装置补偿掉,但是大名鼎鼎的 <a href="https://zh.wikipedia.org/wiki/%E4%BA%9A%E5%8E%86%E5%B1%B1%E5%A4%A7%C2%B7%E6%A0%BC%E6%8B%89%E6%B1%89%E5%A7%86%C2%B7%E8%B4%9D%E5%B0%94">贝尔先生</a> 发明了抵消电磁干扰的绝妙方法。</p><p>他的想法是使用两根导线,其中一根发送原始信号,另一根发送与原始信号完全相反的信号。如此一来,两根线会辐射恰好反向的 EMI,也就互相抵消了。</p><p>简单解释一下,如果一根线发送 +10V 的电压,并辐射了 +0.01V 的 EMI;而另一根线同时发送 -10V 的电压,并辐射了 -0.01V 的 EMI。它们的 EMI 加起来就是 0。</p><p>在电气工程中,这两根线通常被称为“差分对”,可以用 TX+ 和 TX- 来表示。</p><p>这一发明可以实现不需要大量屏蔽的布线方案,也是当前非屏蔽线得以大量使用的原因之一。</p><p>但现在我们只回答了“双绞线”中的“双”,至于为什么还要“绞”,我们继续往下看:</p><h3 id="减少外部-EMI-的吸收"><a href="#减少外部-EMI-的吸收" class="headerlink" title="减少外部 EMI 的吸收"></a>减少外部 EMI 的吸收</h3><p>即使采用了上述的“差分线”,我们也无法避开所有外部的电磁干扰。无线网络、蓝牙、卫星通讯以及手机等都会成为空间中杂散的无线电波来源。</p><p>但幸好贝尔又出现了,并设计了一种非常简单却很有效的方案以屏蔽电磁干扰。</p><p>这一设计基于 EMI 的一个基本概念:离 EMI 辐射源越近,收到的干扰越强。如果两根线交替着靠近 EMI 辐射源,它们就能吸收同样多的辐射。如下图所示:</p><p><img src="/static/blog_images/Ethernet-Wiring/11.png"></p><p>蓝色线的初始电压是 +50V,绿线与之相反为 -50V。EMI 辐射源为图中的红圈,一圈圈向外辐射,离中心越远的圈层干扰电压越小。如果简单将图中每根线上绘制的点受到的干扰电压相加,会发现两根线都增加了 22V 的电压。</p><p>尽管上图导线右侧的电压与左侧的不同,但是两根导线之间的电压差却总是一致的,一直都是 100V。EMI 对两根导线的影响是等同的。经过简单的计算与变换,即可根据最终的 100V 电压差得到初始信号分别为 +50V 和 -50V,如下图所示:</p><p><img src="/static/blog_images/Ethernet-Wiring/12.png"></p><blockquote><p>提醒一下,以上 EMI 干扰相关电压数值被严重夸大了。实际上,正常情况下 EMI 带来的电压扰动是微伏(µV)级别的,即 1/1000,000 V。但原理依然是一样的。</p></blockquote><h3 id="发送比特位"><a href="#发送比特位" class="headerlink" title="发送比特位"></a>发送比特位</h3><p>上文讲到,网线中的数据是以数字信号的方式发送的,也就是一串 1 和 0 的数据流。但双绞线具体是如何发送数据的呢?我们接下来会用一个简化的模型来解释一下。</p><p>发送数据信号,本质上来说就是在某段时间内,给导线加上变化的电压。收发双方会先协商好一个时钟频率,以确定传输的每一单位的电压信号将维持多长时间。简便起见,我们称之为“位号”。在给定的时间点,每一个位号只能表示线上传输的 0 或者 1。</p><p>不同的标准会规定不同的电压等级,但由于我们简化了模型,所以不用管真正的电压是多少。但我们依然会使用 100BASE-TX 标准所规定的电压等级,即 +2.5V 和 -2.5V。</p><p>如果要在某个位号上发送比特 1,发送方会向 TX+ 线上施加 +2.5V 电压;如果要发送比特 0,就向 TX+ 线上发送 -2.5V 电压。</p><p>而 TX- 线则始终相反,比特 1 是 -2.5V,比特 0 是 +2.5V。</p><p>下表是发送 110010101110 二进制序列的相关情况:</p><p><img src="/static/blog_images/Ethernet-Wiring/13.png"></p><p>注意上图不是网线的实体布局,只代表 TX+ 和 TX- 线上交替变化的电压信号。双绞线实际是均匀缠绕的。</p><p>就像之前讲到的,每对中的两根线上的电压总是互为相反量,一切都很整齐,且在水平方向上是对称的。</p><p>现在假设网线附近有 EMI 辐射源,我们在上表中添加一行噪声数据,然后看看最终会变成什么样:</p><p><img src="/static/blog_images/Ethernet-Wiring/14.png"></p><p>注意到,现在这幅图已经不再对称了。两根线仍然发送相反的电压,但加了一个偏置量。</p><p>但是,接收端并不一定要完美的 +2.5V 和 -2.5V,它只需确定哪根线发送更高的电平。如果 TX+ 发送的是高电平,那么这个位号就表示 1,如果 TX- 是更高的电平,那么这个位号就表示 0。</p><p>或者更简单,如果上图中蓝线在上面,就代表 1,黄线在上面,就代表 0。</p><p>通过这种方式,接收端能一位一位地拼凑好整个数据,不管 EMI 对原始电平有怎样的干扰。可见,非屏蔽线不能消除电磁干扰,但能消除电磁干扰的影响。</p><h2 id="吉比特以太网"><a href="#吉比特以太网" class="headerlink" title="吉比特以太网"></a>吉比特以太网</h2><p>我们已经详细介绍了快速以太网(100Mbps),现在我们继续讨论一下吉比特以太网(千兆以太网,1000Mbps 或者 1Gbps)。</p><p>首要的区别就是,吉比特以太网标准需要用到全部 4 对 8 根线,不像百兆网只用到 2 对。因此,在制造吉比特以太网网线时,全部 4 对线都需要交叉。</p><p>前文讲到,RJ45 有两种不同的标准:T-568a 和 T-568b。下图描绘了 4 对线都交叉它们各自的样子:</p><p><img src="/static/blog_images/Ethernet-Wiring/15.png"></p><p>也就是说,吉比特以太网需要自动 MDI-X。所以,你可以直接在千兆网络中使用直通线,然后让网卡自动选择是否需要交叉。</p><p>吉比特以太网有两种布线标准:</p><h3 id="1000BASE-TX"><a href="#1000BASE-TX" class="headerlink" title="1000BASE-TX"></a>1000BASE-TX</h3><p>此标准使用了全部 4 对线,但规定了其中两对线为 TX,另外两对线为 RX。</p><p>理论上讲,这比 1000BASE-T 更简单,但是这需要更昂贵的 Cat6 网线,而不是常见的 Cat5 或 Cat5e 网线。因此,1000BASE-TX 在实际部署中并不常见。</p><h3 id="1000BASE-T"><a href="#1000BASE-T" class="headerlink" title="1000BASE-T"></a>1000BASE-T</h3><p>这是当前应用最广泛的吉比特以太网标准。它以全双工模式同时使用了全部 4 对线,也就是说每对线都可以<strong>同时</strong>用作 RX 和 TX。这是通过“回声消除”技术实现的,我们会在下一节详细阐述。</p><p>使用这种线序标准的最大优势是,你可以在现有的 Cat5e 网线上跑到千兆,而无需升级到更贵的 Cat6 网线。</p><blockquote><p>1000BASE-T 经常被错误地指代 1000BASE-TX。这可能是因为在快速以太网协议中,占主导地位的标准是 100BASE-TX。另外很多时候,线缆标准也经常合起来称作 10/100/1000 BASE-TX。实际上,各个不同速率下,占主导的以太网协议分别是 10BASE-T、100BASE-TX 以及 1000BASE-T。</p></blockquote><h2 id="在同一对线上实现全双工"><a href="#在同一对线上实现全双工" class="headerlink" title="在同一对线上实现全双工"></a>在同一对线上实现全双工</h2><p>上节说到,1000BASE-T 标准可以在同一对线上同时发送和接收数据。在本节我们将解释这是如何实现的。首先,我们来做一个简单的类比。</p><p>你应该有过这样的经历:在跟别人通电话时,如果对方开了免提,你就能在听筒中听到自己的声音。这是因为你的声音从对方的扬声器中发出,在空间中遇到障碍物反射,又被对方的麦克风接收。这就叫做回声。</p><p>高端的电话可以从麦克风收到的声波中剔除扬声器发出的声波——这个技术就叫做回声消除。</p><p>回声消除也是吉比特以太网能够在同一对线上同时发送和接收数据的基础。基本原理就是,如果你知道你发送了什么信号,那么你就能从你收到的信号中将其剔除。</p><p>前文讲到,发送信号本质上是往导线上施加电压。反之,接收信号就是读取导线上的电压值。</p><p>如果发送方往某根导线上施加了以下电压:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+0.5V, +1V, -2V, -1V</span><br></pre></td></tr></table></figure><p>同时,也是发送方,它在同一个导线上读取到了以下电压值:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+1.5V, 0V, -2.5V, +1V</span><br></pre></td></tr></table></figure><p>那么,发送方可做一个减法,用读取值减去其发送的值,这样就能得到对方往这根线上加了多高的电压:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">+1V, -1V, -0.5V, +2V</span><br></pre></td></tr></table></figure><p>如此一来,同一根线就能在同一时间,同时发送和接收数据了。</p><p>再次强调,上述电压值仅仅为了解释原理,实际情况下,电压值可能完全不同,还会包含 EMI 等。同时,我们刚刚只讨论了双绞线中的一根线,另一根线仍然会承载反向的电压。</p><p>使用这种技术,全部 4 对线都可被同时用作 TX 和 RX。另外与前面几节的讨论相同,由于采用了双绞线,它们都还会消除入方向和出方向的 EMI。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>读到这里,你应该对以太网和双绞线的知识点有一个宏观的理解了。这些年我们学习并整理出了这篇文章,原来看似简单的网线居然囊括了这么多技术点,现在感觉很对不起那些被我随便就扔掉的网线了。</p><p>以太网线充满了许多我们本以为理所当然的技术,但实际却很复杂。本文为了便于理解,也省略了很多细节,如果读者有兴趣可以继续研究。</p>]]></content>
<summary type="html">
<blockquote>
<p>译者按:大部分人都知道,百兆以太网只用了 RJ45 端口中的 2 对 4 根线,分别为 TX、RX 的差分信号。<br>千兆以太网用了 RJ45 端口中的全部 4 对 8 根线,但是这 4 对 8 根线是怎么定义的?哪些属于 TX,哪些属于 RX?
</summary>
<category term="攻玉计划" scheme="https://blog.vvzero.com/categories/%E6%94%BB%E7%8E%89%E8%AE%A1%E5%88%92/"/>
<category term="以太网" scheme="https://blog.vvzero.com/tags/%E4%BB%A5%E5%A4%AA%E7%BD%91/"/>
</entry>
<entry>
<title>FreeBSD vs Linux:哪个开源操作系统更强大</title>
<link href="https://blog.vvzero.com/2022/05/25/freebsd-vs-linux-which-open-source-os-is-superior/"/>
<id>https://blog.vvzero.com/2022/05/25/freebsd-vs-linux-which-open-source-os-is-superior/</id>
<published>2022-05-25T12:50:12.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文是“攻玉计划”的一部分,翻译自 <a href="https://www.ateamsystems.com/tech-blog/freebsd-vs-linux-which-open-source-os-is-superior/">https://www.ateamsystems.com/tech-blog/freebsd-vs-linux-which-open-source-os-is-superior/</a></p></blockquote><p>FreeBSD 和 Linux,哪一个更强大?这个问题没那么简单。它们各有春秋,不能一概而论。</p><p>来自我们 A-Team Systems 的专家们有数十年这两个系统的使用经验,所以,我们将详细阐述这两个系统的优势和劣势,供你选择最适合的系统。</p><h2 id="FreeBSD-vs-Linux:功能对比"><a href="#FreeBSD-vs-Linux:功能对比" class="headerlink" title="FreeBSD vs Linux:功能对比"></a>FreeBSD vs Linux:功能对比</h2><p>让我们比较一下这两个 Unix 系统的关键几个方面:</p><h3 id="操作系统完整性"><a href="#操作系统完整性" class="headerlink" title="操作系统完整性"></a>操作系统完整性</h3><p>在这一点上,<a href="https://www.ateamsystems.com/tech-blog/intro-to-freebsd-learn-what-it-is-and-how-it-works/">FreeBSD</a> 更有优势。<br>这是因为 Linux 实际上并不是一个完整的操作系统,而只是一个内核。这是一个很常见的误解,因为很多用户经常把 Linux 看成是一个完整的操作系统。<br>各个 Linux 发行版通常会将必需的软件和库文件打包进系统,这些软件和库文件大多来自 GNU 项目,所以自由软件基金会才将 Linux 称作“GNU/Linux”。</p><p>以下是一些流行的 Linux 发行版:</p><ul><li>Ubuntu</li><li>CentOS</li><li>Fedora</li><li>Arch Linux</li><li>Linux Mint</li><li>Debian</li></ul><h3 id="价格"><a href="#价格" class="headerlink" title="价格"></a>价格</h3><p>关于价格,二者不分胜负。因为作为开源软件,FreeBSD 和 Linux 自然都是免费的。</p><blockquote><p>译者按:在译者看来,开源并不一定意味着免费,很多开源许可证并不允许商用。当然,Linux 和 FreeBSD 是允许免费商用的。</p></blockquote><p>你可能需要为某些额外功能付费,比如服务支持、硬件等。</p><p>任何人都可以免费使用、修改、分发、查阅 Linux 及 FreeBSD 的源代码。但是,任何对 Linux 所作的修改都必须公开源码。<br>而 FreeBSD 并不需要公开,因此,需要在产品中使用相关源码的公司,在这一点上可能更倾向于使用 FreeBSD。</p><h3 id="安全性"><a href="#安全性" class="headerlink" title="安全性"></a>安全性</h3><p>FreeBSD 比 Linux 略微更安全一点。FreeBSD 项目的核心支柱之一就是安全性,并且预先安装了顶级的安全功能,所以在这一点上,毫无疑问它更有优势。</p><p>但这也并不意味着 Linux 不安全。Linux 是高度可配置的,因此可以实现你想要的任何安全特性。但是从操作系统整体角度来看,FreeBSD 的安全性更高。</p><h3 id="硬件与架构支持"><a href="#硬件与架构支持" class="headerlink" title="硬件与架构支持"></a>硬件与架构支持</h3><p>如果比较硬件与架构支持度的话,Linux 绝对是占优势的。Linux 可以在许多不同的平台上运行,但是 FreeBSD 不行。所以,如果你很在乎兼容性和跨平台性,请选择 Linux。</p><p>但这也是一把双刃剑,为了能在大量不同的平台上运行,Linux 必须牺牲一部分性能以换取兼容性。而另一方面,FreeBSD 无需牺牲性能,因为它只需要在有限数量的平台上运行即可。</p><p>由于 Linux 是一个主流的系统,而 FreeBSD 不是,所以设备制造商更倾向于制造兼容 Linux 的软硬件。举个例子,如果你需要经常更新显卡驱动,Linux 会比 FreeBSD 更快获取相关更新支持。</p><p>FreeBSD 对硬件支持的短板大多集中在外设和显卡这种桌面级应用方面。但 FreeBSD 的目标场景是服务器应用,所以这并没有多大影响。</p><h3 id="稳定性"><a href="#稳定性" class="headerlink" title="稳定性"></a>稳定性</h3><p>Linux 和 FreeBSD 都相当稳定可靠。但如果必须得比个高下的话,FreeBSD 会更稳定一点。这又回到了一个事实:FreeBSD 更有组织性。Linux 的稳定性可能会被用户使用的额外组件而拖累。而与此同时,FreeBSD 是一个完整的操作系统,所以它的默认配置更加可靠。总而言之,二者都不缺乏稳定性。</p><h3 id="性能"><a href="#性能" class="headerlink" title="性能"></a>性能</h3><p>虽然业界没有确凿的证据证明 FreeBSD 比 Linux 的性能更优,但是大多数用过二者的用户都说 FreeBSD 在这方面更强一点。这同样归咎于 Linux 的高兼容性。FreeBSD 更精简,无需对环境做额外的判断,因此通常来说它的性能更好。</p><p>FreeBSD 的延迟比 Linux 更低。这里延迟指的是系统时钟中断发生后,到处理器开始运行代码的这段时间。但是大多数应用在 Linux 上跑得更快。</p><h3 id="许可证"><a href="#许可证" class="headerlink" title="许可证"></a>许可证</h3><p>FreeBSD 使用了它自己的 BSD 许可证。该许可证允许用户免费使用该操作系统,并随意修改源码。如果愿意的话,用户也可以发布修改后的源码,或者直接闭源,BSD 许可证允许他们这么做。</p><p>Linux 使用的是 GNU GPL 许可证(GNU通用公共许可协议)。用户可在遵循该许可证限制的情况下随意修改源码。主要区别是,如果你对 Linux 源码作了修改,那么法律意义上你<strong>必须</strong>公开你的代码。</p><blockquote><p>译者按:译者认为这是片面的,如果你修改代码并仅供自己研究使用,那么不需要公开代码。你只需要把源码公开给用户即可。</p></blockquote><p>这个许可证既有好处也有坏处,最大的劣势就是,用户不能用 Linux 开发闭源的系统。而优势是,所有用户都可互相贡献代码,推动整个项目前进。这也是 Linux 能有这么大社区的原因。</p><p>大多数用户无需关心本节的区别,因为大多数人根本不会修改源码。但如果你想使用一个开源的系统来开发闭源的系统,请选择 FreeBSD 而不是 Linux。</p><h3 id="Shell"><a href="#Shell" class="headerlink" title="Shell"></a>Shell</h3><p>从用户角度,大多数人可能认为 Linux 默认的 BASH 比 FreeBSD 的 tcsh 更强大,因为 tcsh 太落伍了。BASH 非常灵活,用户几乎可以在任何 Unix 兼容的系统上做任何事。但这也并不意味着 tcsh 一无是处,tcsh 只是学习路线更陡峭而已。当然,在 FreeBSD 上安装 BASH 也很简单。</p><h3 id="文件系统"><a href="#文件系统" class="headerlink" title="文件系统"></a>文件系统</h3><p>这一方面,二者也是平手。Linux 和 FreeBSD 都采用了非常高效的文件系统。</p><p>FreeBSD 默认使用 ZFS(泽字节文件系统),这绝对是长期存储数据的最佳文件系统之一。它内置了一个磁盘卷管理器,因此允许用户在同一个存储池上创建多个文件系统。因此在发生物理故障、操作失误或者数据损坏的情况下,仍能保证数据一定的可靠性。</p><p>ext4 是大多数 Linux 发行版的默认文件系统。它不如 ZFS 那么灵活,但相当可靠。</p><h3 id="制造商支持"><a href="#制造商支持" class="headerlink" title="制造商支持"></a>制造商支持</h3><p>这一轮 Linux 获胜。IBM、戴尔和惠普的服务器都直接支持运行 Linux。FreeBSD 也能在这些服务器上运行,并且有 A-Team Systems 团队可提供支持。你可以查阅 FreeBSD 的 <a href="https://www.freebsd.org/commercial/hardware/">硬件制造商</a> 以了解当前所支持的硬件。</p><h3 id="更新"><a href="#更新" class="headerlink" title="更新"></a>更新</h3><p>当考虑更新时,你需要关注两方面:更新的便捷度以及更新发布是否及时。</p><p>在便捷度方面,FreeBSD 更胜一筹。用户可以依其意愿选择更新某些组件,比如,你可以只更新某些核心组件,比如内核、源码等,或者只更新它们的子组件。当然也可以全部更新,操作非常简单。</p><p>而对于更新的及时度,Linux 表现得更好。开源公司通常有很强的动力去更新,因此,只要有需求,更新很快就能发布。FreeBSD 可能需要花更长的时间去开发、发布更新,但事实上,Linux 和 FreeBSD 经常可以同时获取相关更新,因为他们使用了同样的上游项目。</p><h3 id="包管理"><a href="#包管理" class="headerlink" title="包管理"></a>包管理</h3><p>在 FreeBSD 上安装软件包很简单。FreeBSD Ports 项目包含了将近 40000 个软件源,用户或管理员可以方便快捷地安装它们。每个软件源都有针对用户实际系统的相关补丁,以确保软件能在特定平台上正常运行。</p><p>而不同 Linux 发行版的包管理工具就参差不齐了,有些非常棒,有些就很一般。以下是一些做得比较好的包管理工具:</p><ul><li>DPKG - Debian</li><li>RPM - Red Hat</li><li>Pacman Package Manager</li><li>Pkgsrc</li><li>Portage</li></ul><h3 id="开发维护人员"><a href="#开发维护人员" class="headerlink" title="开发维护人员"></a>开发维护人员</h3><p>FreeBSD 核心团队有 9 名成员,并在世界范围内有大约 500 名代码贡献者。这个团队负责调试、开发并优化主线代码仓库。大多数贡献者都是不求回报的志愿者,核心团队成员由所有活跃的贡献者每两年一次投票选出。</p><p>而 Linux 内核由 Linus Torvals 先生管理维护,他也是 Linux 的缔造者。Linus 先生对 Linux 的新功能拥有最终决定权。</p><h2 id="FreeBSD-与-Linux-到底如何不同?"><a href="#FreeBSD-与-Linux-到底如何不同?" class="headerlink" title="FreeBSD 与 Linux 到底如何不同?"></a>FreeBSD 与 Linux 到底如何不同?</h2><p>FreeBSD 是一个完整的操作系统,拥有内核、驱动、文档以及各种工具。Linux 只有内核以及部分驱动,并且依赖第三方系统软件才能运行。FreeBSD 的源码使用 BSD 许可证,而 Linux 使用 GPL 许可证。</p><p>Linux 广泛支持各种硬件,而 FreeBSD 支持的硬件非常有限。Linux 也是当前市场上最流行的开源操作系统,所以不缺各种支持。FreeBSD 也有非常忠实的用户群,但远不能与 Linux 的用户群相提并论。</p><h2 id="FreeBSD-比-Linux-更安全吗?"><a href="#FreeBSD-比-Linux-更安全吗?" class="headerlink" title="FreeBSD 比 Linux 更安全吗?"></a>FreeBSD 比 Linux 更安全吗?</h2><p>FreeBSD 的安全问题通常比 Linux 更少,但是差距并不大。Linux 的用户比 FreeBSD 更多,所以也会发现更多的漏洞。由于 FreeBSD 提供了完整的操作系统,所以其默认配置非常安全。</p><p>Linux 系统的安全性取决于用户的配置。由于其高度的可定制化,Linux 用户可以让他们的系统变得几乎牢不可破。</p><h2 id="FreeBSD-可以运行-Linux-的程序吗?"><a href="#FreeBSD-可以运行-Linux-的程序吗?" class="headerlink" title="FreeBSD 可以运行 Linux 的程序吗?"></a>FreeBSD 可以运行 Linux 的程序吗?</h2><p>FreeBSD 提供了与 Linux 的 <a href="https://docs.freebsd.org/en/books/handbook/linuxemu/">二进制兼容性</a>。这允许用户在 FreeBSD 系统上安装并运行 Linux 的二进制程序。FreeBSD 上默认没有安装 Linux 的相关库文件,但可以从 FreeBSD Ports 上安装,或者手动安装。</p><h2 id="为什么-Linux-比-FreeBSD-更流行?"><a href="#为什么-Linux-比-FreeBSD-更流行?" class="headerlink" title="为什么 Linux 比 FreeBSD 更流行?"></a>为什么 Linux 比 FreeBSD 更流行?</h2><p>这其中有多个原因。一方面,FreeBSD 缺乏硬件支持,这就限制了用户使用它的场景。</p><p>另一个原因是 FreeBSD 缺乏商业支持。有如 Red Hat 这样的大公司能确保 Linux 及时获取更新支持,但对于 FreeBSD 而言这是不可能的。</p><p>最后,Linux 拥有数量众多的软件,允许其发挥最大的灵活性和可用性。FreeBSD 提供了一些预编译的软件包,但仍无法与 Linux 相比。</p><h2 id="FreeBSD-和-Linux-哪个用起来更简单?"><a href="#FreeBSD-和-Linux-哪个用起来更简单?" class="headerlink" title="FreeBSD 和 Linux 哪个用起来更简单?"></a>FreeBSD 和 Linux 哪个用起来更简单?</h2><p>FreeBSD 和 Linux 都需要一定的学习成本。但是,FreeBSD 相对而言更易学习使用,因为它没有那么多学习选项,例如发行版、包管理工具等等。</p><p>大多数开发者认为,比起 FreeBSD,Linux 太混乱了。对于同一个任务有无数种实现方案,并且不同的用户对应该如何选择方案有不同(且强烈)的意见。Linux 社区是一个快节奏的社区,经常经历变化。因此,很多用户更喜欢 FreeBSD 社区的一致性和条理性。</p><h2 id="哪个更快?"><a href="#哪个更快?" class="headerlink" title="哪个更快?"></a>哪个更快?</h2><p>总的来说,FreeBSD 通常比 Linux 更快。这主要是因为它是一个完整的系统。此外,FreeBSD 的延迟比 Linux 低,也就意味着它能更快处理输入。有如网飞、苹果和思科之类的公司会采用 FreeBSD 以获取这种处理速度优势。</p><p>Linux 也能获得类似的速度,但是,这取决于你的配置。还值得注意的是,大多数应用程序在 Linux 上运行得更快。因此大多数超级计算机会使用 Linux 而不是 FreeBSD。</p><h2 id="FreeBSD-vs-Linux:哪一个最适合你?"><a href="#FreeBSD-vs-Linux:哪一个最适合你?" class="headerlink" title="FreeBSD vs Linux:哪一个最适合你?"></a>FreeBSD vs Linux:哪一个最适合你?</h2><p>FreeBSD 和 Linux 都可作为开源用户的选择。最主要的区别就是,FreeBSD 更完整,更标准化,而 Linux 只提供了内核及驱动,需要第三方软件支持。</p><p>如果想要尽可能少地配置系统,FreeBSD 是更好的选择。但是,Linux 提供了更多的自定义选项,对于想要定制系统的人是个更好的选择。另外,如果你有硬件平台限制的话,Linux 的支持性可能更好点。</p><p>如果你喜欢紧跟技术潮流,Linux 的新技术、新特性和更新速度肯定会让你满意。如果稳定性、性能和安全性对你来说更重要,FreeBSD 也许更适合你。</p>]]></content>
<summary type="html">
<blockquote>
<p>本文是“攻玉计划”的一部分,翻译自 <a href="https://www.ateamsystems.com/tech-blog/freebsd-vs-linux-which-open-source-os-is-superior/">https:
</summary>
<category term="攻玉计划" scheme="https://blog.vvzero.com/categories/%E6%94%BB%E7%8E%89%E8%AE%A1%E5%88%92/"/>
<category term="FreeBSD" scheme="https://blog.vvzero.com/tags/FreeBSD/"/>
<category term="Linux" scheme="https://blog.vvzero.com/tags/Linux/"/>
</entry>
<entry>
<title>如何在 Markdown 中修改字体颜色</title>
<link href="https://blog.vvzero.com/2022/05/06/How-to-apply-color-in-Markdown/"/>
<id>https://blog.vvzero.com/2022/05/06/How-to-apply-color-in-Markdown/</id>
<published>2022-05-06T12:21:48.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文是“攻玉计划”的一部分,翻译自 <a href="https://stackoverflow.com/questions/35465557/how-to-apply-color-in-markdown">https://stackoverflow.com/questions/35465557/how-to-apply-color-in-markdown</a></p></blockquote><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>我想用 Markdown 记录文字信息,但我搜了一圈 Google,发现 Markdown 不支持修改字体颜色。而且 StackOverflow 和 GitHub 的 Markdown 编辑模式也不支持指定文字颜色。</p><p>有什么办法可以在 Markdown 里指定文字颜色吗?</p><h2 id="最佳答案"><a href="#最佳答案" class="headerlink" title="最佳答案"></a>最佳答案</h2><p><strong>太长不看系列:</strong></p><p>Markdown 自身并不支持色彩配置,但你可以在 Markdown 中添加 HTML 代码,例如:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">span</span> <span class="attr">style</span>=<span class="string">"color:blue"</span>></span>这是**蓝色**的文字<span class="tag"></<span class="name">span</span>></span></span><br></pre></td></tr></table></figure><p><strong>以下是长回答:</strong></p><p>根据官方的 <a href="http://daringfireball.net/projects/markdown/syntax#html">语法规则</a>:</p><blockquote><p>Markdown 只有一个用途,就是作为编写网页的一种语法格式。<br>Markdown 不能取代 HTML,甚至不能实现 HTML 的大部分功能。它的语法很简单,只能覆盖很小一部分的 HTML 标签。Markdown 并不是为了让你更方便地插入 HTML 标签。我的观点是,HTML 标签已经很方便了,而 Markdown 是为了让人更容易读、写、改。HTML 是用于发布的格式,而 Markdown 是给人写的格式。因此,<strong>Markdown的语法格式只用来处理可以用纯文本表达的信息。</strong><br>对于任何 Markdown 未实现的功能,直接插入 HTML 代码即可。</p></blockquote><p>由于 Markdown 并不是用来发布的格式,修改字体的颜色已经超出了 Markdown 的处理范围。但你仍可以插入裸的 HTML 代码(因为 HTML 是发布级的格式),例如以下 Markdown 文本:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">包含 <span class="tag"><<span class="name">span</span> <span class="attr">style</span>=<span class="string">"color:blue"</span>></span>*蓝色* 文字<span class="tag"></<span class="name">span</span>></span>的 Markdown 语句。</span><br></pre></td></tr></table></figure><p>将会转换为以下 HTML 代码:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">p</span>></span>包含 <span class="tag"><<span class="name">span</span> <span class="attr">style</span>=<span class="string">"color:blue"</span>></span><span class="tag"><<span class="name">em</span>></span>蓝色<span class="tag"></<span class="name">em</span>></span> 文字<span class="tag"></<span class="name">span</span>></span>的 Markdown 语句。<span class="tag"></<span class="name">p</span>></span></span><br></pre></td></tr></table></figure><p>目前,StackOverflow (也许 GitHub 也是)会把 HTML 代码原原本本地显示出来(因为安全性考虑),因此你无法在这些地方实现文字颜色的功能,但你可以在 Markdown 的任何标准实现中使用。</p><p>另一种解决方案是,直接使用 <a href="http://maruku.rubyforge.org/proposal.html#attribute_lists">Markuru</a> 的非标准属性表。此标准后续被 <a href="https://pythonhosted.org/Markdown/extensions/attr_list.html">其它</a> <a href="https://pythonhosted.org/Markdown/extensions/attr_list.html">一些人</a>(可能还有其他类似理念的方案,比如 <a href="http://pandoc.org/MANUAL.html#divs-and-spans">pandoc 中的 div 和 span 参数</a>)继承开发。如果用了这种方案,你就可以为一段文字或者行内元素配置一个类,然后用 CSS 给这个类定义色彩属性。但显然,你必须使用支持这些非标准方案的编辑工具,并且这样写出来的文档也没法移植到其他系统上。</p><h2 id="其他回答-1"><a href="#其他回答-1" class="headerlink" title="其他回答 1"></a>其他回答 1</h2><p>如果你不想嵌入 HTML,只想用纯净的 Markdown 语句,可以尝试添加 emoji 以便强调指定语句。比如:⚠️警告⚠️,🔴重要❗🔴 或者 🔥新功能🔥。</p><h2 id="其他回答-2"><a href="#其他回答-2" class="headerlink" title="其他回答 2"></a>其他回答 2</h2><p>尽管 Markdown 不支持文字颜色属性,但你可以用 CSS 重定义一些格式标签,以便用它们来修改文字颜色。当然,你可以选择是否保留这些格式标签的原有属性。</p><p>例如:</p><figure class="highlight css"><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><br><span class="line">s { <span class="attribute">text-decoration</span>: none; } // 删除线</span><br><span class="line"><span class="selector-tag">em</span> { <span class="attribute">font-style</span>: normal; <span class="attribute">font-weight</span>: bold; } // 斜体</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">// 增加颜色属性</span><br><span class="line">s { <span class="attribute">color</span>: green }</span><br><span class="line"><span class="selector-tag">em</span> { <span class="attribute">color</span>: blue }</span><br></pre></td></tr></table></figure><p>参见 <a href="https://stackoverflow.com/questions/25535836/how-to-restyle-em-tag-to-be-bold-instead-of-italic">如何使 em 标签标记粗体而不是斜体</a>。</p><p>然后,在 Markdown 文本中这样使用:</p><figure class="highlight plaintext"><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><br><span class="line">_这是蓝色_</span><br></pre></td></tr></table></figure><h2 id="其他回答-3"><a href="#其他回答-3" class="headerlink" title="其他回答 3"></a>其他回答 3</h2><p>可以换个思路,你可以用各种颜色的 Unicode 字符以满足相关需求,比如 🔴,U+1F534(大红圈)。</p><p>举个例子,在我 GitHub 里的硬件项目中,我会用以下的字符以注明接线的颜色:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">🔴 红色: +5V</span><br><span class="line">🟠 橙色: +3.3V</span><br><span class="line">⚫ 黑色: GND</span><br><span class="line">⚪ 白色: GND (拉低)</span><br><span class="line">🟣 紫色: I2C 信号线</span><br><span class="line">🟢 绿色: 时钟信号</span><br><span class="line">🟡 黄色: WS2812 信号</span><br><span class="line">🔵 蓝色: 电阻桥(模拟)输入</span><br></pre></td></tr></table></figure><p>这也许能帮到你,你可以直接复制粘贴上述字符到你的文档中,或者直接在网上搜例如“Unicode 紫色方块”之类。当然,它们也叫做 emoji。</p>]]></content>
<summary type="html">
<blockquote>
<p>本文是“攻玉计划”的一部分,翻译自 <a href="https://stackoverflow.com/questions/35465557/how-to-apply-color-in-markdown">https://stackoverflo
</summary>
<category term="攻玉计划" scheme="https://blog.vvzero.com/categories/%E6%94%BB%E7%8E%89%E8%AE%A1%E5%88%92/"/>
<category term="Markdown" scheme="https://blog.vvzero.com/tags/Markdown/"/>
</entry>
<entry>
<title>如何在 ESP8266 上选用合适的引脚</title>
<link href="https://blog.vvzero.com/2022/05/05/esp8266-pinout-reference-gpios/"/>
<id>https://blog.vvzero.com/2022/05/05/esp8266-pinout-reference-gpios/</id>
<published>2022-05-05T10:40:55.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文是“攻玉计划”的一部分,翻译自 <a href="https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/">https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/</a></p></blockquote><p>本文旨在介绍 ESP8266 的引脚定义、引脚功能及如何使用它们。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/1.png"></p><p>ESP-12E 模块拥有 17 个 GPIO 引脚。但在各个开发板上,ESP8266 芯片的 GPIO 引脚并不一定全部引出,而且某些引脚不建议使用,某些引脚有非常特殊的功能。</p><p>本文将指导你如何正确使用 ESP8266 的各个 GPIO,避免用错引脚而浪费时间。</p><h2 id="ESP12-E-模块引脚定义"><a href="#ESP12-E-模块引脚定义" class="headerlink" title="ESP12-E 模块引脚定义"></a>ESP12-E 模块引脚定义</h2><p>下图阐述了 ESP-12E 模块的引脚定义。当你的项目使用裸 ESP-12E/F 模块的时候,可以参考此图。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/2.png"></p><blockquote><p>🔵注意:某些开发板可能不能使用全部的引脚,但相同的引脚在不同的开发板上,功能肯定是一样的。</p></blockquote><p>当前市场上有很多不同的 ESP8266 模块/开发板,它们的形状、大小、可用 GPIO 数目各不相同。但最常用的是 ESP-01(S)、ESP-12E/F、NodeMCU 开发板以及 Wemos D1 Mini 开发板。你可以自己搜索这些开发板模块的区别。</p><h2 id="ESP-01-S-引脚定义"><a href="#ESP-01-S-引脚定义" class="headerlink" title="ESP-01(S) 引脚定义"></a>ESP-01(S) 引脚定义</h2><p>如果你在用 ESP-01(S) 的板子,可以参考下图的 GPIO 引脚定义。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/3.png"></p><h2 id="ESP-12E-NodeMCU-开发板"><a href="#ESP-12E-NodeMCU-开发板" class="headerlink" title="ESP-12E NodeMCU 开发板"></a>ESP-12E NodeMCU 开发板</h2><p>ESP-12E NodeMCU 开发板的引脚定义如下图所示。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/4.png"></p><h2 id="Wemos-D1-Mini-开发板"><a href="#Wemos-D1-Mini-开发板" class="headerlink" title="Wemos D1 Mini 开发板"></a>Wemos D1 Mini 开发板</h2><p>Wemos D1 Mini 开发板的引脚定义如下图所示。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/5.png"></p><h2 id="ESP8266-的外设"><a href="#ESP8266-的外设" class="headerlink" title="ESP8266 的外设"></a>ESP8266 的外设</h2><p>ESP8266 的外设包括:</p><ul><li>17 个 GPIO</li><li>SPI</li><li>I2C(软件实现)</li><li>I2S(支持 DMA)</li><li>UART</li><li>10 位 ADC</li></ul><h2 id="推荐使用的引脚"><a href="#推荐使用的引脚" class="headerlink" title="推荐使用的引脚"></a>推荐使用的引脚</h2><p>需要注意的一点是,ESP8266 开发板上丝印的引脚号,并不是芯片真正的 GPIO 编号。比如,D0 是 GPIO16,D1 是 GPIO5。</p><p>下表说明了 ESP8266 开发板上丝印的引脚号与实际 GPIO 编号的对应关系,并提醒你哪些引脚在使用时需要注意。</p><p>绿色标记的引脚可以随意使用;黄色标记的引脚可以使用,但需要注意它们在芯片启动时的影响,可能带来意外的问题。红色标记的引脚不建议用作输入或输出功能。</p><table><thead><tr><th>丝印标签</th><th>GPIO</th><th>可作为输入</th><th>可作为输出</th><th>备注</th></tr></thead><tbody><tr><td>D0</td><td>GPIO16</td><td>不可用于中断</td><td>不可用于 PWM 或 I2C</td><td>🟠启动时为高电平<br>用于从深度睡眠中唤醒</td></tr><tr><td>D1</td><td>GPIO5</td><td>🟢是</td><td>🟢是</td><td>通常用作 <strong>SCL</strong> (I2C)</td></tr><tr><td>D2</td><td>GPIO4</td><td>🟢是</td><td>🟢是</td><td>通常用作 <strong>SDA</strong> (I2C)</td></tr><tr><td>D3</td><td>GPIO0</td><td>已被上拉</td><td>🟢是</td><td>与 FLASH 按键连接,如果拉低则会启动失败</td></tr><tr><td>D4</td><td>GPIO2</td><td>已被上拉</td><td>🟢是</td><td>🟠启动时为高电平<br>连接板载 LED,如果拉低则会启动失败</td></tr><tr><td>D5</td><td>GPIO14</td><td>🟢是</td><td>🟢是</td><td><strong>SPI</strong> (SCLK)</td></tr><tr><td>D6</td><td>GPIO12</td><td>🟢是</td><td>🟢是</td><td><strong>SPI</strong> (MISO)</td></tr><tr><td>D7</td><td>GPIO13</td><td>🟢是</td><td>🟢是</td><td><strong>SPI</strong> (MOSI)</td></tr><tr><td>D8</td><td>GPIO15</td><td>已被下拉至 GND</td><td>🟡是</td><td><strong>SPI</strong> (CS)<br>如果拉高则会启动失败</td></tr><tr><td>RX</td><td>GPIO3</td><td>🟡是</td><td>🔴RX 引脚</td><td>🟠启动时为高电平</td></tr><tr><td>TX</td><td>GPIO1</td><td>🔴TX 引脚</td><td>🟡是</td><td>🟠启动时为高电平<br>启动时的调试输出引脚,如果拉低会启动失败</td></tr><tr><td>A0</td><td>ADC0</td><td>🟢模拟输入</td><td>🔴禁用</td><td></td></tr></tbody></table><p>接下来的篇幅将更详细地介绍 ESP8266 GPIO 引脚的功能。</p><h3 id="连接-FLASH-芯片的引脚"><a href="#连接-FLASH-芯片的引脚" class="headerlink" title="连接 FLASH 芯片的引脚"></a>连接 FLASH 芯片的引脚</h3><p>GPIO6 到 GPIO11 通常用于连接 FLASH 芯片,所以,不推荐使用这几个引脚。</p><h3 id="启动过程中用到的引脚"><a href="#启动过程中用到的引脚" class="headerlink" title="启动过程中用到的引脚"></a>启动过程中用到的引脚</h3><p>如果某些引脚被拉高或者拉低,ESP8266 可能会启动失败。下表是部分引脚在启动时的状态:</p><ul><li><strong>GPIO16</strong>:启动时为高电平</li><li><strong>GPIO0</strong>:如果被拉低,则启动失败</li><li><strong>GPIO2</strong>:启动时为高电平,如果被拉低,则启动失败</li><li><strong>GPIO15</strong>:如果被拉高,则启动失败</li><li><strong>GPIO3</strong>:启动时为高电平</li><li><strong>GPIO1</strong>:启动时为高电平,如果被拉低,则启动失败</li><li><strong>GPIO10</strong>:启动时为高电平</li><li><strong>GPIO9</strong>:启动时为高电平</li></ul><h3 id="启动时为高电平的引脚"><a href="#启动时为高电平的引脚" class="headerlink" title="启动时为高电平的引脚"></a>启动时为高电平的引脚</h3><p>以下引脚在启动时会输出 3.3V 的高电平。如果你在这些引脚上接了继电器之类的外设,可能会带来一些问题:</p><ul><li>GPIO16</li><li>GPIO3</li><li>GPIO1</li><li>GPIO10</li><li>GPIO9</li></ul><p>此外,其他引脚(除了 GPIO5 和 GPIO4),在启动时会输出低电平信号,同样可能带来问题。你可以阅读 <a href="https://rabbithole.wwwdotorg.org/2017/03/28/esp8266-gpio.html">此文章</a> 以详细了解各个 GPIO 在启动时的状态。</p><blockquote><p>🟢如果需要控制继电器或功率管,GPIO4 和 GPIO5 是最安全的引脚。</p></blockquote><h3 id="模拟输入引脚"><a href="#模拟输入引脚" class="headerlink" title="模拟输入引脚"></a>模拟输入引脚</h3><p>ESP8266 只有一个引脚支持模拟输入,此引脚叫 ADC0,丝印上常标记为 A0。</p><p>如果使用 ESP8266 裸芯片(ESP-12E/F)的话,此引脚的电压输入范围为 0-1V。如果使用了 NodeMCU 之类的开发板,那么电压输入范围就是 0-3.3V,因为开发板上已经集成了分压器。</p><h3 id="板载-LED"><a href="#板载-LED" class="headerlink" title="板载 LED"></a>板载 LED</h3><p>大多数 ESP8266 模块均有一个内置的 LED,通常连在 GPIO2 上。LED 亮灭的逻辑是反向的,GPIO2 为高电平时,LED 熄灭;GPIO2 低电平时,LED 亮起。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/6.png"></p><h3 id="复位引脚"><a href="#复位引脚" class="headerlink" title="复位引脚"></a>复位引脚</h3><p>当 RST 引脚被拉低时,ESP8266 将被复位。按开发板上的 RESET 按键同理。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/7.png"></p><h3 id="GPIO0"><a href="#GPIO0" class="headerlink" title="GPIO0"></a>GPIO0</h3><p>当 GPIO0 被拉低时,复位 ESP8266,芯片将进入 bootloader 模式。按开发板上的 FLASH/BOOT 按钮同理。</p><p><img src="/static/blog_images/esp8266-pinout-reference-gpios/8.png"></p><h3 id="GPIO16"><a href="#GPIO16" class="headerlink" title="GPIO16"></a>GPIO16</h3><p>GPIO16 可被用于从深度睡眠中唤醒 ESP8266。要实现此功能,需要将 GPIO16 连接在 RST 引脚上。关于如何实现深度睡眠,请搜索并参考 Arduino 官网上的相关案例。</p><h3 id="I2C"><a href="#I2C" class="headerlink" title="I2C"></a>I2C</h3><p>ESP8266 没有硬件 I2C 引脚,但可以用软件模拟,所以你可以使用任意引脚实现 I2C。通常我们会使用以下引脚:</p><ul><li><strong>GPIO5</strong>:SCL</li><li><strong>GPIO4</strong>:SDA</li></ul><h3 id="SPI"><a href="#SPI" class="headerlink" title="SPI"></a>SPI</h3><p>ESP8266 上的 SPI 引脚如下:</p><ul><li><strong>GPIO12</strong>:MISO</li><li><strong>GPIO13</strong>:MOSI</li><li><strong>GPIO14</strong>:SCLK</li><li><strong>GPIO15</strong>:CS</li></ul><h3 id="PWM-引脚"><a href="#PWM-引脚" class="headerlink" title="PWM 引脚"></a>PWM 引脚</h3><p>我们可以在 ESP8266 的所有引脚(GPIO0 至 GPIO15)上软件实现 PWM 功能。ESP8266 上的 PWM 有 10 位精度。关于如何实现 PWM 功能,请搜索并参考 Arduino 官网上的相关案例。</p><h3 id="中断引脚"><a href="#中断引脚" class="headerlink" title="中断引脚"></a>中断引脚</h3><p>ESP8266 的所有 GPIO 引脚均支持中断,除了 GPIO16。相关案例请搜索并参考 Arduino 官网上的相关案例。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>希望本文能解决你对 ESP8266 GPIO 的相关疑惑,祝好!</p>]]></content>
<summary type="html">
<blockquote>
<p>本文是“攻玉计划”的一部分,翻译自 <a href="https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/">https://randomnerdtutorials.com/
</summary>
<category term="攻玉计划" scheme="https://blog.vvzero.com/categories/%E6%94%BB%E7%8E%89%E8%AE%A1%E5%88%92/"/>
<category term="Arduino" scheme="https://blog.vvzero.com/tags/Arduino/"/>
<category term="esp8266" scheme="https://blog.vvzero.com/tags/esp8266/"/>
</entry>
<entry>
<title>如何 DIY 一个苏康码与行程码“双码合一”的健康码 APP</title>
<link href="https://blog.vvzero.com/2022/04/08/diy-to-combine-sukangma-and-xingchengma/"/>
<id>https://blog.vvzero.com/2022/04/08/diy-to-combine-sukangma-and-xingchengma/</id>
<published>2022-04-08T01:56:04.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<h2 id="背景介绍"><a href="#背景介绍" class="headerlink" title="背景介绍"></a>背景介绍</h2><p>众所周知的背景:</p><ol><li>苏康码打开很慢,在支付宝中如果没有快捷键,需要以下步骤:点击打开支付宝 -> 点击健康码 -> 点击立即查看,如果设置了长按图标打开健康码,也得至少两步;其他 APP 比如“苏周到”,可以实现长按快捷键后一步访问,但是其中存在三个步骤:APP 启动 -> 健康码小程序启动 -> 加载网页,这种不可理喻的框架,在某些低端机上冷启动,可能需要长达数十秒的时间;</li><li>行程卡打开也很慢,在微信小程序中打开,不知道为什么每次都让我确认一下“同意并授权运营商查询”,严重影响效率;</li><li>很多地方两个码都要查,于是慢*2,话说我也不知道为什么有关部门不把这两个码合二为一……</li></ol><p>所以干脆 DIY 一个。</p><h2 id="开发流程"><a href="#开发流程" class="headerlink" title="开发流程"></a>开发流程</h2><p>我想做出这样的效果:打开 APP 后,直接显示苏康码,滑动屏幕可切换到行程码,不需要任何多余的点击动作。</p><h3 id="技术栈选择"><a href="#技术栈选择" class="headerlink" title="技术栈选择"></a>技术栈选择</h3><p>我没有任何 APP 开发经验,所以相当于新手。因为最近用 C# 和 .NET 框架比较多,<a href="https://v2ex.com/t/844404">经 V2EX 网友提醒</a>,我选择了 Xamarin 框架。据说如果新手想快速尝试跨平台 APP 开发,用 flutter 比较好,但是……whatever,支持一下微软,及其宇宙第一 IDE。</p><h3 id="获取苏康码直链"><a href="#获取苏康码直链" class="headerlink" title="获取苏康码直链"></a>获取苏康码直链</h3><p>既然苏康码本质上是网页,而且我在朋友圈了解到,可以获取到直链而且是不需要认证的,只要 token 对就行了,那就简单了。</p><p>于是我决定使用 <a href="https://www.telerik.com/fiddler/fiddler-classic">Fiddler</a> 抓包,大致步骤就是:配好 Fiddler 的监听端口,然后保证电脑和手机在同一个局域网内,手机在 WiFi 设置里配置好 Fiddler 的代理。</p><p><img src="/static/blog_images/diy-to-combine-sukangma-and-xingchengma/1.png"></p><p>测试的 APP 是苏周到,不出所料,苏康码链接是 HTTPS 的,想解密只能在手机上安装一个证书然后中间人了。</p><p><img src="/static/blog_images/diy-to-combine-sukangma-and-xingchengma/2.png"></p><p>导出 Fiddler 的证书并复制到手机上,MIUI 安装证书的步骤也很简单,记得在抓完包之后删除这个证书就好。</p><p><img src="/static/blog_images/diy-to-combine-sukangma-and-xingchengma/3.png"></p><p>然后就可以解密 HTTPS 流量。理论上,对于现在的 Android 版本,APP 可以选择不信任用户安装的证书,但还好,苏康码并没有采取这样的机制。</p><p>解析出来的苏康码的直链很简单,就是 <a href="https://jsstm.jszwfw.gov.cn/jkmIndex.html?token=xxxxxxxxxxxxxxxx&uuid=xxxxxxxxxxxxxxx">https://jsstm.jszwfw.gov.cn/jkmIndex.html?token=xxxxxxxxxxxxxxxx&uuid=xxxxxxxxxxxxxxx</a> 这样的格式,直接访问就可以看到自己的苏康码界面。</p><h3 id="获取行程码直链"><a href="#获取行程码直链" class="headerlink" title="获取行程码直链"></a>获取行程码直链</h3><p>行程码直链更简单,直接就可以搜到: <a href="https://xc.caict.ac.cn/">https://xc.caict.ac.cn/</a> 。有趣的是,行程码居然是用 Vue 框架做的,如果只是普通的 HTML 表单页面的话,我也许会做一个自动发验证码查询的功能。</p><h3 id="APP-开发"><a href="#APP-开发" class="headerlink" title="APP 开发"></a>APP 开发</h3><p>安装移动端开发相关 SDK 后,启动宇宙最强 IDE,新建一个空项目叫 <code>ShuangShuangMa</code> (随便起一个名字,双双码),然后面向 Google 编程……基本上只要查一下怎么使用 WebView 以及如何实现滑动切换页面就好了。</p><p>代码非常简单,xaml 页面如下:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span> ?></span></span><br><span class="line"><span class="tag"><<span class="name">CarouselPage</span> <span class="attr">xmlns</span>=<span class="string">"http://xamarin.com/schemas/2014/forms"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xmlns:x</span>=<span class="string">"http://schemas.microsoft.com/winfx/2009/xaml"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">x:Class</span>=<span class="string">"ShuangShuangMa.MainPage"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">ContentPage</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">StackLayout</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">WebView</span> <span class="attr">x:Name</span>=<span class="string">"WebView_SuKangMa"</span> <span class="attr">VerticalOptions</span>=<span class="string">"FillAndExpand"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">Button</span> <span class="attr">Text</span>=<span class="string">"点击刷新"</span> <span class="attr">Margin</span>=<span class="string">"50,10"</span> <span class="attr">Clicked</span>=<span class="string">"Button_RefreshSuKangMa_Clicked"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">StackLayout</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ContentPage</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ContentPage</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">StackLayout</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">WebView</span> <span class="attr">x:Name</span>=<span class="string">"WebView_XingChengMa"</span> <span class="attr">VerticalOptions</span>=<span class="string">"FillAndExpand"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">StackLayout</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ContentPage</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">CarouselPage</span>></span></span><br></pre></td></tr></table></figure><p>C# 逻辑如下:</p><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> Xamarin.Forms;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">ShuangShuangMa</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">MainPage</span> : <span class="title">CarouselPage</span></span><br><span class="line"> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MainPage</span>()</span></span><br><span class="line"> {</span><br><span class="line"> InitializeComponent();</span><br><span class="line"> WebView_SuKangMa.Source = <span class="string">"https://jsstm.jszwfw.gov.cn/jkmIndex.html?token=xxxxxx&uuid=xxxxxx"</span>;</span><br><span class="line"> WebView_XingChengMa.Source = <span class="string">"https://xc.caict.ac.cn/"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">Button_RefreshSuKangMa_Clicked</span>(<span class="params"><span class="built_in">object</span> sender, EventArgs e</span>)</span></span><br><span class="line"> {</span><br><span class="line"> WebView_SuKangMa.Reload();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>部分解释:</p><ol><li>添加刷新按钮是因为,WebView 在后台不会运行,而苏康码上的时间又是在前端运算的,所以防止再次打开应用后,苏康码的时间不对;</li><li>行程码网页在重新加载后就必须重新认证手机号,而 Android 应用在触发返回按钮后,会关闭所有的 WebView,所以需要在 <code>MainActivity.cs</code> 中添加以下代码以便把返回按钮当 Home 键用:</li></ol><figure class="highlight cs"><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="function"><span class="keyword">public</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">OnBackPressed</span>()</span></span><br><span class="line">{</span><br><span class="line"> MoveTaskToBack(<span class="literal">true</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当然,应用安装后得关闭电池优化、锁在后台,毕竟被清理掉之后又得重新认证行程码。</p><p>至此,APP 完成。</p><h2 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h2><p>效果见下面的视频:</p><video src='/static/blog_images/diy-to-combine-sukangma-and-xingchengma/4.mp4 ' type='video/mp4' controls='controls' width='320px'></video><p>话说,本来想着 Xamarin 是跨平台的,准备给玲玲的 iPhone 也整一个的,但无奈意识到自己没有 Mac,作罢。</p>]]></content>
<summary type="html">
<h2 id="背景介绍"><a href="#背景介绍" class="headerlink" title="背景介绍"></a>背景介绍</h2><p>众所周知的背景:</p>
<ol>
<li>苏康码打开很慢,在支付宝中如果没有快捷键,需要以下步骤:点击打开支付宝 -&gt
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Xamarin" scheme="https://blog.vvzero.com/tags/Xamarin/"/>
<category term="C#" scheme="https://blog.vvzero.com/tags/C/"/>
<category term="WEB" scheme="https://blog.vvzero.com/tags/WEB/"/>
</entry>
<entry>
<title>我两周就写了三行代码 - ARM Cortex A9 中断与浮点数运算、FPU 问题</title>
<link href="https://blog.vvzero.com/2022/03/08/arm-cortex-a9-interrupt-with-fpu/"/>
<id>https://blog.vvzero.com/2022/03/08/arm-cortex-a9-interrupt-with-fpu/</id>
<published>2022-03-08T13:09:38.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<h2 id="问题出现"><a href="#问题出现" class="headerlink" title="问题出现"></a>问题出现</h2><p>公司产品采用了 Xilinx Zynq 7z010 芯片,用于运动控制以及网络通讯。两周前,测试过程中发现网络通信会小概率出错,TCP 收到的数据 CRC 校验失败,无法稳定复现。</p><p>设备平台概述:</p><ol><li>CPU: Cortex-A9 双核</li><li>RAM: 1GB DDR3</li><li>操作系统: FreeRTOS</li><li>网络协议栈: lwip211</li></ol><h2 id="定位过程"><a href="#定位过程" class="headerlink" title="定位过程"></a>定位过程</h2><h3 id="怀疑应用层数据处理问题"><a href="#怀疑应用层数据处理问题" class="headerlink" title="怀疑应用层数据处理问题"></a>怀疑应用层数据处理问题</h3><p>TCP 是二进制数据流,每个包的长度不固定,应用层也许会写错。于是我修改了应用层的处理方案,手动构造了定长的数据包,虽然会导致 TCP 流量大幅上涨,但是逻辑看起来更清晰。</p><p>然而,修改后,似乎由于流量变大了,原来小概率出现的错误,现在大概率会出现!这也给 Debug 带来了有利的一面。</p><h3 id="怀疑网络通讯链路电磁干扰问题"><a href="#怀疑网络通讯链路电磁干扰问题" class="headerlink" title="怀疑网络通讯链路电磁干扰问题"></a>怀疑网络通讯链路电磁干扰问题</h3><p>但是这个怀疑方向很快就被否定了,因为我用了 TCP 协议,理论上只可能超时,不可能出错。</p><h3 id="怀疑-lwip-接口调用问题"><a href="#怀疑-lwip-接口调用问题" class="headerlink" title="怀疑 lwip 接口调用问题"></a>怀疑 lwip 接口调用问题</h3><p>lwip 有多个 TCP API,之前用的 Socket API,我尝试换成了 RAW API,但是问题依旧。</p><p>在调试的过程中,我尝试在网络链路的每一层数据打印出来,惊奇地发现,在数据链路层,数据是正确的!然而 lwip 的代码冗杂且数 MB 数据中才会出现几个错误位,于是我暂时没有考虑一层层分析代码。</p><h3 id="怀疑与其他线程之间存在干扰,或者存在数组越界访问"><a href="#怀疑与其他线程之间存在干扰,或者存在数组越界访问" class="headerlink" title="怀疑与其他线程之间存在干扰,或者存在数组越界访问"></a>怀疑与其他线程之间存在干扰,或者存在数组越界访问</h3><p>这样 Debug 就很简单了。我关闭了所有其他的线程,不出所料,Bug 消失了。</p><p>一点点放开线程,发现是一个<strong>运动控制的硬中断</strong>造成的 Bug。</p><p>然后再“二分法”排除代码,结果排除到最后,仅仅是一行代码:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// b, c 也为 long long</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> a = (<span class="type">long</span> <span class="type">long</span>)((<span class="type">double</span>)b * (<span class="type">double</span>)c);</span><br></pre></td></tr></table></figure><p>这让我大跌眼镜,因为实验证明,把这句话删了,TCP 通讯就正常了。</p><h3 id="怀疑是浮点运算的问题"><a href="#怀疑是浮点运算的问题" class="headerlink" title="怀疑是浮点运算的问题"></a>怀疑是浮点运算的问题</h3><p>更加让我迷惑的是,把上述语句改下,同样也没问题了:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">long</span> <span class="type">long</span> a = b * c;</span><br></pre></td></tr></table></figure><p>进一步定位:我在另一个线程中添加了浮点运算,并把这个有影响的中断关闭,TCP 通讯同样出问题了。</p><p>就此,几乎可以确定是浮点数运算造成的问题了。</p><h3 id="问题小结"><a href="#问题小结" class="headerlink" title="问题小结"></a>问题小结</h3><p>一句话描述问题:在中断或某个线程中进行浮点数操作,会导致另一个 TCP 通讯线程数据出错。</p><p>说实话,我当时也没法理解其中的联系。</p><p>只不过我们用的芯片自带双精度 FPU(浮点运算单元),也许是 FPU 的问题?</p><h2 id="解决过程"><a href="#解决过程" class="headerlink" title="解决过程"></a>解决过程</h2><h3 id="查找资料"><a href="#查找资料" class="headerlink" title="查找资料"></a>查找资料</h3><p>关键词 <code>lwip tcp receive wrong data</code>,<code>zynq float process corrupt memory</code> 等关键词,都没有找到有价值的解决方案。</p><h3 id="求助-Xilinx-技术支持"><a href="#求助-Xilinx-技术支持" class="headerlink" title="求助 Xilinx 技术支持"></a>求助 Xilinx 技术支持</h3><p>果然用微信联系的技术支持不靠谱,上午说帮忙复现,下午就没信了。</p><h3 id="求助朋友圈资深开发者"><a href="#求助朋友圈资深开发者" class="headerlink" title="求助朋友圈资深开发者"></a>求助朋友圈资深开发者</h3><p>只可惜他们都是互联网界的大佬,只有我在嵌入式开发领域摸爬滚打,他山之玉难以攻石。</p><h3 id="求助-V2EX-网友"><a href="#求助-V2EX-网友" class="headerlink" title="求助 V2EX 网友"></a>求助 V2EX 网友</h3><p>发了帖子 <a href="https://v2ex.com/t/838643">在这里</a>。</p><p>V 站网友给了非常有价值的线索:</p><ol><li>网友 A 称他们使用同样的平台出现过类似的问题。他们的解决方案是,进行浮点数操作之前,关闭所有的中断;</li><li>网友 B 分析可能 <code>正在计算浮点数的时候,刚好发生了 systick 线程切换,但是线程切换过程中,没有保存 /恢复浮点寄存器</code>;</li><li>网友 C 更是找到了相关文章:<blockquote><p>“Some GCC libraries optimise memory copy and memory set (and possibly other) functions by making use of the wide floating point registers. Therefore, by default, any task that uses functions such as memcpy(), memcmp() or memset(), or uses a FreeRTOS API function such as xQueueSend() which itself uses memcpy(), will inadvertently corrupt the floating point registers.”</p></blockquote></li></ol><p>真可谓一针见血,<strong>TCP 协议栈中大量使用了 memcpy,而 memcpy 又使用了 FPU 的寄存器,极有可能在 TCP 处理数据的过程中,另一个中断来了,进行了浮点运算并修改了 FPU 的寄存器,以致 TCP 数据出错。</strong></p><p>同样根据网友的指点,看了这篇文章 <a href="https://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html">Using FreeRTOS on ARM Cortex-A9 Embedded Processors</a>,原来 FreeRTOS 自身已经考虑了 FPU 与上下文切换相关的问题,只是要我们将 <code>configUSE_TASK_FPU_SUPPORT</code> 这个宏定义为 2 即可。</p><h2 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h2><p>花了些时间进行 FPU 寄存器相关的搜索,依照 <a href="https://stackoverflow.com/questions/38667425/how-to-push-and-pop-floating-point-registers-to-the-stack-on-armv7-32-bit">这篇文章</a> ,对 FPU 的寄存器做了相关处理,总结起来就三行代码:</p><h3 id="第一行代码"><a href="#第一行代码" class="headerlink" title="第一行代码"></a>第一行代码</h3><p>在中断响应函数开头添加以下代码:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__asm(<span class="string">"VPUSH {d0-d15}"</span>); <span class="comment">// FPU 寄存器入栈</span></span><br></pre></td></tr></table></figure><h3 id="第二行代码"><a href="#第二行代码" class="headerlink" title="第二行代码"></a>第二行代码</h3><p>在中断响应函数末尾添加以下代码:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">__asm(<span class="string">"VPOP {d0-d15}"</span>); <span class="comment">// FPU 寄存器出栈</span></span><br></pre></td></tr></table></figure><h3 id="第三行代码"><a href="#第三行代码" class="headerlink" title="第三行代码"></a>第三行代码</h3><p>FreeRTOS 启用 FPU 支持相关宏:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> configUSE_TASK_FPU_SUPPORT 2</span></span><br></pre></td></tr></table></figure><p>至此,问题解决。</p>]]></content>
<summary type="html">
<h2 id="问题出现"><a href="#问题出现" class="headerlink" title="问题出现"></a>问题出现</h2><p>公司产品采用了 Xilinx Zynq 7z010 芯片,用于运动控制以及网络通讯。两周前,测试过程中发现网络通信会小概率出
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="ARM" scheme="https://blog.vvzero.com/tags/ARM/"/>
<category term="FPU" scheme="https://blog.vvzero.com/tags/FPU/"/>
</entry>
<entry>
<title>随便聊聊最近在本站折腾的那些东西</title>
<link href="https://blog.vvzero.com/2022/01/24/Casual-talk-of-current-site-management/"/>
<id>https://blog.vvzero.com/2022/01/24/Casual-talk-of-current-site-management/</id>
<published>2022-01-24T02:23:44.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<blockquote><p>重新加上了 Disqus 评论系统,翻译了一个树莓派引脚定义网站,换了企业邮箱服务商,部署了一个站点监控系统,开始搞 nextcloud 中文论坛……随便聊聊最近在本站折腾的那些东西。</p></blockquote><h2 id="Disqus-评论系统"><a href="#Disqus-评论系统" class="headerlink" title="Disqus 评论系统"></a>Disqus 评论系统</h2><p>本站博客一直用的是 hexo,评论系统一开始也是用的 Disqus,后来不知道为啥就把评论系统给删了(可能是因为被墙了没意思?还是因为换了主题不支持啥的)。评论系统删了以后,又脑抽用 flarum 搭建了一个论坛,然后每篇博客前面都加一个对应的论坛讨论地址……这个方案被 PM 学叔给喷了,因为谁会为了评论,再去注册一个你的论坛账户呢?所以前几天把论坛给删了(话说也只有瑞辅一个人评论过一次,哈哈)。</p><p>Disqus 评论系统可能是在 hexo 上最好用的评论系统了,谁让 hexo 自己本身就是一个静态页面呢。但问题是 Disqus 被墙了,我得提醒读者,本站是有评论系统的。很久以前看到奶冰的博客,也是用的 Disqus,他是自己加了一个按钮,提醒用户点击加载 Disqus 评论组件。于是时隔几年我把这个想法抄过来了。</p><p>一开始感觉做这个按钮好复杂啊,因为自己几乎没有前端开发的经验。但是随便翻了翻我用的这个 aircloud 主题,发现源码改起来还是挺容易的,就强行加了一个按钮(话说改的方法我也上传到我 fork 的这个仓库里面了,<a href="https://github.com/Villivateur/hexo-theme-aircloud">https://github.com/Villivateur/hexo-theme-aircloud</a>。</p><h2 id="博客主题的魔改"><a href="#博客主题的魔改" class="headerlink" title="博客主题的魔改"></a>博客主题的魔改</h2><p>除了改了评论系统,我也魔改了其他小东西。我有点洁癖嘛,而且自从上次 jsdelivr 被拔网线的事情之后,我觉得静态资源用其他的站点托管都不太稳定,于是把 aircloud 里面用到的 js 和 CSS 都下载并重新托管在了自己的服务器上。</p><p>aircloud 还提供了浏览量计数功能,一看也是加载了第三方的 JS,算了不要了,有点洁癖。</p><h2 id="树莓派引脚定义网站-pinout-vvzero-com"><a href="#树莓派引脚定义网站-pinout-vvzero-com" class="headerlink" title="树莓派引脚定义网站 pinout.vvzero.com"></a>树莓派引脚定义网站 pinout.vvzero.com</h2><p>很早就了解到过一个做得很棒的树莓派引脚定义查询网站 pinout.xyz,而且已经有人翻译成了法语、德语、土耳其语啥的。我一直有很强烈的翻译欲望,现在终于找到了一个适合翻译的(之前没人翻译过、数据量不大),于是就开始了。</p><p>按照原作者给的翻译指引翻译了几个页面,但是感觉不过瘾,于是把本不该翻译的也翻译了(比如页面左下角的树莓派小图,还有生成脚本里面的一些细节),看来是没法全部提交 PR 了。故又 fork 了一个纯中文版的仓库,地址 <a href="https://git.vvzero.com/villivateur/pinout.vvzero.com">https://git.vvzero.com/villivateur/pinout.vvzero.com</a> ,用于部署 <a href="https://pinout.vvzero.com/">https://pinout.vvzero.com</a> 。按照翻译指引的仓库 <a href="https://github.com/Villivateur/Pinout.xyz">https://github.com/Villivateur/Pinout.xyz</a> 只用来提交 PR。</p><h2 id="zoho-企业邮箱"><a href="#zoho-企业邮箱" class="headerlink" title="zoho 企业邮箱"></a>zoho 企业邮箱</h2><p>腾讯企业邮箱是越来越恶心了,强行给你绑企业微信,还各种限制,不用微信登陆就强制改密码,算了,终于忍不了弃用了。V 站网友推荐了 zoho 邮箱,试了下感觉不错,于是给自己和玲玲买了两个账户,国区的,一年一共 100,完全可以接受。</p><p>目前发现 zoho 邮箱有两个问题:</p><ol><li>spam 有点严格,zoho 自家发的邮件都会进去,哈哈</li><li>我在 archive.org 的注册邮件,zoho 收不到</li></ol><h2 id="站点监控部署"><a href="#站点监控部署" class="headerlink" title="站点监控部署"></a>站点监控部署</h2><p>也是看到奶冰的网站,有个站点监控的玩意,用了 uptimerobot 的服务,试了下挺好玩,于是也部署在了 <a href="https://status.vvzero.com/">https://status.vvzero.com</a></p><h2 id="nextcloud-中文论坛"><a href="#nextcloud-中文论坛" class="headerlink" title="nextcloud 中文论坛"></a>nextcloud 中文论坛</h2><p>算是闲得蛋疼吧,感觉没人做 nextcloud 中文论坛,但我用 nextcloud 确实很多,就“抢先”搞了,虽然目前没什么内容。<a href="https://www.nextcloudcn.com/">https://www.nextcloudcn.com</a> 这个域名还是我抢注的,之前一个人刚好到期。没备案,所以只能托管在香港,套个 cloudflare。</p><p>至于运营就看情怀了,希望明年还想续费这个域名……</p>]]></content>
<summary type="html">
<blockquote>
<p>重新加上了 Disqus 评论系统,翻译了一个树莓派引脚定义网站,换了企业邮箱服务商,部署了一个站点监控系统,开始搞 nextcloud 中文论坛……随便聊聊最近在本站折腾的那些东西。</p>
</blockquote>
<h2 id="Disqu
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="树莓派" scheme="https://blog.vvzero.com/tags/%E6%A0%91%E8%8E%93%E6%B4%BE/"/>
<category term="email" scheme="https://blog.vvzero.com/tags/email/"/>
<category term="站点监控" scheme="https://blog.vvzero.com/tags/%E7%AB%99%E7%82%B9%E7%9B%91%E6%8E%A7/"/>
<category term="评论系统" scheme="https://blog.vvzero.com/tags/%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F/"/>
<category term="nextcloud" scheme="https://blog.vvzero.com/tags/nextcloud/"/>
</entry>
<entry>
<title>分享一下我的家庭网络布局</title>
<link href="https://blog.vvzero.com/2021/11/04/Network-devices-at-my-home/"/>
<id>https://blog.vvzero.com/2021/11/04/Network-devices-at-my-home/</id>
<published>2021-11-04T13:47:31.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<h2 id="我的家庭网络拓扑图"><a href="#我的家庭网络拓扑图" class="headerlink" title="我的家庭网络拓扑图"></a>我的家庭网络拓扑图</h2><p><img src="/static/blog_images/Network-devices-at-my-home/1.jpg"></p><h3 id="数据流部分"><a href="#数据流部分" class="headerlink" title="数据流部分"></a>数据流部分</h3><ol><li>网络核心部分是 Nano Pi R2S 这个软路由,经过一年调教,已经能适应我这里的一切网络需求了。主要运行了 Wireguard、流量监控、网路唤醒等服务。因为我们小区只有百兆网,所以性能暂时够用。</li><li>几乎所有网络设备都通过中间这个交换机与软路由通讯,虽然不算什么好的交换机,但是同理,够用。</li><li>数据中心是用 intel NUC8 搭建的家庭服务器,主要部署了 emby、Nextcloud、qbittorrent 等服务,满足了家庭观影、数据存储等需求。</li><li>存储采用多个硬盘,目前放弃了阵列模式,家庭用的话还是单块盘来用性价比最高,经常冷备、热备就好。</li><li>家庭服务器通过 FRP 服务把 Nextcloud 服务映射到公网,方便在外看家庭数据。</li><li>主力机是一台 E3-1231 V3 的机器,为什么还在用好几年前的平台?因为满足我的需求了。</li><li>我把一台小米路由器当 AP 用了,目前可以满足所有智能家居、所有手机、平板的稳定联网。</li><li>智能家居方面,完全采用了小米的方案,好看就是王道。</li></ol><h3 id="功率电部分"><a href="#功率电部分" class="headerlink" title="功率电部分"></a>功率电部分</h3><ol><li>图中除了主力 PC、移动设备和智能家居,其他均部署在一个机柜内,所以两个插排就搞定了。</li><li>UPS 还是很有必要的,帮我至少挡住了两次意外停电。</li></ol>]]></content>
<summary type="html">
<h2 id="我的家庭网络拓扑图"><a href="#我的家庭网络拓扑图" class="headerlink" title="我的家庭网络拓扑图"></a>我的家庭网络拓扑图</h2><p><img src="/static/blog_images/Network-devi
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="智能家居" scheme="https://blog.vvzero.com/tags/%E6%99%BA%E8%83%BD%E5%AE%B6%E5%B1%85/"/>
<category term="运维" scheme="https://blog.vvzero.com/tags/%E8%BF%90%E7%BB%B4/"/>
</entry>
<entry>
<title>Mapuino - 一个硬件极客风的 WEB 访客地图显示摆件</title>
<link href="https://blog.vvzero.com/2021/09/27/Mapuino-a-cute-knickknack-for-web-access-monitor/"/>
<id>https://blog.vvzero.com/2021/09/27/Mapuino-a-cute-knickknack-for-web-access-monitor/</id>
<published>2021-09-27T13:46:59.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<h2 id="Mapuino-是什么"><a href="#Mapuino-是什么" class="headerlink" title="Mapuino 是什么"></a>Mapuino 是什么</h2><p>Mapuino 是一个简单的摆件,或者叫“玩具”。你可以在自己的个人博客、主页或者任何可以插入个性代码的社交网站(如 V2EX)上添加一行 URL,然后就可以在 Mapuino 上观赏全国哪些地方的人正在访问你的网站。</p><p><img src="/static/blog_images/Mapuino-a-cute-knickknack-for-web-access-monitor/1.jpg"></p><h2 id="Mapuino-不是什么"><a href="#Mapuino-不是什么" class="headerlink" title="Mapuino 不是什么"></a>Mapuino 不是什么</h2><p>Mapuino 不是生产力工具,它功能单一,仅供娱乐。但它真的可以给你的生活带来一些小乐趣。</p><h2 id="Mapuino-的历史故事"><a href="#Mapuino-的历史故事" class="headerlink" title="Mapuino 的历史故事"></a>Mapuino 的历史故事</h2><p>2017 年秋学季,我上大二,有幸加入学校的学生 IT 创新创业区,并认识了 suruifu 同学,当时我所在的部门叫“物联网创新区”。圣诞前夜,suruifu 同学在创新区内给我分享了一个外国小哥的 youtube 视频。视频中,外国小哥做了一个圣诞树,神奇之处是,只要有人 ping 他的电脑 ip,圣诞树上就会随机亮起一个 LED。很多人一起 ping 的时候,圣诞树就会闪闪发光。</p><p>suruifu 同学感慨:“这才是物联网!”</p><p>而后,到了今年,一个月以前,我做了第一个小摆件 <a href="https://blog.vvzero.com/2021/08/31/Topuino-the-wonderful-Knickknack-for-server-monitoring/">Topuino</a>。</p><p>用同样的技术栈,我又做了 Mapuino。</p><h2 id="Mapuino-的工作模式"><a href="#Mapuino-的工作模式" class="headerlink" title="Mapuino 的工作模式"></a>Mapuino 的工作模式</h2><p>Mapuino 与 Topuino 类似,在配置之后,会连接 Wi-Fi 并从服务器获取数据,在每个周期内(比如 1 分钟),所有在上一个周期访问过你网站的用户,其所在省级行政区的 LED 将会亮起。</p><h2 id="Mapuino-的工作原理"><a href="#Mapuino-的工作原理" class="headerlink" title="Mapuino 的工作原理"></a>Mapuino 的工作原理</h2><p>硬件部分与 Topuino 非常类似,采用 ESP8266 作为 MCU,TM1638 作为 LED 驱动。</p><p>Mapuino 会以 1 分钟为周期向服务器发起请求,服务器返回上一个周期哪些地区有用户访问了指定 URL。此 URL 可以嵌入在任何网页中,比如通过 JS 发起请求,或者假装是一个 img 标签,或者也可以用各类站长测速工具直接 DDOS 这个 URL……</p><p>服务端直接解析访问此 URL 的 IP 所在地(目前使用了高德的 API),并临时存储。</p><p><img src="/static/blog_images/Mapuino-a-cute-knickknack-for-web-access-monitor/2.jpg"></p><h2 id="与-Topuino-相比的改进"><a href="#与-Topuino-相比的改进" class="headerlink" title="与 Topuino 相比的改进"></a>与 Topuino 相比的改进</h2><ol><li>体积更小,<del>可以白嫖部分 PCB 打样厂的免费额度</del>;</li><li>调整了下面两个固定孔的位置,可以直接拧上两个螺柱,方便放在桌上;</li><li>隐藏了 Wi-Fi 天线;</li><li>成本更低。</li></ol><h2 id="代码与开源"><a href="#代码与开源" class="headerlink" title="代码与开源"></a>代码与开源</h2><p>硬件端: <a href="https://github.com/Villivateur/Mapuino">https://github.com/Villivateur/Mapuino</a></p><p>服务端: <a href="https://github.com/Villivateur/MapuinoServer">https://github.com/Villivateur/MapuinoServer</a></p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>我又有其他点子啦,下一个做啥呢?</p>]]></content>
<summary type="html">
<h2 id="Mapuino-是什么"><a href="#Mapuino-是什么" class="headerlink" title="Mapuino 是什么"></a>Mapuino 是什么</h2><p>Mapuino 是一个简单的摆件,或者叫“玩具”。你可以在自己的个人
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Arduino" scheme="https://blog.vvzero.com/tags/Arduino/"/>
<category term="ESP8266" scheme="https://blog.vvzero.com/tags/ESP8266/"/>
<category term="IoT" scheme="https://blog.vvzero.com/tags/IoT/"/>
</entry>
<entry>
<title>Topuino - 你愿意在办公桌上放一个监控服务器的小摆件吗?</title>
<link href="https://blog.vvzero.com/2021/08/31/Topuino-the-wonderful-Knickknack-for-server-monitoring/"/>
<id>https://blog.vvzero.com/2021/08/31/Topuino-the-wonderful-Knickknack-for-server-monitoring/</id>
<published>2021-08-31T13:21:35.000Z</published>
<updated>2023-06-30T07:45:03.949Z</updated>
<content type="html"><![CDATA[<blockquote><p>我做了一个用来监控服务器的桌面小摆件</p></blockquote><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/1.png"></p><h2 id="什么是-Topuino"><a href="#什么是-Topuino" class="headerlink" title="什么是 Topuino"></a>什么是 Topuino</h2><p>Topuino 是我 DIY 的一个桌面小摆件,可以实现通用服务器或计算机的数据监控,包括 CPU 占用、RAM 占用、两个硬盘的可用空间、硬盘读写速度、网络 IO 速率。</p><h2 id="为什么叫-Topuino"><a href="#为什么叫-Topuino" class="headerlink" title="为什么叫 Topuino"></a>为什么叫 Topuino</h2><p>在 Linux 系列服务器上,我们通常使用 top 命令查看 CPU 内存占用,我最初的设想也是将 top 命令实物化,这就是 Topuino 中 Top 的由来。</p><p>在选型的时候,为了兼顾开发效率和成本,我选用了大名鼎鼎的 ESP8266 单片机,配合了 Arduino 开发框架,Arduino 则是 Topuino 中 uino 的由来。</p><h2 id="Topuino-有哪些亮点"><a href="#Topuino-有哪些亮点" class="headerlink" title="Topuino 有哪些亮点"></a>Topuino 有哪些亮点</h2><p>先看图解:</p><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/2.png"></p><ol><li>我觉得它挺好看,哑光黑的 PCB 底板富有科技感,红绿蓝三色 LED 层次分明,指示性强;</li><li>显示的参数满足大部分的需求,刷新率为 1 秒,CPU、内存、磁盘占用以百分比表示在柱状图上,磁盘、网络 IO 各以四位数码管显示,配合 KB、MB 单位显示,可以表示 0KB - 9999MB /s 的速率;</li><li>配置、操作方便。在需要监控的服务器上只需要跑一个 python 脚本即可;Topuino 首次上电后支持用手机或任何支持 Wi-Fi 的设备连接,并通过浏览器配置。若需要重新配置,通过按键即可恢复;</li><li>使用了通用的 USB-TypeC 接口(后期会做带电池版本);</li><li>成本不高,谁都可以承担。</li></ol><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/3.jpg"></p><h2 id="Topuino-的工作原理"><a href="#Topuino-的工作原理" class="headerlink" title="Topuino 的工作原理"></a>Topuino 的工作原理</h2><ul><li>服务器部分很简单,主站使用了 Flask,维护一个数据库,保存着从站(被监控服务器)UUID 与运行参数的映射关系(实际上现在是用 python 的字典简单实现的)。主站接收从站的运行数据,并向 Topuino 回传数据;</li><li>服务器从站采用 python 的 psutil 库,获取所有的运行数据;</li><li>Topuino 硬件部分使用了 ESP-12F 作为 MCU,显示采用 LED 整列和数码管,显示驱动是 TM1638 芯片。</li></ul><p>附上原理图:</p><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/4.png"></p><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/5.png"></p><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/6.png"></p><p>PCB 打样交给专门的厂家,回来自己焊。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>你愿意在办公桌上放一个监控服务器的小摆件吗?至少,我做出来之后,很喜欢,就像看着一只猫一样。</p><p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring/7.png"></p><p>另:ESP8266 的代码初步开源在 <a href="https://github.com/Villivateur/Topuino">https://github.com/Villivateur/Topuino</a> ,供大家参考。服务器端代码因为太简单且写得太丑,以后再说吧~~</p>]]></content>
<summary type="html">
<blockquote>
<p>我做了一个用来监控服务器的桌面小摆件</p>
</blockquote>
<p><img src="/static/blog_images/Topuino-the-wonderful-Knickknack-for-server-monitoring
</summary>
<category term="TECHNOLOGY" scheme="https://blog.vvzero.com/categories/TECHNOLOGY/"/>
<category term="Arduino" scheme="https://blog.vvzero.com/tags/Arduino/"/>
<category term="IoT" scheme="https://blog.vvzero.com/tags/IoT/"/>
<category term="Topuino" scheme="https://blog.vvzero.com/tags/Topuino/"/>
</entry>
</feed>