-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1701 lines (1292 loc) · 164 KB
/
index.html
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
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 7.2.0">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"example.com","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta name="description" content="笔记记录">
<meta property="og:type" content="website">
<meta property="og:title" content="wqllyx的网站">
<meta property="og:url" content="http://example.com/index.html">
<meta property="og:site_name" content="wqllyx的网站">
<meta property="og:description" content="笔记记录">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="风">
<meta property="article:tag" content="计算机">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="http://example.com/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'zh-CN'
};
</script>
<title>wqllyx的网站</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">wqllyx的网站</h1>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>
</li>
</ul>
</nav>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/13/json%E5%AD%A6%E4%B9%A0%E4%B9%8B%E7%90%86%E8%AE%BA/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/13/json%E5%AD%A6%E4%B9%A0%E4%B9%8B%E7%90%86%E8%AE%BA/" class="post-title-link" itemprop="url">json学习之理论</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-13 00:19:13" itemprop="dateCreated datePublished" datetime="2024-06-13T00:19:13+08:00">2024-06-13</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Json"><a href="#Json" class="headerlink" title="Json"></a>Json</h1><p>JSON(JavaScrip Object Notation)是一种轻量级的<strong>数据交换格式</strong>。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。</p>
<p>关于上面的描述可以精简为一句话:<strong>Json是一种数据格式,和语言无关,在什么语言中都可以使用Json。</strong>基于这种通用的数据格式,一般处理两方面的任务:</p>
<ul>
<li>组织数据(数据序列化),用于数据的网络传输</li>
<li>组织数据(数据序列化),写磁盘文件实现数据的持久化存储(一般以.json作为文件后缀)</li>
</ul>
<p>Json中主要有两种数据格式:Json数组和Json对象,并且这两种格式可以交叉嵌套使用,下面依次介绍下这两种数据格式:</p>
<h2 id="Json数组"><a href="#Json数组" class="headerlink" title="Json数组"></a>Json数组</h2><p>Json数组使用 [] 表示,[]里边是元素,元素和元素之间使用逗号间隔,最后一个元素后边没有逗号,一个Json数组中支持同时存在多种不同类型的成员,包括:整形、 浮点、 字符串、 布尔类型、 json数组、 json对象、 空值-null。由此可见Json数组比起C/C++数组要灵活很多。</p>
<ul>
<li>Json数组中的元素数据类型一致</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 整形</span></span><br><span class="line"><span class="punctuation">[</span><span class="number">1</span><span class="punctuation">,</span><span class="number">2</span><span class="punctuation">,</span><span class="number">3</span><span class="punctuation">,</span><span class="number">4</span><span class="punctuation">,</span><span class="number">5</span><span class="punctuation">]</span></span><br><span class="line"><span class="comment">// 字符串</span></span><br><span class="line"><span class="punctuation">[</span><span class="string">"luffy"</span><span class="punctuation">,</span> <span class="string">"sanji"</span><span class="punctuation">,</span> <span class="string">"zoro"</span><span class="punctuation">,</span> <span class="string">"nami"</span><span class="punctuation">,</span> <span class="string">"robin"</span><span class="punctuation">]</span></span><br></pre></td></tr></table></figure>
<ul>
<li>Json数组中的元素数据类型不一致</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">[</span><span class="number">12</span><span class="punctuation">,</span> <span class="number">13.34</span><span class="punctuation">,</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span> <span class="string">"hello,world"</span><span class="punctuation">,</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">]</span></span><br></pre></td></tr></table></figure>
<ul>
<li>Json数组中的数组嵌套使用</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">[</span><span class="string">"cat"</span><span class="punctuation">,</span> <span class="string">"dog"</span><span class="punctuation">,</span> <span class="string">"panda"</span><span class="punctuation">,</span> <span class="string">"beer"</span><span class="punctuation">,</span> <span class="string">"rabbit"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">[</span><span class="string">"北京"</span><span class="punctuation">,</span> <span class="string">"上海"</span><span class="punctuation">,</span> <span class="string">"天津"</span><span class="punctuation">,</span> <span class="string">"重庆"</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">[</span><span class="string">"luffy"</span><span class="punctuation">,</span> <span class="string">"boy"</span><span class="punctuation">,</span> <span class="number">19</span><span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure>
<ul>
<li>Json数组和对象嵌套使用</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"luffy"</span><span class="punctuation">:</span><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"age"</span><span class="punctuation">:</span><span class="number">19</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"father"</span><span class="punctuation">:</span><span class="string">"Monkey·D·Dragon"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"grandpa"</span><span class="punctuation">:</span><span class="string">"Monkey D Garp"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brother1"</span><span class="punctuation">:</span><span class="string">"Portgas D Ace"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"brother2"</span><span class="punctuation">:</span><span class="string">"Sabo"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure>
<h2 id="Json对象"><a href="#Json对象" class="headerlink" title="Json对象"></a>Json对象</h2><p>Json对象使用 {} 来描述,每个Json对象中可以存储若干个元素,每一个元素对应一个键值对(key:value 结构),元素和元素之间使用逗号间隔,最后一个元素后边没有逗号。对于每个元素中的键值对有以下细节需要注意:</p>
<ol>
<li>键值(key)必须是字符串,位于同一层级的键值不要重复(因为是通过键值取出对应的value值)</li>
<li>value值的类型是可选的,可根据实际需求指定,可用类型包括:整形、 浮点、 字符串、 布尔类型、 json数组、 json对象、 空值-null。</li>
</ol>
<p>使用Json对象描述一个人的信息:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Name"</span><span class="punctuation">:</span><span class="string">"Ace"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sex"</span><span class="punctuation">:</span><span class="string">"man"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Age"</span><span class="punctuation">:</span><span class="number">20</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Family"</span><span class="punctuation">:</span><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Father"</span><span class="punctuation">:</span><span class="string">"Gol·D·Roger"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Mother"</span><span class="punctuation">:</span><span class="string">"Portgas·D·Rouge"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Brother"</span><span class="punctuation">:</span><span class="punctuation">[</span><span class="string">"Sabo"</span><span class="punctuation">,</span> <span class="string">"Monkey D. Luffy"</span><span class="punctuation">]</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"IsAlive"</span><span class="punctuation">:</span><span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Comment"</span><span class="punctuation">:</span><span class="string">"yyds"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure>
<h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><p>通过上面的介绍可用看到,Json的结构虽然简单,但是进行嵌套之后就可以描述很复杂的事情,在项目开发过程中往往需要我们根据实际需求自己定义Json格式用来存储项目数据。</p>
<p>另外,如果需要将Json数据持久化到磁盘文件中,需要注意一个问题:<strong>在一个Json文件中只能有一个Json数组或者Json对象的根节点,不允许同时存储多个并列的根节点。</strong>下面举例说明:</p>
<ul>
<li>错误的写法</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// test.json</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span><span class="string">"luffy"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"age"</span><span class="punctuation">:</span><span class="number">19</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"user"</span><span class="punctuation">:</span><span class="string">"ace"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"passwd"</span><span class="punctuation">:</span><span class="string">"123456"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure>
<p>错误原因:在一个Json文件中有两个并列的Json根节点(并列包含Json对象和Json对象、Json对象和Json数组、Json数组和Json数组),根节点只能有一个。</p>
<ul>
<li>正确的写法</li>
</ul>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// test.json</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Name"</span><span class="punctuation">:</span><span class="string">"Ace"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Sex"</span><span class="punctuation">:</span><span class="string">"man"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Age"</span><span class="punctuation">:</span><span class="number">20</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Family"</span><span class="punctuation">:</span><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"Father"</span><span class="punctuation">:</span><span class="string">"Gol·D·Roger"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Mother"</span><span class="punctuation">:</span><span class="string">"Portgas·D·Rouge"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Brother"</span><span class="punctuation">:</span><span class="punctuation">[</span><span class="string">"Sabo"</span><span class="punctuation">,</span> <span class="string">"Monkey D. Luffy"</span><span class="punctuation">]</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"IsAlive"</span><span class="punctuation">:</span><span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"Comment"</span><span class="punctuation">:</span><span class="string">"yyds"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure>
<p>在上面的例子中通过Json对象以及Json数组的嵌套描述了一个人的身份信息,并且根节点只有一个就是Json对象,如果还需要使用Json数组或者Json对象描述其他信息,需要将这些信息写入到其他文件中,不要和这个Json对象并列写入到同一个文件里边,切记!!!</p>
<hr>
<p>作者: 苏丙榅<br>链接: <a target="_blank" rel="noopener" href="https://subingwen.cn/qt/json/">https://subingwen.cn/qt/json/</a><br>来源: 爱编程的大丙<br>著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/" class="post-title-link" itemprop="url">操作系统持久性--存储设备(0)--文件系统介绍和概述</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-11 15:20:59" itemprop="dateCreated datePublished" datetime="2024-06-11T15:20:59+08:00">2024-06-11</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>如果操作系统崩溃或电源故障,系统主内存的内容可能会丢失。相比之下,非易失性存储是持久的,并且在崩溃和断电时仍能保持其状态。非易失性存储还具有更高的容量和更低的成本。</p>
<p>然而,非易失性存储技术有其自身的缺点。</p>
<ul>
<li>例如,当前的非易失性存储技术(例如磁盘和高密度闪存存储)不允许随机访问单个存储字;相反,访问必须以更粗粒度的单位进行——一次 512、2048 或更多字节。</li>
<li>此外,这些访问可能比访问 DRAM 慢得多;</li>
</ul>
<p>这种巨大的差异(在旋转磁盘的情况下约为五个数量级)促使操作系统以不同于主内存的方式组织和使用持久存储设备。</p>
<p><strong>文件系统是</strong>一种常见的操作系统抽象,允许应用程序访问非易失性存储。文件系统使用多种技术来应对非易失性存储设备的物理限制并为用户提供更好的抽象。例如,图 11.1 总结了<code>物理特性</code>如何激发文件系统设计的几个关键方面:</p>
<table>
<thead>
<tr>
<th>目标</th>
<th>物理特点</th>
<th>设计影响</th>
</tr>
</thead>
<tbody><tr>
<td>高性能</td>
<td>发起IO访问成本大</td>
<td>使用文件、目录、可用空间位图和放置试探法组织数据放置,以便以大型连续单元访问存储 缓存以避免访问持久存储</td>
</tr>
<tr>
<td>命名的数据</td>
<td>存储容量大,不会崩溃,并且可以在程序之间共享</td>
<td>支持具有有意义名称的文件和目录</td>
</tr>
<tr>
<td>受控制的共享</td>
<td>设备存储大量用户数据</td>
<td>在文件中包含访问控制元数据</td>
</tr>
<tr>
<td>可靠存储</td>
<td>1.更新期间可能会发生崩溃<br />2. 存储设备可能会发生故障 <br /> 3.闪存单元可能会磨损</td>
<td>1.使用事务使一组更新成为原子的<br />2.使用冗余来检测和纠正故障<br />3.将数据移动到不同的存储位置以均匀磨损</td>
</tr>
</tbody></table>
<ul>
<li><strong>性能:</strong>文件系统通过对数据的放置位置进行分组来分摊启动昂贵操作的成本,例如移动磁盘臂或擦除固态内存块,以便此类操作访问大范围的连续存储</li>
<li><strong>名字</strong>:文件系统将相关数据分组到目录和文件中,并为它们提供人类可读的名称(例如,/home/alice/Pictures/summer-vacation/hiking.jpg。)即使在创建文件的程序退出之后,这些数据名称仍然有意义它们帮助用户组织大量存储,并且使用户可以轻松地使用不同的程序来创建、读取和编辑数据。</li>
<li><strong>受控共享。</strong>文件系统包括有关谁拥有哪些文件以及允许哪些其他用户读取、写入或执行数据和程序文件的元数据</li>
<li>**可靠性:**文件系统使用事务以原子方式更新多个持久存储块,类似于操作系统如何使用临界区以原子方式更新内存中的不同数据结构</li>
</ul>
<p>为了获得良好的性能和可接受的可靠性,应用程序编写者和操作系统设计者都必须了解存储设备和文件系统的工作原理。本章和接下来的三章讨论关键问题:</p>
<p>其余部分通过描述典型的 <strong>API 和抽象集</strong>来介绍文件系统,并概述提供这些抽象的<strong>软件层</strong></p>
<h1 id="文件系统抽象"><a href="#文件系统抽象" class="headerlink" title="文件系统抽象"></a>文件系统抽象</h1><p>文件系统抽象有两个关键部分:定义数据集的<strong>文件</strong>和定义文件名称的<strong>目录。</strong></p>
<ol>
<li><strong>文件</strong>。文件是文件系统中命名的<strong>数据集合</strong>。<ol>
<li>文件的信息有两部分:元数据和数据。文件的元数据是操作系统理解和管理的有关文件的信息。例如,文件的元数据通常包括文件的大小、修改时间、所有者及其安全信息,例如文件是否可以由所有者或其他用户读取、写入或执行</li>
<li>文件的数据可以是用户或应用程序放入其中的任何信息。从文件系统的角度来看,文件的数据只是一个无类型字节数组。</li>
</ol>
</li>
</ol>
<p><code>通常,操作系统将文件的数据视为无类型字节数组,将其留给应用程序来解释文件的内容。然而,有时操作系统需要能够解析文件的数据。例如,Linux 支持许多不同的可执行文件类型,例如 ELF 和 a.out 二进制文件以及 tcsh、csh 和 perl 脚本。Linux 通过让可执行文件以标识文件格式的幻数开头来实现此目的。例如,ELF 二进制可执行文件以四个字节 0x7f、0x45、0x4c 和 0x46(ASCII 字符 DEL、E、L 和 F)开头</code></p>
<ol start="2">
<li><strong>目录。</strong>文件包含系统定义的元数据和任意数据,而目录提供<strong>文件的名称</strong>。特别是,文件目录是人类可读名称的列表以及从每个名称到特定底层文件或目录的映射。</li>
</ol>
<p><strong>Volume卷(分区):</strong></p>
<p>文件系统是以分区为单位来进行管理的:</p>
<ul>
<li>不同的分区都有各自独立的文件系统。一台计算机可以通过在<code>单个逻辑层次结构(目录层次结构)</code>中<strong>挂载</strong>(安装)多个卷来利用多个卷上存储的文件系统。</li>
<li>分区是逻辑磁盘的抽象。<ul>
<li>一个磁盘可以对应一个分区</li>
<li>一个磁盘也可以对应多分区。每个分区可以使用各自的文件系统组织数据。</li>
<li>多个磁盘也可以对应一个分区。使得文件系统跨物理磁盘管理</li>
</ul>
</li>
</ul>
<p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/image-20240611144135477.png" alt="image-20240611144135477"></p>
<p>例如,假设 USB 驱动器包含一个带有目录 /Movies 和 /Backup 的文件系统,如图 11.4 所示。如果 Alice 将该驱动器插入她的笔记本电脑,笔记本电脑的操作系统可能会使用路径 /Volumes/usb1/ 挂载 USB 卷的文件系统,如图 11.5 所示。然后,如果 Alice 调用 open(“/Volumes/usb1/Movies/vacation.mov”),她将从 USB 驱动器卷上的文件系统中打开文件 /Movies/vacation.mov。相反,如果 Bob 将该驱动器插入他的笔记本电脑,则笔记本电脑的操作系统可能会使用路径 /media/disk-1 挂载卷的文件系统,并且 Bob 将使用路径 /media/disk-1/Movies 访问同一文件/vacation.mov。</p>
<h2 id="API"><a href="#API" class="headerlink" title="API"></a>API</h2><h1 id="文件系统的软件层次"><a href="#文件系统的软件层次" class="headerlink" title="文件系统的软件层次"></a>文件系统的软件层次</h1><p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/image-20240611144335671.png" alt="image-20240611144335671"></p>
<p>如图11.7所示,操作系统通过一系列软件层来实现文件系统抽象。从广义上讲,这些层有两组任务:</p>
<ul>
<li><strong>向上 提供API给用户</strong> <em>软件堆栈的顶层——库、内核级文件系统和内核的块缓存——提供了一个方便的API来访问命名文件</em>,并通过缓存、写缓冲(延迟写,回写)和预取来最大限度地减少缓慢的存储访问</li>
<li><strong>向下 提供设备访问。</strong> <em>软件堆栈的较低级别为操作系统提供了访问各种 I/O 设备的方法。</em> <strong>设备驱动程序</strong>通过为每个设备提供<strong>专用于</strong>此硬件的代码,并将该代码放置在操作系统其余部分可以使用的更简单、更通用的接口(例如块设备接口)后面,隐藏特定 I/O 硬件的详细信息。<strong>设备驱动程序</strong>使用系统的主处理器和内存作为正常的内核级代码执行,但它们(CPU和内存)必须与 I/O 设备交互。系统的处理器和内存使用内存映射 I/O、DMA 和中断与其 I/O 设备进行通信。</li>
</ul>
<p><strong>在其余部分中,我们首先讨论文件系统 API 和性能层。然后我们讨论设备访问。</strong></p>
<h2 id="API-1"><a href="#API-1" class="headerlink" title="API"></a>API</h2><p><strong>文件系统软件堆栈的顶层(分为应用程序库和操作系统内核代码)提供文件系统 API,并提供缓存和写入缓冲以提高性能。</strong></p>
<ul>
<li><strong>系统调用和库。</strong>文件系统抽象(如图 11.6 所示的 API)可以直接由系统调用提供。或者,应用程序库可以<strong>包装</strong>系统调用以添加附加功能,例如<strong>缓冲</strong>(系统调用IO和标准库IO就这样)<ul>
<li>后者(c库函数)的优点是该库包含缓冲区,可将程序的小型读取和写入聚合到访问较大块的系统调用中,从而减少开销。例如,如果程序使用库函数 fread() 读取 1 个字节的数据,则 fread() 实现可以使用 read() 系统调用将更大的数据块(例如 4 KB)读取到维护的缓冲区中通过应用程序地址空间中的库。然后,如果进程再次调用 fread() 来读取另一个字节,则库只需从缓冲区返回该字节,而无需执行系统调用。</li>
</ul>
</li>
<li><strong>块缓存 Block cache</strong>:典型的存储设备比计算机的主内存慢得多。因此,<strong>操作系统的块缓存</strong>会缓存最近读取的块,并缓冲最近写入的块,以便稍后将它们写回存储设备。<ul>
<li>除了通过缓存和写缓冲提高性能之外,块缓存还充当<strong>同步点</strong>:因为对给定块的所有请求都经过块缓存,所以操作系统在每个缓冲区缓存条目中包含信息,例如,阻止进程在另一个进程写入块时读取该块,或者确保给定的块仅从存储设备中获取一次,即使它被许多进程同时读取。</li>
</ul>
</li>
<li><strong>预取Prefetching</strong>。操作系统使用预取来提高 I/O 性能。例如,如果进程读取文件的前两个块,操作系统可能会预取接下来的十个块</li>
</ul>
<h2 id="设备驱动程序:通用抽象"><a href="#设备驱动程序:通用抽象" class="headerlink" title="设备驱动程序:通用抽象"></a>设备驱动程序:通用抽象</h2><p>设备驱动程序在操作系统实现的高级抽象与I/O设备的硬件特定细节之间转换。</p>
<p>操作系统可能必须处理<strong>许多不同</strong>的I/O设备。</p>
<p><code>例如,笔记本电脑可能连接到两个键盘(一个内部和一个外部),触控板,一只鼠标,一个有线以太网,无线802.11网络,一个无线蓝牙网络,两个磁盘驱动器(一个内部和一个磁盘驱动器(一个外部),麦克风,扬声器,相机,打印机,扫描仪和USB拇指驱动器。这只是当今可以连接到计算机的数千台设备中的少数。构建一个分别处理每种情况的操作系统将是不可能的。</code></p>
<p><code>分层提供了访问各类设备的通用方法,有助于简化操作系统。例如,对于任何给定的操作系统,存储设备驱动程序通常实现标准块设备接口,允许以固定大小的块(例如,512、2048 或 4096 字节)读取或写入数据。</code></p>
<p><code>在标准块设备接口上运行的文件系统可以将文件存储在任何存储设备上,只要该存储设备的驱动程序实现了该接口,无论是 Seagate 旋转磁盘驱动器、Intel 固态驱动器、Western Digital RAID 还是 Amazon Elastic Block Store 卷。这些设备都有不同的内部组织和控制寄存器,但如果每个制造商都提供导出标准接口的设备驱动程序,则操作系统的其余部分无需关心这些每个设备的详细信息</code></p>
<h2 id="设备访问"><a href="#设备访问" class="headerlink" title="设备访问"></a>设备访问</h2><p>操作系统的<strong>设备驱动程序</strong>应如何与<strong>存储设备</strong>通信并控制存储设备?乍一看,存储设备似乎与我们迄今为止讨论的内存和 CPU 资源有很大不同。例如,磁盘驱动器包括多个电机、用于读取数据的传感器和用于写入数据的电磁体。</p>
<h3 id="内存映射IO-(还有个端口映射IO)"><a href="#内存映射IO-(还有个端口映射IO)" class="headerlink" title="内存映射IO (还有个端口映射IO)"></a>内存映射IO (还有个端口映射IO)</h3><p>端口映射IO:提供额外的 I/O 指令</p>
<p>内存映射 I/O(memory- mapped I/O):硬件将设备寄存器作为内存地址提供。</p>
<p>端口映射 I/O 在物理内存地址受限的体系结构中非常有用,因为 I/O 设备不需要消耗物理内存地址范围。另一方面,对于具有足够大物理地址空间的系统,内存映射 I/O 可以更简单,因为不需要定义新指令或地址范围,并且设备驱动程序可以使用任何标准内存访问指令来访问设备。此外,内存映射 I/O 提供了更统一的模型来支持 <strong>DMA—-I/O 设备和主存之间的直接传输</strong></p>
<p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/image-20240611150436636.png" alt="image-20240611150436636"></p>
<p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/image-20240611150644649.png" alt="image-20240611150644649"></p>
<p>每个 I/O 设备都有一个带有一组寄存器的<strong>控制器</strong>,可以写入和读取这些寄存器,以将命令和数据传输到设备或从设备传输命令和数据。例如,一个简单的键盘控制器可能有一个寄存器,可以通过读取该寄存器来了解最近按下的按键,以及另一个可以写入的寄存器来打开或关闭大写锁定灯。为了允许读写 I/O 控制寄存器,系统实现了内存映射 I/O。内存映射 I/O 将每个设备的控制寄存器映射到内存总线上的一系列物理地址。 CPU 对此物理地址范围的读取和写入不会进入主内存。相反,它们会访问 I/O 设备控制器上的寄存器。</p>
<p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%880%EF%BC%89-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BB%8B%E7%BB%8D%E5%92%8C%E6%A6%82%E8%BF%B0/image-20240611150723698.png" alt="image-20240611150723698"></p>
<h3 id="DMA"><a href="#DMA" class="headerlink" title="DMA"></a>DMA</h3><p>许多 I/O 设备(包括大多数存储设备)都会批量传输数据。例如,操作系统不会从磁盘读取一两个字,它们通常一次至少传输几千字节。 I/O 设备可以使用直接内存访问,而不是要求 CPU 读取或写入大量传输的每个字。当使用直接内存访问 (DMA) 时,I/O 设备在其自己的内存和系统主内存之间复制数据块。为了设置 DMA 传输,简单的操作系统可以使用内存映射 I/O 来向设备提供目标物理地址、传输长度和操作代码。然后,设备将数据复制到目标地址或从目标地址复制数据,而无需额外的处理器参与。设置 DMA 传输后,在 DMA 传输完成之前,操作系统不得将目标物理页用于任何其他目的。因此,操作系统将目标页面“固定”在内存中,以便在取消固定之前无法重用它们。例如,固定的物理页无法交换到磁盘,然后重新映射到其他虚拟地址。</p>
<p>为了设置 DMA 传输,简单的操作系统可以使用内存映射 I/O 来向设备提供目标物理地址、传输长度和操作代码。然后,设备将数据复制到目标地址或从目标地址复制数据,而无需额外的处理器参与。设置 DMA 传输后,在 DMA 传输完成之前,操作系统不得将目标物理页用于任何其他目的。因此,操作系统将目标页面“固定”在内存中,以便在取消固定之前无法重用它们。例如,固定的物理页无法交换到磁盘,然后重新映射到其他虚拟地址</p>
<h3 id="中断"><a href="#中断" class="headerlink" title="中断"></a>中断</h3><p><strong>操作系统需要知道 I/O 设备何时完成对请求的处理或者新的外部输入何时到达</strong>。一种选择是轮询,即重复使用内存映射 I/O 来读取设备上的状态寄存器。由于 I/O 设备通常比 CPU 慢得多,并且 I/O 设备接收的输入可能以不规则的速率到达,因此 I/O 设备通常最好使用中断来通知操作系统重要事件</p>
<h1 id="将它们放在一起:简单的磁盘请求"><a href="#将它们放在一起:简单的磁盘请求" class="headerlink" title="将它们放在一起:简单的磁盘请求"></a>将它们放在一起:简单的磁盘请求</h1><p>当进程发出像 read() 这样的系统调用来将数据从磁盘读取到进程的内存中时,操作系统会将调用线程移至等待队列。然后,操作系统使用内存映射 I/O 来告诉磁盘读取请求的数据并设置 DMA,以便磁盘可以将该数据放入内核内存中。然后磁盘读取数据并将其 DMA 到主内存中;一旦完成,磁盘就会触发中断。然后,操作系统的中断处理程序将数据从内核缓冲区复制到进程的地址空间中。最后,操作系统将线程移动到就绪列表中。当线程下次运行时,它将从系统调用中返回,数据现在存在于指定的缓冲区中。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%881%EF%BC%89-%E7%A3%81%E7%9B%98%E6%9E%84%E9%80%A0/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%881%EF%BC%89-%E7%A3%81%E7%9B%98%E6%9E%84%E9%80%A0/" class="post-title-link" itemprop="url">操作系统持久性--存储设备(1)--磁盘构造</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-11 14:02:58" itemprop="dateCreated datePublished" datetime="2024-06-11T14:02:58+08:00">2024-06-11</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="磁盘"><a href="#磁盘" class="headerlink" title="磁盘"></a>磁盘</h1><p>根据存储器层次结构,磁盘容量大但访问速度极其慢。</p>
<p>并且磁盘设备不像内存设备是通用的(所有内存几乎具有一致的访问接口)</p>
<p>不同的磁盘特性各不相同(所以需要后面的文件系统软件统一这些接口,抽象思想)</p>
<ul>
<li>访问粒度不同:内存可以按字节粒度访问,磁盘的访问单位至少要几百字节。</li>
<li>访问速度(时间)不同:内存可以随机访问,磁盘能在顺序访问,如果要访问不连续的位置吗,需要机械运动(慢)</li>
</ul>
<h2 id="磁盘结构"><a href="#磁盘结构" class="headerlink" title="磁盘结构"></a>磁盘结构</h2><p>磁盘驱动器存储数据的原理是 通过磁性将写入到快速旋转的金属薄盘上。</p>
<p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%881%EF%BC%89-%E7%A3%81%E7%9B%98%E6%9E%84%E9%80%A0/image-20240611100839851.png" alt="image-20240611100839851"></p>
<p>一个完整的磁盘结构组成:</p>
<ul>
<li><p><strong>磁头</strong>:每个表面都有一个磁头,能够按同心圆读取数据。</p>
<ul>
<li>为了达到整个表面(任意一个<strong>磁道</strong>),每个磁头都连接到一个<strong>磁臂</strong>,所有磁臂连接到<strong>同一臂杆(可以由内向外反复移动)</strong>。所以<strong>所有磁头都是同时移动的。</strong></li>
</ul>
</li>
<li><p><strong>盘片</strong>(可能有多个):容纳磁性材料的薄圆板。通常两面(两面都可存储数据)。盘片不断旋转。</p>
</li>
<li><p><strong>磁道</strong>(track):每个盘片被划分为一圈一圈的有一定宽度的同心圆,这就是磁道。</p>
<ul>
<li>磁盘可以在无需移动<strong>磁臂</strong>的情况下,读取同一<strong>磁道</strong>的所有数据(因为<strong>盘片在</strong>转)。所以磁盘访问<em>同一磁道</em>上的数据,比<em>不同磁道</em>上的要快。</li>
</ul>
</li>
<li><p><strong>扇区</strong>(sector):扇区是磁盘的最小<strong>访问单元</strong>(一般为,512B),<em>磁盘不能读取单个字节。它必须至少读取或写入一个扇区。</em></p>
<ul>
<li>操作系统如果要更改磁盘中的一个字节,就<em>不得不</em>读取整个扇区,然后写入整个扇区。(这是个问题,解决办法:延迟磁盘的读写。)</li>
</ul>
</li>
<li><p>柱面:</p>
<ul>
<li><code>一些早期的文件系统将相关数据放在不同的表面但在同一个柱面中。这个想法是可以读取来自柱面中不同磁道的数据,而无需进行寻道。一旦柱面已满,文件系统就会开始将数据放置在相邻柱面之一中。随着磁盘密度的增加,柱面的重要性下降了。如今,访问同一柱面内的不同磁道的成本与访问同一盘片上相邻磁道的成本大致相同。</code></li>
</ul>
</li>
</ul>
<p><u>两点实现细节:</u></p>
<ol>
<li><code>轨道倾斜(track skewing):为了最大化顺序存取速度,每个磁道上的逻辑扇区零与前一磁道上的扇区零错开一定量,该量对应于磁盘将磁头 从一个磁道移动到另一磁道,或者 从一个表面上的磁头切换到另一个表面上的磁头扇区往往会偏斜,因为从一个磁道切换到另一个磁道时,磁盘需要时间来重新定位磁头(即便移到相邻磁道)。如果没有这种偏斜,磁头将移动到下一个磁道,但所需的下一个块已经旋转到磁头下,因此驱动器将不得不等待整个旋转延迟,才能访问下一个块。</code></li>
<li><code>另一个事实是,外圈磁道通常比内圈磁道具有更多扇区,这是几何结构的结果。那里空间更多。这些磁道通常被称为多区域(multi-zoned)磁盘驱动器,其中磁盘被组织成多个区域,区域是表面上连续的一组磁道。每个区域每个磁道具有相同的扇区数量,并且外圈区域具有比内圈区域更多的扇区。</code></li>
</ol>
<p><em>磁盘驱动器</em>通常包含几 MB 的<strong>缓冲内存(buffer memory)<strong>,<em>磁盘控制器</em>使用该内存来缓冲从磁盘读取或写入磁盘的数据、用于</strong>磁道缓冲</strong>和<strong>写入加速。</strong></p>
<ul>
<li><p><strong>磁道缓冲(track buffering):</strong> <u>预读</u></p>
<p> 磁道缓冲通过存储磁头已<strong>读取</strong>但操作系统尚未请求的扇区来提高性能。特别是,当磁头移动到磁道时,它可能必须等待其需要访问的扇区在磁头下方旋转。当磁盘等待时,它将未请求的扇区读取到其机架缓冲区,以便如果操作系统稍后请求这些扇区,它们可以立即返回。</p>
</li>
<li><p><strong>写入加速(write acceleration)</strong>:<u>延迟写</u>(后写,write back)</p>
<p> 将要写入的数据存到自己的内存中,就向操作系统报告(写入)。磁盘固件稍后将写入内容从内存刷新到盘片。后写缓存有时会使驱动器看起来“更快”,但可能有危险。如果文件系统或应用程序要求将数据按特定顺序写入磁盘以保证正确性,后写缓存可能会导致问题(请阅读文件系统日志的章节以了解详细息)。</p>
</li>
</ul>
<h2 id="磁盘访问过程和性能"><a href="#磁盘访问过程和性能" class="headerlink" title="磁盘访问过程和性能"></a>磁盘访问过程和性能</h2><p>通过一个场景来查看磁盘的访问过程:<strong>操作系统向磁盘发出读取一个或多个连续扇区的请求(命令)。</strong></p>
<p>磁盘扇区通过逻辑块地址(logical block address,LBA)来指定要访问的<strong>盘面</strong>,<strong>磁道</strong>和<strong>扇区</strong></p>
<p><code>LBA: 每个磁盘扇区或闪存块的唯一标识符,通常编号从 1 到磁盘/闪存设备的大小。磁盘接口将此标识符转换为扇区的物理位置。</code></p>
<p>为了响应操作系统的请求:</p>
<p>磁盘必须</p>
<ul>
<li>首先寻找(seek)正确的磁道,【寻道,Seek】</li>
<li>等待第一个所需扇区旋转到磁头,【旋转,Rotate】</li>
<li>然后传输块。【传输,Transfer】</li>
</ul>
<p>因此,<strong>一次磁盘访问的时间为</strong>:</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><br></pre></td></tr></table></figure>
<ul>
<li><strong>寻道</strong>:</li>
</ul>
<p><code>磁盘必须首先寻找——将其臂移动到所需的磁道上。为了进行寻道,磁盘首先启动电机,将臂组件移动到磁盘上大致正确的位置。然后,当磁臂停止因寻道运动而振动时,磁盘开始读取嵌入在扇区中的定位信息,以确定其准确位置,并进行细粒度的定位校正以固定在所需的磁道上。一旦磁头定位在正确的磁道上,磁盘就会使用信号强度和定位信息对磁臂位置进行微小修正,以将磁头保持在所需的磁道上</code></p>
<ul>
<li><strong>旋转</strong>:</li>
</ul>
<p><code>一旦磁盘头定位在正确的磁道上,它必须等待目标扇区在其下方旋转。该等待时间称为旋转延迟。如今,大多数磁盘的旋转速度为 4200 RPM 到 15,000 RPM(每次旋转 15 毫秒到 4 毫秒),对于许多工作负载,旋转延迟的合理估计是完整旋转时间的一半 — 7.5 毫秒到 2 毫秒</code></p>
<p><code>一旦磁盘头确定在新磁道上,大多数磁盘立即开始将扇区读入其缓冲存储器,无论请求了哪些扇区。这样,如果有对已经通过磁头下方的扇区之一的请求,则可以立即传输数据,而不必延迟近一个完整旋转的请求来重新读取数据。</code></p>
<ul>
<li><p><strong>传输</strong>:</p>
<ul>
<li><p><code>一旦磁盘头到达所需的扇区,当扇区在磁头下方旋转时,磁盘必须将数据从该扇区传输到其缓冲存储器(用于读取),反之亦然(用于写入)。然后,对于读取,它必须将数据从其缓冲存储器传输到主机的主存储器。对于写入,传输顺序相反</code></p>
</li>
<li><p><code>由于磁盘的外部磁道比其内部磁道具有容纳更多扇区的空间,并且由于给定磁盘以恒定速率旋转,因此外部磁道的表面传输带宽通常高于内部磁道。</code></p>
</li>
<li><p><code>对于磁盘读取,一旦扇区已传输到磁盘的缓冲存储器,它们必须通过某些连接传输到主机的内存,例如 SATA(串行 ATA)、SAS(串行连接 SCSI)、光纤通道或 USB(通用串行bus)。对于写入,传输朝另一个方向进行。主机内存和磁盘缓冲区之间传输数据的时间就是主机传输时间。典型带宽范围为USB 2.0的60 MB/s 到光纤通道的2500 MB/s。</code></p>
</li>
<li><p><code>对于多扇区读取,磁盘在表面和磁盘缓冲存储器之间以及缓冲存储器和主机内存之间进行管道(串行)传输;因此,对于大型传输,总传输时间将由其中的瓶颈决定。类似地,对于写入,磁盘将主机传输与寻道、旋转和表面传输重叠;同样,总传输时间将由瓶颈决定。</code></p>
</li>
</ul>
</li>
</ul>
<h2 id="案例研究-:-东芝-MK3254GSY"><a href="#案例研究-:-东芝-MK3254GSY" class="headerlink" title="案例研究 : 东芝 MK3254GSY"></a>案例研究 : 东芝 MK3254GSY</h2><p>图 12.3 显示了最新的笔记本电脑 2.5 英寸磁盘的一些关键参数。</p>
<p><strong>Platters/Heads 2/4:两个盘(四个盘面),四个磁头</strong></p>
<p><img src="/2024/06/11/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%8C%81%E4%B9%85%E6%80%A7-%E5%AD%98%E5%82%A8%E8%AE%BE%E5%A4%87%EF%BC%881%EF%BC%89-%E7%A3%81%E7%9B%98%E6%9E%84%E9%80%A0/image-20240611112833324.png" alt="image-20240611112833324"></p>
<ul>
<li><strong>Spindle speed 7200 RPM:<strong>该磁盘将320 GB的数据存储在两个</strong>盘片</strong>上,因此每<strong>盘面</strong>存储80 GB。盘片以每分钟7200RPM(每分钟转数)旋转,每转为8.3ms;由于每个盘片的直径约为6.3厘米,因此每个盘片的外边缘以85km/h的速度移动!</li>
<li>**Average seek time read/write 10.5 ms / 12.0 ms: **读取和写入的寻道时间不同,因为磁盘在磁盘臂完全稳定之前开始尝试读取数据,但必须等待一段时间才能安全写入</li>
<li><strong>Transfer rate (surface to buffer)</strong> :54–128 MB/s当传输大量连续扇区时,磁盘的带宽为 54-128 MB/s。带宽用一个范围来表示,因为磁盘的外磁道比内磁道有更多的扇区,因此当磁盘访问外磁道上的数据时,扇区以更高的速率扫过磁头</li>
<li><strong>Transfer rate (buffer to host) 375 MB/s</strong>:一旦数据从盘片上传输出来,磁盘就可以通过 SATA(串行 ATA)接口【现代SATA已经过时了。有更快的PCIE等】以高达 375 MB/s 的速度将其发送到计算机的主内存。</li>
</ul>
<h3 id="随机与顺序访问性能"><a href="#随机与顺序访问性能" class="headerlink" title="随机与顺序访问性能"></a>随机与顺序访问性能</h3><p>给定的寻求和旋转时间以毫秒为单位,<strong>对磁盘上随机扇区的小访问比大的顺序访问要慢得多</strong></p>
<h2 id="磁盘调度"><a href="#磁盘调度" class="headerlink" title="磁盘调度"></a>磁盘调度</h2><p>由于移动磁盘臂和等待盘片旋转的成本非常高,因此通过优化待处理请求的服务顺序可以显着提高性能。磁盘调度可以由操作系统、磁盘固件或两者共同完成。</p>
<p>先进先出。最简单的做法是按照先进先出 (FIFO) 的顺序处理请求。不幸的是,FIFO 调度程序的性能可能很差。</p>
<p> SPTF/SSTF。一个最初有吸引力的选择是使用贪婪调度程序,在给定磁盘头和盘片的当前位置的情况下,该调度程序始终为可以在最短时间内处理的待处理请求提供服务。这种方法称为最短定位时间优先 (SPTF)(如果不考虑旋转定位,则称为最短寻道时间优先 (SSTF))。</p>
<p> SPTF 和 SSTF 有两个明显的局限性。首先,由于移动磁盘臂并等待一些旋转时间会影响服务后续请求的成本,因此这些贪婪方法并不能保证优化磁盘性能。其次,这些贪婪的方法可能会导致饥饿,例如,当对内部轨道的连续请求流阻止对外部轨道的请求得到服务时。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/" class="post-title-link" itemprop="url">TCP-IP网络----网络层(数据平面3)网络结构</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-10 18:00:09 / 修改时间:18:04:28" itemprop="dateCreated datePublished" datetime="2024-06-10T18:00:09+08:00">2024-06-10</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="网络层及其协议IP"><a href="#网络层及其协议IP" class="headerlink" title="网络层及其协议IP"></a>网络层及其协议IP</h1><p>我们将主要讨论互联网寻址——对于理解互联网网络层的工作原理至关重要。</p>
<p>掌握IP寻址就是掌握互联网的网络层本身。</p>
<h2 id="IPV4-数据报格式"><a href="#IPV4-数据报格式" class="headerlink" title="IPV4 数据报格式"></a>IPV4 数据报格式</h2><p> IPv4数据报格式如图4.17所示。 IPv4数据报中的关键字段如下:</p>
<p><img src="/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/image-20240610154918890.png" alt="image-20240610154918890"></p>
<ul>
<li>版本号</li>
<li>头部长度</li>
<li>服务类型</li>
<li>数据报长度</li>
<li>标识符</li>
<li>生存时间TTL</li>
<li>协议</li>
<li>头部校验和</li>
<li>源和目的IP地址</li>
<li>选项</li>
<li>数据</li>
</ul>
<p>请注意,<code>IP 数据报</code>总共有 20 个字节的报头(假设没有选项)。如果数据报携带 <code>TCP 报文段</code>,则每个数据报携带总共 40 字节的报头(20 字节 IP 报头加 20 字节 TCP 报头)以及应用层消息。</p>
<h2 id="IPv4-寻址"><a href="#IPv4-寻址" class="headerlink" title="IPv4 寻址"></a>IPv4 寻址</h2><p>然而,在讨论 IP 寻址之前,我们需要先介绍一下<code>主机和路由器如何连接到互联网。</code></p>
<p><code>主机</code>通常只有一个连接到网络的链路;当主机中的 IP 想要发送数据报时,它会通过此链接进行发送。主机和物理链路之间的边界称为<em>接口interface</em>。</p>
<p>现在考虑<code>路由器</code>及其接口。由于路由器的工作是在一个链路上接收数据报并在其他链路上转发该数据报,因此路由器必须连接两个或多个链路。路由器与其任一链路之间的边界也称为接口。因此,路由器具有多个接口,每个接口对应其每条链路。由于每个主机和路由器都能够发送和接收 IP 数据报,因此 IP 要求每个主机和路由器接口都有自己的 IP 地址。</p>
<p><strong>因此,IP 地址与接口相关联,而不是与包含该接口的主机或路由器相关联</strong></p>
<p>全球互联网中每台主机和路由器上的每个接口都必须有一个全球<code>唯一</code>的 IP 地址(NAT 后面的接口除外,如第 4.3.3 节所述)。然而,这些地址<code>不能随意</code>选择。接口 IP 地址的一部分将由其连接的<code>子网</code>确定。</p>
<p><img src="/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/image-20240610160046788.png" alt="image-20240610160046788"></p>
<p>图 4.18 提供了 IP 寻址和接口的示例。在该图中,一台路由器(具有三个接口)用于互连七台主机。仔细查看分配给主机和路由器接口的 IP 地址,有几件事需要注意。</p>
<ul>
<li>图 4.18 左上部分的三台主机以及它们所连接的路由器接口都具有 223.1.1.xxx 形式的 IP 地址。也就是说,它们的 IP 地址都具有相同的最左边 24 位。</li>
<li>这四个接口也通过<code>不包含路由器的网络</code>相互互连。该网络可以通过<code>以太网 LAN</code> 互连,在这种情况下,接口将通过以太网交换机(我们将在第 6 章中讨论)或<code>无线接入点</code>(我们将在第 7 章中讨论)互连。我们暂时将连接这些主机的无路由器网络表示为云,并在第 6 章和第 7 章中深入研究此类网络的内部结构</li>
</ul>
<p>在 IP 术语中,该网络互连三个主机接口和一个路由器接口形成一个<code>子网</code>。 (子网也称为<code> IP 网络</code>或简称为<code>网络</code>)</p>
<p><code>223.1.1.0/24</code>:</p>
<ul>
<li><strong>/24</strong>: 子网掩码,表示32位数量的最左边24位定义<code>子网地址</code></li>
</ul>
<p>从上面的讨论可以清楚地看出,具有多个<code>以太网段</code>和<code>点对点链路</code>的组织将拥有多个子网,给定子网上的所有设备都具有相同的子网地址。</p>
<p>原则上,不同的子网可以具有完全不同的子网地址。但实际上,它们的子网地址通常有很多共同点。为了理解其中的原因,接下来让我们将注意力转向互联网中如何处理<strong>寻址</strong>。</p>
<p><strong>互联网的地址分配策略</strong>称为无类别域间路由CIDR 。</p>
<p> CIDR 泛化了子网寻址的概念。与子网寻址一样,32 位 IP 地址分为两部分,并再次采用点分十进制形式 a.b.c.d/x,其中 x 表示地址第一部分的位数。</p>
<p><code>a.b.c.d/x </code></p>
<ul>
<li>地址的 x 个最高有效位构成 IP 地址的<strong>网络部分</strong>,通常称为地址的前缀(或<code>网络前缀</code>)。通常会为组织分配一个连续地址块,即具有公共前缀的一系列地址。在这种情况下,组织内的设备的 IP 地址将共享公共前缀。当外部路由器转发此子网中的数据时,就只需考虑网络前缀就可以进入到此子网中,而不用考虑后面的位。</li>
<li>地址的其余 32-x 位识别组织中的设备(<strong>主机部分</strong>),所有设备都具有相同的网络前缀。这些是在组织内的路由器上转发数据包时要考虑的位。<strong>这些低阶位可以(或可以不)具有额外的子网划分结构</strong>。</li>
</ul>
<p>例如,假设地址 a.b.c.d/21 的前 21 位指定组织的网络前缀,其余 11 位则标识组织中的特定主机。</p>
<p>但可以继续用这11个主机位,继续划分子网:</p>
<ul>
<li>a.b.c.d/24:前24位都是属于子网,只有后8位标识主机。</li>
<li>a.b.c.d/30:前30位都是属于子网,只有后两位标识主机。</li>
</ul>
<h3 id="地址聚合(路由聚合、路由汇总)"><a href="#地址聚合(路由聚合、路由汇总)" class="headerlink" title="地址聚合(路由聚合、路由汇总)"></a>地址聚合(路由聚合、路由汇总)</h3><p>通过ISP的无分类编址,使得同一组织内的所有主机都通过最大的IP地址来寻址,内部细节不需要外界路由器关心。</p>
<p>这种使用<code>单个前缀(分配给组织的那个顶级子网)</code>通告<code>多个网络(组织内部细分的许多网络)</code>的能力通常称为地址聚合(也称为路由聚合或路由汇总)</p>
<h3 id="广播地址"><a href="#广播地址" class="headerlink" title="广播地址"></a>广播地址</h3><p>如果我们没有提到另一种类型的 IP 地址,即 IP 广播地址 255.255.255.255,那就是我们的失职。当主机发送目标地址为 255.255.255.255 的数据报时,该消息将传递到同一子网上的所有主机。路由器也可以选择将消息转发到相邻子网(尽管它们通常不这样做)</p>
<h3 id="组织如何获得一组地址"><a href="#组织如何获得一组地址" class="headerlink" title="组织如何获得一组地址"></a>组织如何获得一组地址</h3><p>一个组织或者企业如何获取一组用于本组织内部的IP地址呢?</p>
<ul>
<li><p>方法1:通过中介ISP(ISP本身已经<code>从互联网名称与数字地址分配机构 (ICANN))</code>获取一大块地址)</p>
</li>
<li><p>方法2:直接向ICANN申请IP地址。</p>
</li>
</ul>
<h3 id="获取主机地址:动态主机配置协议-DHCP"><a href="#获取主机地址:动态主机配置协议-DHCP" class="headerlink" title="获取主机地址:动态主机配置协议( DHCP)"></a>获取主机地址:动态主机配置协议( DHCP)</h3><p>一旦组织获得了地址块,它就可以将单独的 IP 地址分配给组织中的<code>主机</code>和<code>路由器接口</code>。</p>
<p><strong>路由器</strong>中的IP地址通常是手动配置的(通常使用网络管理工具进行远程配置)</p>
<p><strong>主机</strong>地址也可以手动配置,但这通常是使用动态主机配置协议(DHCP)[RFC 2131]完成的。 </p>
<p>DHCP 允许主机自动获取(分配)IP 地址。网络管理员可以配置 DHCP,以便给定主机每次连接到网络时都会收到相同的 IP 地址,或者可以为主机分配一个临时 IP 地址,该地址每次连接到网络时都会有所不同。</p>
<p>除了主机 IP 地址分配之外,DHCP 还允许主机了解其他信息</p>
<ul>
<li>子网掩码、</li>
<li>第一跳路由器的地址(通常称为默认网关)</li>
<li>本地 DNS 服务器的地址。</li>
</ul>
<p><strong>DHCP 是一种客户端-服务器协议。</strong>客户端通常是想要获取网络配置信息的新到达的主机。</p>
<p>在最简单的情况下,每个子网都有一个 DHCP 服务器。如果子网上没有服务器,则需要有DHCP服务器的中继代理(通常是路由器)。图 4.23 显示了连接到子网 223.1.2/24 的 DHCP 服务器,路由器充当连接到子网 223.1.1/24 和 223.1.3/24 的到达客户端的中继代理。在下面的讨论中,我们假设子网上有 DHCP 服务器可用。</p>
<p><img src="/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/image-20240610171047542.png" alt="image-20240610171047542"></p>
<p>对于新到达的主机,DHCP 协议是一个四步过程,如图 4.24 所示,网络设置如图 4.23 所示。在此图中,yiaddr(如“您的互联网地址”)表示分配给新到达的客户端的地址。这四个步骤是:</p>
<ul>
<li>DHCP 服务器发现:找到与客户端交互的DHCP服务器。通过发送<code>DHCP发现消息</code>完成的(UDP协议):<ul>
<li>由于此时不知道目的IP,自己也没有IP。</li>
<li>所以发送源IP为<code>0.0.0.0</code>目的IP为<code>255.255.255.255</code>的广播,发现DHCP服务器。</li>
</ul>
</li>
<li>DHCP 服务器提供者:<ul>
<li>收到发现消息的服务器也会发送一个<code>DHCP offer message</code>,这个消息也是一个广播消息。(因为新客户端没有被分配IP,所以只能广播)。</li>
<li>通常会有多个服务器响应客户端,从而各自发送这条消息。</li>
</ul>
</li>
<li>DHCP 请求:<ul>
<li>客户端在收到(数条)提供者的回应信息之后,选择其中一个,向他发送<code>DHCP request message</code>。</li>
</ul>
</li>
<li>DHCP ACK<ul>
<li> 服务器用 <code>DHCP ACK message</code>响应 DHCP 请求消息,确认请求的参数。</li>
</ul>
</li>
</ul>
<p><img src="/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/image-20240610172622526.png" alt="image-20240610172622526"></p>
<h3 id="网络地址转换"><a href="#网络地址转换" class="headerlink" title="网络地址转换"></a>网络地址转换</h3><p><img src="/2024/06/10/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A23%EF%BC%89%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84/image-20240610174549887.png" alt="image-20240610174549887"></p>
<p>支持 NAT 的路由器在外界看来并不像路由器。相反,NAT 路由器对于外界来说就像具有单个 IP 地址的单个设备。</p>
<p>本质上,启用 NAT 的路由器向外界隐藏了家庭网络的详细信息。</p>
<p>如果从 WAN 到达 NAT 路由器的所有数据报都具有相同的<code>目标 IP 地址</code>,那么路由器如何知道应将数据转发到哪个内部主机?技巧是在 NAT 路由器上使用<code> NAT 转换表</code>,并在表条目中包含端口号和 IP 地址。</p>
<p><code>考虑图 4.25 中的示例。假设位于主机 10.0.0.1 后面的家庭网络中的用户请求 IP 地址为 128.119.40.186 的某个 Web 服务器(端口 80)上的网页。主机 10.0.0.1 分配了源端口号 3345 并将数据报发送到 LAN。 NAT路由器收到数据报后,为该数据报生成新的源端口号5001,并用其WAN侧IP地址138.76.29.7替换源IP地址,并用新的源端口号5001替换原来的源端口号3345。当生成新的源端口号时,NAT 路由器可以选择当前不在 NAT 转换表中的任何源端口号。 (请注意,由于端口号字段为 16 位长,因此 NAT 协议可以通过路由器的单个 WAN 侧 IP 地址支持超过 60,000 个同时连接!)路由器中的 NAT 还会在其 NAT 转换表中添加一个条目。 Web 服务器完全没有意识到包含 HTTP 请求的到达数据报已被 NAT 路由器操纵,它以目标地址为 NAT 路由器的 IP 地址、目标端口号为 5001 的数据报进行响应。当该数据报到达时在 NAT 路由器处,路由器使用目标 IP 地址和目标端口号索引 NAT 转换表,以为家庭网络中的浏览器获取适当的 IP 地址(10.0.0.1)和目标端口号(3345)。然后路由器重写数据报的目标地址和目标端口号,并将数据报转发到家庭网络。</code></p>
<p><code>近年来,NAT 得到了广泛的部署。但 NAT 也并非没有批评者。首先,有人可能会争辩说,端口号旨在用于寻址进程,而不是用于寻址主机。这种违规确实会给在家庭网络上运行的服务器带来问题,因为正如我们在第 2 章中所看到的,服务器进程在众所周知的端口号处等待传入请求,P2P 协议中的对等点在充当服务器时需要接受传入连接。一个对等点如何连接到位于 NAT 服务器后面且具有 DHCP 提供的 NAT 地址的另一个对等点? </code></p>
<p><code>在P2P应用程序中,任何参与对等方A应当能够对任何其他参与的对等方B发起一条TCP连接。所以该问题实质在于,如果对等方B在一个NAT后面,那么它将不能充当服务器并接受TCP连接。(A单独无法得知B的地址)</code></p>
<p><code>现在假设对等方A不在NAT后面,对等方B在NAT后面,对等方C不在NAT后面。C与B已经创建了一条TCP连接,如果A想与B创建连接,则需要通过C请求对等方B,发起直接返回对等方A的一条TCP连接。一旦二者建立一条直接的P2P TCP连接,两个对等方就可以交换报文或文件了。</code></p>
<p><code>这种雇佣关系被称为连接反转(connection reversal),已被许多P2P应用程序用于NAT穿越(NAT traversal)。</code></p>
<p><code>若A,B二者都在NAT后面则可使用应用程序进行中继处理。</code></p>
<h2 id="IPV6"><a href="#IPV6" class="headerlink" title="IPV6"></a>IPV6</h2>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A22%EF%BC%89%E8%B7%AF%E7%94%B1%E5%99%A8%E7%BB%93%E6%9E%84/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A22%EF%BC%89%E8%B7%AF%E7%94%B1%E5%99%A8%E7%BB%93%E6%9E%84/" class="post-title-link" itemprop="url">TCP-IP网络----网络层(数据平面2)路由器结构</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-09 17:06:21" itemprop="dateCreated datePublished" datetime="2024-06-09T17:06:21+08:00">2024-06-09</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="路由器结构"><a href="#路由器结构" class="headerlink" title="路由器结构"></a>路由器结构</h1><p>已经概述了网络层内的数据和控制平面、转发和路由之间的重要区别以及网络层的服务和功能。</p>
<p>我们将注意力转向<strong>网络层的转发功能</strong>——数据包从路由器的传入链路到该路由器相应的传出链路的实际传输。</p>
<p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A22%EF%BC%89%E8%B7%AF%E7%94%B1%E5%99%A8%E7%BB%93%E6%9E%84/image-20240609154639090.png" alt="image-20240609154639090"></p>
<p>图 4.4 显示了通用路由器架构的高级视图。可以识别四个路由器组件:</p>
<ol>
<li><strong>输入端口</strong>:输入端口执行几个关键功能。<ol>
<li><code>perform the physical layer function of terminating an incoming physical link at a router</code>。这显示在图4.4中输入端口的最左侧框和输出端口的最右边框。</li>
<li><code>also perform link-layer functions needed to interoperate with the link layer at the other side of the incoming link</code>。这由输入和输出端口中的中间框表示。</li>
<li><code>a lookup function is also performed at the input port</code>这将发生在输入端口的最右边框中。在这里,<strong>查询转发表</strong>以确定到达的数据包将通过交换结构转发到路由器的哪个输出端口。<strong>控制数据包</strong>(例如,携带路由协议信息的数据包)<strong>从输入端口转发到路由处理器</strong></li>
<li>请注意,这里的“端口”一词 - 引入物理输入和输出路由器接口 - 与第2章和第3章中讨论的与网络应用程序和socket相关的软件端口明显不同。</li>
</ol>
</li>
<li><strong>交换结构</strong>:交换结构将路由器的输入端口连接到其输出端口。此交换结构完全包含在路由器内——网络路由器内的网络</li>
<li><strong>输出端口:</strong></li>
<li><strong>路由处理器:</strong></li>
</ol>
<p><strong>路由器的输入端口、输出端口和交换结构几乎总是以硬件实现,如图 4.4 所示。(在下层)</strong></p>
<p><strong>因为软件跟不上速度。</strong></p>
<p><code>要理解为什么需要硬件实现,请考虑使用 100 Gbps 输入链路和 64 字节 IP 数据报,输入端口只有 5.12 纳秒的时间来处理数据报,然后另一个数据报才会到达。如果 N 个端口组合在线卡上(实践中经常这样做),数据报处理管道必须以 N 倍的速度运行——对于软件实现来说太快了</code></p>
<p>当<code>数据平面以纳秒时间尺度运行时</code>,路由器的<code>控制功能(执行路由协议、响应向上或向下的附加链路、与远程控制器通信(在 SDN 情况下)以及执行管理功能)运行在毫秒或秒的时间尺度上</code>。</p>
<p>因此,这些<strong>控制平面功能通常以软件实现</strong>并在<code>路由处理器</code>(通常是传统 CPU)上执行。</p>
<p><code>在深入研究路由器内部细节之前,让我们回到本章开头的类比,其中将数据包转发与进入和离开立交桥的汽车进行比较。假设立交桥是一个环岛,当汽车进入环岛时,需要进行一些处理。让我们考虑一下此处理需要哪些信息:</code></p>
<p><code>基于目的地的转发。假设汽车停在入口站并指示其最终目的地(不是当地的环岛,而是其旅程的最终目的地)。入口站的工作人员会查找最终目的地,确定通往最终目的地的环岛出口,并告诉司机要走哪个环岛出口。</code></p>
<p><code>通用转发。除了目的地之外,乘务员还可以根据许多其他因素来确定汽车的出口坡道。例如,所选的出口坡道可能取决于汽车的来源,例如颁发汽车牌照的州。来自某一组州的汽车可能会被指示使用一个出口坡道(通过慢速道路通向目的地),而来自其他州的汽车可能会被指示使用不同的出口坡道(通过超级道路通向目的地)高速公路)。可能会根据汽车的型号、品牌和年份做出相同的决定。或者,一辆不适合上路的汽车可能会被封锁,不被允许通过环岛。在通用转发的情况下,许多因素都可能影响乘务员为给定汽车选择出口坡道</code></p>
<p><code>一旦车辆进入环岛(其中可能挤满了从其他输入道路进入并前往其他环岛出口的其他车辆),它最终会在规定的环岛出口坡道处离开,在那里它可能会遇到在该出口离开环岛的其他车辆。通过这个类比,我们可以很容易地识别出图 4.4 中的主要路由器组件——入口道路和入口站对应于输入端口(具有查找功能以确定本地输出端口);环岛对应于交换结构;环岛出口道路对应输出口。通过这个类比,考虑瓶颈可能出现在哪里是有启发性的。如果汽车到达速度极快(例如,环岛在德国或意大利!)但车站服务员却很慢,会发生什么?服务员必须以多快的速度工作才能确保入口道路上没有后援?即使有速度极快的服务员,如果汽车缓慢地穿过环岛——还能进行倒车吗?如果进入环岛所有入口坡道的大多数车辆都想在同一个出口坡道离开环岛,会发生什么情况——可以在出口坡道或其他地方进行备份吗?如果我们想为不同的车辆分配优先级,或者首先阻止某些车辆进入环岛,那么环岛应该如何运行?这些都类似于路由器和交换机设计人员面临的关键问题。</code></p>
<p>为了具体和简单起见,我们最初在本节中假设转发决策仅<strong>基于数据包的目标地址</strong>,而不是基于一组<strong>通用的数据包标头字段</strong>。</p>
<p>我们将在 4.4 节中介绍更<strong>广义的数据包转发情况</strong></p>
<h2 id="输入端口处理和基于目的地的转发"><a href="#输入端口处理和基于目的地的转发" class="headerlink" title="输入端口处理和基于目的地的转发"></a>输入端口处理和基于目的地的转发</h2><p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A22%EF%BC%89%E8%B7%AF%E7%94%B1%E5%99%A8%E7%BB%93%E6%9E%84/image-20240609160152331.png" alt="image-20240609160152331"></p>
<p>输入处理的更详细视图如图 4.5 所示。</p>
<p>正如刚才所讨论的,输入端口的线路终止功能和链路层处理实现了各个输入链路的物理层和链路层。</p>
<p>在输入端口中执行的<strong>查找是路由器操作的核心</strong>——路由器在这里使用转发表来查找输出端口,到达的数据包将通过<code>交换结构</code>转发到该输出端口。</p>
<p>转发表要么由路由处理器计算和更新(使用路由协议与其他网络路由器中的路由处理器交互),要么从远程 SDN 控制器接收。</p>
<p>转发表通过单独的总线(例如 PCI 总线)从<code>路由处理器</code>复制到<strong>线卡</strong>,如图 4.4 中从路由处理器到输入线卡的虚线所示。</p>
<p>通过每个线卡上的<strong>副本</strong>,可以在每个输入端口<strong>本地</strong>做出转发决策,而无需针对每个数据包调用<strong>集中式路由</strong>处理器,从而避免集中式处理瓶颈。</p>
<p>现在让我们考虑“最简单”的情况,即传入数据包要交换到的输出端口基于数据包的目标地址。</p>
<p>对于 32 位 IP 地址,转发表的暴力解法将为每个可能的目标地址都有一个条目。由于可能的地址超过 40 亿个,因此该选项完全不可能。</p>
<p><strong>实际做法是:</strong></p>
<p>将IP地址划分范围,每个范围指定一个转发端口。然后使用最长前缀匹配算法,匹配最合适的范围,如果全部都没匹配,则走默认端口。(有点像switch)</p>
<p>而不是每个Ip地址都对应一个端口号。</p>
<ul>
<li><p>有了转发表,查找在概念上很简单——硬件逻辑只是搜索转发表以查找最长的前缀匹配。但在千兆位传输速率下,此查找必须在纳秒内完成(所以查找的复杂度也不能太高))。</p>
</li>
<li><p>一旦通过<code>查找</code>确定了数据包的<code>输出端口</code>,就可以将数据包发送到<code>交换结构中</code>。在某些设计中,如果来自其他输入端口的数据包当前正在使用<code>交换结构</code>,则可能会暂时<code>阻止</code>数据包进入交换结构。被阻止的数据包将在<code>输入端口排队</code>,然后安排在稍后的时间点穿过<code>交换结构</code>。</p>
</li>
<li><p><code>输入端口除了查找,还要采取许多其他操作</code>:</p>
<ul>
<li>必须进行物理层和链路层处理,如前所述; </li>
<li>必须检查数据包的版本号、校验和和生存时间字段并重写后两个字段;</li>
<li>用于网络管理的计数器(例如接收到的IP数据报的数量)必须更新。</li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>查找目标 IP 地址(“匹配”)然后将数据包发送到交换结构再到指定输出端口(“操作”)的输入端口步骤是更一般的“匹配加操作”抽象的特定情况,即在许多联网设备中执行,而不仅仅是路由器。</strong></p>
</blockquote>
<p>在链路层交换机(第 6 章中介绍)中,查找链路层目标地址,并且除了将帧发送到交换结构并朝向输出端口之外,还可以采取多种操作。</p>
<p>在防火墙(第 8 章介绍)中(过滤选定的传入数据包的设备),如果传入数据包的报头符合给定的标准(例如,源/目标 IP 地址和传输层端口号的组合),则可能会被丢。</p>
<p>在网络地址转换器(NAT,第 4.3 节中介绍)中,传输层端口号与给定值匹配的传入数据包将在转发(操作)之前重写其端口号。</p>
<p>事实上, <strong>the “match plus action” abstraction</strong>在当今的<strong>网络设备</strong>中既强大又普遍,并且是<strong>广义转发概念的核心。</strong></p>
<h2 id="交换(结构)"><a href="#交换(结构)" class="headerlink" title="交换(结构)"></a>交换(结构)</h2><p><code>交换结构</code>是路由器的核心,因为数据包实际上是通过该结构<code>从输入端口交换(即转发)到输出端口的</code>。交换可以通过多种方式完成,如图4.6所示:</p>
<p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A22%EF%BC%89%E8%B7%AF%E7%94%B1%E5%99%A8%E7%BB%93%E6%9E%84/image-20240609163429174.png" alt="image-20240609163429174"></p>
<ul>
<li><strong>通过内存交换:</strong>最简单、最早的路由器是传统计算机,输入和输出端口之间的交换是在 CPU(<code>路由处理器</code>)的直接控制下完成的。输入和输出端口在传统操作系统中充当传统I/O设备。输入端口首先通过中断向路由处理器发出信号。然后,数据包从输入端口复制到处理器内存中。然后,路由处理器从报头中提取目标地址,在转发表中查找适当的输出端口,并将数据包复制到输出端口的缓冲区。在这种情况下,如果内存带宽使得每秒最多可以将 B 个数据包写入内存或从内存中读取,则总体转发吞吐量(数据包从输入端口传输到输出端口的总速率)端口)一定小于 B/2。<strong>两个数据包不能同时转发,即使他们有不同的目的端口。因为共享系统总线一次只能执行一次内存读/写操作</strong>。一些现代路由器通过内存进行切换。然而,与早期路由器的主要区别在于,<code>目标地址的查找以及将数据包存储到适当的内存位置是通过对输入线卡进行处理来执行的</code>。在某些方面,通过内存进行交换的路由器看起来非常像共享内存多处理器,通过线卡上的处理将数据包交换(写入)到适当输出端口的内存中。</li>
<li><strong>通过总线交换</strong></li>
<li><strong>通过互联网交换</strong></li>
</ul>
<h2 id="输出端口处理"><a href="#输出端口处理" class="headerlink" title="输出端口处理"></a>输出端口处理</h2><p>如图4.7所示的输出端口处理,采用已存储在输出端口内存中的数据包,并通过输出链接传输它们。这包括选择(即调度)和用于传输的出队数据包,并执行所需的链路层和物理层传输功能。</p>
<p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A22%EF%BC%89%E8%B7%AF%E7%94%B1%E5%99%A8%E7%BB%93%E6%9E%84/image-20240609165747703.png" alt="image-20240609165747703"></p>
<h2 id="排队在哪发生"><a href="#排队在哪发生" class="headerlink" title="排队在哪发生"></a>排队在哪发生</h2><p>如果我们考虑<code>输入和输出端口功能</code>以及图 4.6 中所示的配置,很明显,数据包队列可能在输入端口和输出端口处形成,就像我们识别出汽车可能在输入和输出处等待的情况一样。</p>
<p>现在让我们更详细地考虑这些队列,因为随着这些队列变大,路由器的内存最终可能会耗尽,并且当没有内存可用于存储到达的数据包时,就会发生数据包丢失。回想一下,在我们之前的讨论中,我们说过数据包“在网络内丢失”或“在路由器处丢失”。正是在这里,在路由器内的这些队列中,这些数据包实际上被扔掉和丢失。</p>
<h2 id="数据包调度"><a href="#数据包调度" class="headerlink" title="数据包调度"></a>数据包调度</h2><p>现在让我们回到确定排队数据包通过传出链路传输的顺序的问题。由于您自己无疑曾多次排长队等待并观察如何为等待的客户提供服务,因此您无疑熟悉路由器中常用的许多排队规则。先来先服务(FCFS,也称为先进先出,FIFO)。英国人以在公交车站和市场上耐心有序的 FCFS 排队而闻名(“哦,你在排队吗?”)。其他国家/地区以优先权为基础,一类等待的客户比其他等待的客户获得优先服务。还有循环排队,顾客再次被分为不同的类别(如优先级排队),但每个类别的顾客依次获得服务。</p>
<ul>
<li>先进先出</li>
<li>优先级</li>
<li>循环和赋予权重</li>
</ul>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>计算机网络-自顶向下方法 8th</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="http://example.com/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A21%EF%BC%89/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="风">
<meta itemprop="description" content="笔记记录">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="wqllyx的网站">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A21%EF%BC%89/" class="post-title-link" itemprop="url">TCP-IP网络----网络层(数据平面)</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2024-06-09 15:38:51 / 修改时间:15:39:56" itemprop="dateCreated datePublished" datetime="2024-06-09T15:38:51+08:00">2024-06-09</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="网络层(数据平面)"><a href="#网络层(数据平面)" class="headerlink" title="网络层(数据平面)"></a>网络层(数据平面)</h1><p><code>现在提供网络层的概述,我们将在本章的以下部分中介绍网络层的数据平面组件。</code></p>
<p><code>在4.2节中,我们将深入研究路由器的内部硬件操作,包括输入和输出数据包处理、路由器的内部交换机制以及数据包排队和调度。</code></p>
<p><code>在 4.3 节中,我们将了解传统的 IP 转发,其中数据包根据其目标 IP 地址转发到输出端口。我们将遇到 IP 寻址、著名的 IPv4 和 IPv6 协议等等。</code></p>
<p><code>在第 4.4 节中,我们将介绍更广义的转发,其中数据包可以基于大量标头值(即,不仅基于目标 IP 地址)转发到输出端口。数据包可能会在路由器处被阻止或重复,或者可能会重写某些标头字段值——所有这些都在软件控制下。这种更通用的数据包转发形式是现代网络数据平面的关键组成部分,包括软件定义网络 (SDN) 中的数据平面。</code></p>
<p><code>在 4.5 节中,我们将了解“中间盒(middleboxes)”,它除了转发之外还可以执行其他功能</code></p>
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>网络层分解为两个相互作用的部分:</p>
<ul>
<li>数据平面—-网络层中每个路由器的功能<ul>
<li>确定到达路由器输入链路之一的数据报【datagram】如何<strong>转发</strong>到该路由器的输出链路之一。</li>
</ul>
</li>
<li>控制平面—-<ul>
<li>控制 数据报如何在 源主机和目的主机之间的路由器之间<strong>路由</strong>。</li>
</ul>
</li>
</ul>
<p>以前,这些控制平面<strong>路由协议</strong>和数据平面<strong>转发功能</strong>是在路由器内一起<strong>整体</strong>实现的。</p>
<p>软件定义网络(SDN)通过将这些控制平面功能实现为<strong>单独</strong>的服务(通常在远程“控制器”中)来明确分离数据平面和控制平面。</p>
<p>每个路由器的主要数据平面作用是将数据报从其输入链路转发到其输出链路;网络控制平面的主要作用是<code>协调每个路由器的转发行为</code>,<code>以便</code>数据报最终沿着源主机和目标主机之间的路由器路径进行端到端传输。</p>
<p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A21%EF%BC%89/image-20240609145545610.png" alt="image-20240609145545610"></p>
<h3 id="转发和路由:数据平面和控制平面"><a href="#转发和路由:数据平面和控制平面" class="headerlink" title="转发和路由:数据平面和控制平面"></a>转发和路由:数据平面和控制平面</h3><p>网络层的主要作用——将数据包从发送主机移动到接收主机。</p>
<p>为此,可以确定两个重要的网络层功能:</p>
<ol>
<li><p><strong>转发</strong>:<em>当数据包到达路由器的输入链路时,路由器必须将数据包移动到适当的输出链路</em>。例如,上图 中从主机 H1 到达路由器 R1 的数据包必须转发到通往 H2 的路径上的下一个路由器。转发仅仅是数据平面中的一个函数。</p>
</li>
<li><p><strong>路由</strong>:<em>网络层必须确定数据包从发送方流向接收方时所采用的路由或路径。</em>计算这些路径的算法称为<strong>路由算法</strong>。例如,路由算法将确定数据包从图中的 H1 流向 H2 的路径。路由是在网络层的控制平面中实现的。</p>
</li>
</ol>
<p>我们将在本书中更准确地使用这些术语。</p>
<p><strong>转发</strong></p>
<ul>
<li>是指将数据包从输入链路接口传输到适当的输出链路接口的路由器本地操作。</li>
<li>转发发生在非常短的时间尺度(通常为几纳秒),因此通常在硬件中实现。</li>
</ul>
<p><strong>路由</strong></p>
<ul>
<li>是指确定数据包从源到目的地所采用的端到端路径的过程。</li>
<li>路由发生在更长的时间尺度(通常是几秒),通常是在软件中实现的。</li>
</ul>
<p><code>使用我们的驾驶类比,考虑我们的旅行者从宾夕法尼亚州到佛罗里达州的旅行。在这次旅行中,我们的司机在前往佛罗里达州的途中经过了许多交汇处。我们可以将转发视为通过单个立交桥的过程:一辆汽车从一条道路进入立交桥,并确定应该走哪条路离开立交桥。我们可以将路由视为规划从宾夕法尼亚州到佛罗里达州的行程的过程:在出发之前,驾驶员查阅了地图并选择了许多可能的路径之一,每条路径都由一系列在交汇处连接的路段组成。</code></p>
<p>每个路由器的一个关键元素是其<strong>转发表</strong>。路由器通过检查到达数据包头部(首部)中的一个或多个字段的值来转发数据包,然后使用这些头部值<strong>索引</strong>其转发表。存储在转发表条目中的值指示该数据包要转发到的该路由器的哪一个传链路接口。</p>
<p><code>例如,在图 4.2 中,头字段值为 0110 的数据包到达路由器。路由器索引其转发表并确定该数据包的输出链路接口是接口 2。然后路由器在内部将数据包转发到接口 2。</code></p>
<p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A21%EF%BC%89/image-20240609150753392.png" alt="image-20240609150753392"></p>
<p>转发是网络层<strong>数据平面</strong>功能执行的关键功能。</p>
<h3 id="控制平面:传统方法"><a href="#控制平面:传统方法" class="headerlink" title="控制平面:传统方法"></a>控制平面:传统方法</h3><p>路由器的<strong>转发表是如何配置的</strong>。这是一个至关重要的问题,它揭示了转发(在数据平面中)和路由(在控制平面中)之间的重要相互作用。</p>
<ul>
<li>如图4.2:<strong>路由算法决定路由器转发表的内容</strong></li>
</ul>
<p>在此示例中,路由算法在每个路由器中运行,并且转发和路由功能<strong>都包含在</strong>路由器内。</p>
<p>一<strong>台路由器中的路由算法函数与其他路由器中的路由算法函数进行通信,以计算其转发表的值。</strong></p>
<p>这种通信是如何进行的?</p>
<ul>
<li>通过根据路由协议交换包含路由信息的路由消息!<code>路由算法和协议。</code></li>
</ul>
<p><code>转发和路由的不同目的可以通过考虑假设的(不现实的,但技术上可行的)网络情景来进一步说明,其中所有转发表均由物理上存在于路由器处的人类网络操作员直接配置。在这种情景下,不需要路由协议!当然,操作员需要相互交互,以确保转发表的配置方式使数据包能够到达其预期目的地。</code></p>
<h3 id="控制平面:SDN(软件定义网络)-方法"><a href="#控制平面:SDN(软件定义网络)-方法" class="headerlink" title="控制平面:SDN(软件定义网络) 方法"></a>控制平面:SDN(软件定义网络) 方法</h3><p>实现<strong>路由功能</strong>的方法(每个路由器都有一个与其他路由器的路由组件通信的路由组件)一直是路由供应商采用的<strong>传统方法</strong>。</p>
<p><img src="/2024/06/09/TCP-IP%E7%BD%91%E7%BB%9C-%E7%BD%91%E7%BB%9C%E5%B1%82%EF%BC%88%E6%95%B0%E6%8D%AE%E5%B9%B3%E9%9D%A21%EF%BC%89/image-20240609152558588.png" alt="image-20240609152558588"></p>
<p>图 4.3 显示了另一种方法,其中物理上独立的远程控制器<code>计算并分发转发表</code>以供每个路由器使用。</p>
<p>图 4.2 和 4.3 的<code>数据平面</code>组件是相同的。但是,控制平面路由功能与物理路由器是分开的——<code>路由设备仅执行转发,而远程控制器计算和分发转发表</code></p>
<p><code>远程控制器</code>可能在具有高可靠性和冗余性的远程数据中心中实现,并且可能由ISP或某些第三方管理。</p>
<p><strong>路由器和遥控器如何通信?</strong></p>
<ul>
<li>通过交换包含转发表和其他路由信息的消息。</li>
</ul>
<p>图 4.3 所示的控制平面方法是软件定义网络 (SDN) 的核心,其中网络是“软件定义的”,因为计算转发表并与路由器交互的控制器是在软件中实现的。</p>
<h2 id="Internet(网络层)服务模型"><a href="#Internet(网络层)服务模型" class="headerlink" title="Internet(网络层)服务模型"></a>Internet(网络层)服务模型</h2><p>在深入研究网络层的数据平面之前,让我们以更广泛的视角来总结我们的介绍,并考虑网络层可能提供的不同类型的服务。</p>
<ul>
<li>当发送主机的传输层将数据包传输到网络中(即,将其向下传递到发送主机的网络层)时,传输层是否可以依赖网络层将数据包传送到目的地?(不可以,不可靠)</li>
<li>当发送多个数据包时,它们是否会按照发送的顺序传递到接收主机中的传输层?(不可以,不保证顺序)</li>
<li>发送两个连续数据包传输之间的时间量是否与其接收之间的时间量相同?(不,不提供时间保证)</li>
<li>网络会提供有关网络拥塞的任何反馈吗?</li>
</ul>
<p>这些问题和其他问题的答案取决于<strong>网络层提供的服务模型。</strong></p>
<p><strong>网络服务模型定义了发送主机和接收主机之间端到端数据包传送的特性</strong>:</p>
<p>互联网(Internet)的网络层提供单一服务,称为<strong>尽力服务</strong>。</p>
<p>对于尽力而为的服务,既不能保证数据包按照发送顺序接收,也不能保证它们的最终交付。无法保证端到端延迟,也没有最小带宽保证。尽力而为服务可能是根本不提供服务的委婉说法。</p>
<p><em>其他网络架构</em>已经定义并实现了超出互联网尽力服务范围的服务模型。</p>
<p>例如,ATM 网络体系结构 [Black 1995] 提供有保证的有序延迟、有界延迟和有保证的最小带宽。</p>
<p>例如,Intserv 架构 [RFC 1633] 旨在提供端到端延迟保证和无拥塞通信。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>