From 7992becd2133ce052910f219c8820d72c598b446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=81na=28Bass=20Ver=2E=29?= <1759138827@qq.com> Date: Sat, 2 Aug 2025 11:01:45 +0800 Subject: [PATCH] Delete src/site/notes/notes directory --- ...50\257\264\350\257\235\360\237\245\260.md" | 33 -- ...45\274\217\357\274\237\360\237\244\224.md" | 83 --- ...47\247\213\346\263\242\360\237\230\241.md" | 31 -- ...44\276\213\345\255\220\360\237\245\265.md" | 124 ----- ...46\264\227\346\276\241\360\237\245\265.md" | 113 ---- ...45\215\225\345\221\242\360\237\244\224.md" | 75 --- ...02\347\232\204\344\275\234\347\224\250.md" | 103 ---- ...44\270\200\346\231\232\360\237\245\265.md" | 73 --- ...66\345\257\204\345\255\230\345\231\250.md" | 485 ------------------ ...14\347\232\204\345\210\206\347\261\273.md" | 124 ----- ...32\344\275\231\345\234\260\345\235\200.md" | 93 ---- ...47\224\261\350\241\250&ARP\350\241\250.md" | 103 ---- ...46\255\273\345\220\227\360\237\230\255.md" | 94 ---- ...50\344\272\206\344\273\200\344\271\210.md" | 170 ------ ...47\232\204\360\237\233\241\357\270\217.md" | 106 ---- ...46\255\245\345\220\227\360\237\244\224.md" | 101 ---- ...46\200\235\350\200\203\360\237\244\224.md" | 78 --- src/site/notes/notes/408/demo1111.md | 5 - ...45\344\275\234\346\265\201\347\250\213.md" | 85 --- ...4\344\270\216V\346\223\215\344\275\234.md" | 76 --- ...46\217\255\347\247\230\360\237\230\262.md" | 407 --------------- ...50\247\243\347\240\201\360\237\244\224.md" | 76 --- ...55\347\232\204\345\244\204\347\220\206.md" | 90 ---- ...35\345\255\230\346\203\205\345\206\265.md" | 115 ----- ...04\347\220\206\347\250\213\345\272\217.md" | 152 ------ ...44\346\215\242\345\215\225\344\275\215.md" | 54 -- ...51\227\250\357\274\237\360\237\230\255.md" | 65 --- ...47\273\204\346\210\220\360\237\230\241.md" | 216 -------- ...47\273\223\346\236\204\360\237\230\241.md" | 216 -------- ...47\232\204\345\221\242\360\237\244\224.md" | 103 ---- ...46\265\201\347\250\213\360\237\244\224.md" | 101 ---- ...45\250\207\345\221\242\360\237\244\224.md" | 90 ---- ...46\236\204\345\221\242\360\237\244\224.md" | 143 ------ ...\210\342\200\224\342\200\224Load Store.md" | 52 -- ...42\345\274\225\346\226\207\344\273\266.md" | 97 ---- ...45\210\206\346\236\220\360\237\244\224.md" | 103 ---- ...05\350\246\201\346\235\241\344\273\266.md" | 80 --- ...00\346\265\213\347\256\227\346\263\225.md" | 125 ----- ...50\351\230\273\345\241\236\344\272\206.md" | 42 -- ...45\257\273\345\235\200\360\237\244\224.md" | 131 ----- ...52\350\256\272\350\277\233\347\250\213.md" | 4 - ...45\221\250\346\234\237\360\237\245\260.md" | 85 --- ...67\344\275\223\346\265\201\347\250\213.md" | 233 --------- ...37\230\256\342\200\215\360\237\222\250.md" | 106 ---- ...20\345\210\206\351\205\215\345\233\276.md" | 78 --- ...32\351\201\223\346\212\200\346\234\257.md" | 65 --- ...72\345\210\253\350\247\243\351\207\212.md" | 83 --- ...41\347\220\206\345\206\205\345\255\230.md" | 48 -- .../notes/notes/Welcome\360\237\216\211.md" | 108 ---- 49 files changed, 5423 deletions(-) delete mode 100644 "src/site/notes/notes/408/CDMA\357\274\232\345\234\250\345\230\210\346\235\202\347\232\204\347\216\257\345\242\203\344\270\255\346\210\221\345\217\252\346\203\263\345\256\211\351\235\231\345\220\254\344\275\240\350\257\264\350\257\235\360\237\245\260.md" delete mode 100644 "src/site/notes/notes/408/CSMACA\357\274\232\346\230\257\350\207\252\347\224\261\345\220\237\345\224\261\347\232\204\346\267\267\346\262\214\351\255\224\346\263\225\357\274\214\350\277\230\346\230\257\351\201\265\345\276\252\345\217\244\350\200\201\345\245\221\347\272\246\347\232\204\345\272\217\345\210\227\344\273\252\345\274\217\357\274\237\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/Cache\347\274\272\345\244\261\347\253\237\345\257\271CPU\345\257\204\345\255\230\345\231\250\346\232\227\351\200\201\347\247\213\346\263\242\360\237\230\241.md" delete mode 100644 "src/site/notes/notes/408/DRAM\347\273\223\346\236\204\347\273\204\346\210\220\344\273\245\345\217\212\344\270\200\344\270\252\346\225\260\346\215\256\346\265\201\347\273\217\345\245\271\345\205\250\350\272\253\347\232\204\344\276\213\345\255\220\360\237\245\265.md" delete mode 100644 "src/site/notes/notes/408/DRAM\351\203\275\344\274\232\346\200\216\344\271\210\346\264\227\346\276\241\360\237\245\265.md" delete mode 100644 "src/site/notes/notes/408/ICMP\344\272\244\350\255\246\344\275\225\346\227\266\345\257\271IP\346\225\260\346\215\256\346\212\245\345\217\221\345\207\272\347\275\232\345\215\225\345\221\242\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/IO\345\261\202\346\254\241\347\273\223\346\236\204\344\273\245\345\217\212\346\257\217\345\261\202\347\232\204\344\275\234\347\224\250.md" delete mode 100644 "src/site/notes/notes/408/IO\346\225\260\346\215\256\350\277\233\345\205\245\345\206\205\345\255\230\345\211\215\357\274\214\347\253\237\350\246\201\345\234\250CPU\347\232\204\345\257\204\345\255\230\345\231\250\351\207\214\345\205\210\344\275\217\344\270\200\346\231\232\360\237\245\265.md" delete mode 100644 "src/site/notes/notes/408/IO\347\232\204\347\213\254\347\253\213\347\274\226\345\235\200\344\270\216\347\273\237\344\270\200\347\274\226\345\235\200,\347\212\266\346\200\201&\346\216\247\345\210\266\345\257\204\345\255\230\345\231\250.md" delete mode 100644 "src/site/notes/notes/408/IO\350\256\276\345\244\207\346\240\271\346\215\256\346\225\260\346\215\256\347\232\204\345\255\230\345\217\226\345\222\214\344\274\240\350\276\223\350\277\233\350\241\214\347\232\204\345\210\206\347\261\273.md" delete mode 100644 "src/site/notes/notes/408/IP\345\234\260\345\235\200\345\235\227\345\210\206\351\205\215\347\232\204\345\234\260\345\235\200\344\270\215\351\207\215\345\217\240\345\222\214\350\267\257\347\224\261\350\201\232\345\220\210\347\232\204\344\270\215\345\274\225\345\205\245\345\244\232\344\275\231\345\234\260\345\235\200.md" delete mode 100644 "src/site/notes/notes/408/IP\346\225\260\346\215\256\346\212\245\347\232\204\344\270\244\345\274\240\350\241\250\357\274\232\350\267\257\347\224\261\350\241\250&ARP\350\241\250.md" delete mode 100644 "src/site/notes/notes/408/OSPF&BGP\357\274\232OpenFlow\346\210\221\344\273\254\344\274\232\346\255\273\345\220\227\360\237\230\255.md" delete mode 100644 "src/site/notes/notes/408/PCB\345\275\223\344\270\255\345\255\230\345\202\250\344\272\206\344\273\200\344\271\210.md" delete mode 100644 "src/site/notes/notes/408/ROM\351\205\261\347\232\204\350\264\236\346\223\215\351\224\201\357\274\232CPU\347\232\204\345\206\231\346\214\207\344\273\244\346\230\257\345\246\202\344\275\225\350\242\253\346\227\240\346\203\205\346\213\222\347\273\235\347\232\204\360\237\233\241\357\270\217.md" delete mode 100644 "src/site/notes/notes/408/STDM\347\232\204\345\274\202\346\255\245\344\275\240\347\234\237\347\232\204\347\237\245\351\201\223\346\230\257\344\273\200\344\271\210\347\232\204\345\274\202\346\255\245\345\220\227\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/close()\344\270\216write()\346\223\215\344\275\234\347\232\204\346\200\235\350\200\203\360\237\244\224.md" delete mode 100644 src/site/notes/notes/408/demo1111.md delete mode 100644 "src/site/notes/notes/408/read()\346\223\215\344\275\234\347\232\204\345\267\245\344\275\234\346\265\201\347\250\213.md" delete mode 100644 "src/site/notes/notes/408/signal\346\223\215\344\275\234\344\270\216V\346\223\215\344\275\234.md" delete mode 100644 "src/site/notes/notes/408/\344\270\200\344\270\252\346\225\264\346\225\260\347\232\204\345\245\207\345\271\273\346\274\202\346\265\201\357\274\232\351\200\224\347\273\217\345\217\202\346\225\260\344\270\223\350\275\246`$a0`\357\274\214\344\274\232\346\231\244\350\277\220\347\256\227\346\240\270\345\277\203ALU\357\274\214\346\234\200\345\220\216\346\220\255\344\270\212`$v0`\350\277\224\347\250\213\347\232\204\345\205\250\350\277\207\347\250\213\345\244\247\346\217\255\347\247\230\360\237\230\262.md" delete mode 100644 "src/site/notes/notes/408/\344\270\200\346\235\241\346\214\207\344\273\244\347\232\204\350\257\236\347\224\237\345\222\214\350\247\243\345\211\226\344\273\216\345\246\202\344\275\225\350\256\276\350\256\241\345\210\260CPU\350\247\243\347\240\201\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\344\270\255\346\226\255\344\277\241\345\217\267\347\232\204\344\274\230\345\205\210\347\272\247,\345\244\232\351\207\215\344\270\255\346\226\255\347\232\204\345\244\204\347\220\206.md" delete mode 100644 "src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\345\222\214\345\255\220\347\250\213\345\272\217\350\260\203\347\224\250\345\257\271\345\257\204\345\255\230\345\231\250\347\232\204\344\277\235\345\255\230\346\203\205\345\206\265.md" delete mode 100644 "src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\347\250\213\345\272\217.md" delete mode 100644 "src/site/notes/notes/408/\344\270\273\345\255\230\345\222\214\345\244\226\345\255\230\347\232\204\346\225\260\346\215\256\344\272\244\346\215\242\345\215\225\344\275\215.md" delete mode 100644 "src/site/notes/notes/408/\344\275\217\345\235\200\345\206\263\345\256\232\345\221\275\350\277\220\357\274\232\344\270\272\344\273\200\344\271\210\344\275\240\347\232\204\345\217\230\351\207\217\350\246\201\350\242\253\345\206\205\345\255\230\350\256\277\351\227\256\344\270\244\346\254\241\346\211\215\350\202\257\345\207\272\351\227\250\357\274\237\360\237\230\255.md" delete mode 100644 "src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\350\241\250\345\275\223\344\270\255\347\232\204\350\241\250\351\241\271\345\222\214\351\241\265\350\241\250\347\232\204\351\241\265\347\233\256\345\275\225\351\241\271\347\232\204\351\200\273\350\276\221\345\234\260\345\235\200\347\273\223\346\236\204\347\273\204\346\210\220\360\237\230\241.md" delete mode 100644 "src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\351\241\271\345\222\214\351\241\265\350\241\250\351\241\271\347\232\204\347\273\223\346\236\204\360\237\230\241.md" delete mode 100644 "src/site/notes/notes/408/\345\217\252\344\274\232\350\222\231\345\244\264\347\256\227\346\225\260\347\232\204\345\274\261\346\231\272CPU\346\230\257\346\200\216\344\271\210\350\257\206\345\210\253\345\207\272\345\217\230\351\207\217\347\261\273\345\236\213\347\232\204\345\221\242\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\345\234\250cache\345\260\217\345\247\220\345\215\217\345\212\251\344\270\213\347\232\204CPU\350\256\277\345\255\230\345\205\250\346\265\201\347\250\213\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\345\234\250\351\232\220\345\220\253\345\257\273\345\235\200\344\270\255\347\254\254\344\272\214\346\223\215\344\275\234\346\225\260\346\230\257\346\200\216\344\271\210\345\234\250ACC\345\275\223\344\270\255\351\207\221\345\261\213\350\227\217\345\250\207\345\221\242\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\346\200\216\344\271\210\345\214\272\345\210\206\345\217\214\350\203\236\350\203\216ISA\345\222\214\345\276\256\344\275\223\347\263\273\347\273\223\346\236\204\345\221\242\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\346\214\207\344\273\244\351\233\206\351\207\214\347\232\204\342\200\234\346\226\255\350\210\215\347\246\273\342\200\235\345\244\247\345\270\210\342\200\224\342\200\224Load Store.md" delete mode 100644 "src/site/notes/notes/408/\346\226\207\344\273\266\347\232\204\351\200\273\350\276\221\347\273\223\346\236\204\344\273\245\345\217\212\347\264\242\345\274\225\346\226\207\344\273\266.md" delete mode 100644 "src/site/notes/notes/408/\346\235\241\344\273\266\350\275\254\347\247\273\346\214\207\344\273\244\345\257\271\345\272\224\347\232\204\350\275\254\347\247\273\346\235\241\344\273\266\345\210\206\346\236\220\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\346\255\273\351\224\201\347\232\204\345\233\233\344\270\252\345\277\205\350\246\201\346\235\241\344\273\266.md" delete mode 100644 "src/site/notes/notes/408/\346\255\273\351\224\201\351\201\277\345\205\215,\351\242\204\351\230\262,\346\243\200\346\265\213\347\256\227\346\263\225.md" delete mode 100644 "src/site/notes/notes/408/\347\224\250\346\210\267\347\272\247\347\272\277\347\250\213\344\270\272\344\273\200\344\271\210\351\230\273\345\241\236\344\270\200\344\270\252\345\260\261\345\205\250\351\203\250\351\230\273\345\241\236\344\272\206.md" delete mode 100644 "src/site/notes/notes/408/\347\273\206\345\210\206\344\270\244\347\247\215\345\217\230\345\235\200\345\257\273\345\235\200\360\237\244\224.md" delete mode 100644 "src/site/notes/notes/408/\347\273\252\350\256\272\350\277\233\347\250\213.md" delete mode 100644 "src/site/notes/notes/408/\350\231\232\346\213\237\345\234\260\345\235\200\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\245\260.md" delete mode 100644 "src/site/notes/notes/408/\350\256\276\345\244\207\347\233\270\345\205\263\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204\344\273\245\345\217\212\346\240\271\346\215\256\351\200\273\350\276\221\350\256\276\345\244\207\345\220\215\345\210\206\351\205\215\350\256\276\345\244\207\347\232\204\345\205\267\344\275\223\346\265\201\347\250\213.md" delete mode 100644 "src/site/notes/notes/408/\350\256\276\345\244\207\351\251\261\345\212\250\347\250\213\345\272\217\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\230\256\342\200\215\360\237\222\250.md" delete mode 100644 "src/site/notes/notes/408/\350\265\204\346\272\220\345\210\206\351\205\215\345\233\276.md" delete mode 100644 "src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257.md" delete mode 100644 "src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257\345\222\214DMA\347\232\204\345\214\272\345\210\253\350\247\243\351\207\212.md" delete mode 100644 "src/site/notes/notes/408/\351\241\265\345\274\217\347\256\241\347\220\206\344\270\216\346\256\265\345\274\217\347\256\241\347\220\206\345\206\205\345\255\230.md" delete mode 100644 "src/site/notes/notes/Welcome\360\237\216\211.md" diff --git "a/src/site/notes/notes/408/CDMA\357\274\232\345\234\250\345\230\210\346\235\202\347\232\204\347\216\257\345\242\203\344\270\255\346\210\221\345\217\252\346\203\263\345\256\211\351\235\231\345\220\254\344\275\240\350\257\264\350\257\235\360\237\245\260.md" "b/src/site/notes/notes/408/CDMA\357\274\232\345\234\250\345\230\210\346\235\202\347\232\204\347\216\257\345\242\203\344\270\255\346\210\221\345\217\252\346\203\263\345\256\211\351\235\231\345\220\254\344\275\240\350\257\264\350\257\235\360\237\245\260.md" deleted file mode 100644 index b365087..0000000 --- "a/src/site/notes/notes/408/CDMA\357\274\232\345\234\250\345\230\210\346\235\202\347\232\204\347\216\257\345\242\203\344\270\255\346\210\221\345\217\252\346\203\263\345\256\211\351\235\231\345\220\254\344\275\240\350\257\264\350\257\235\360\237\245\260.md" +++ /dev/null @@ -1,33 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/CDMA:在嘈杂的环境中我只想安静听你说话🥰","permalink":"/408/CDMA:在嘈杂的环境中我只想安静听你说话🥰/"} ---- - - -**CDMA(码分多路复用)是一种允许所有用户在同一时间、使用相同频率进行通信的介质访问控制技术,它通过为每个用户分配一个唯一的、相互正交的“密码”(码片序列),来实现对不同用户信号的区分。** -### 一、核心原理 - -两个核心概念:**码片序列(Chip Sequence)** 和 **正交性(Orthogonality)**。 - -#### 1. 码片序列(The "Secret Language") - -系统会为每一个要通信的用户分配一个独一无二的码片序列。这个序列由 `m` 个码片(chip)组成,每个码片的值是 `+1` 或 `-1`。 - -例如,一个 `m=8` 的码片序列S可以表示为:`S = (-1, +1, -1, -1, +1, +1, -1, +1)` - -**规定**: - -- 发送比特 **1** 时,就发送自己的码片序列 **S**。 - -- 发送比特 **0** 时,就发送该码片序列的**反码 -S**(所有+1变-1,-1变+1)。 - - - `-S = (+1, -1, +1, +1, -1, -1, +1, -1)` - - -#### 2. 正交性(The "Magic Headset") - -分配给不同用户的码片序列(比如S和T)必须是**相互正交**的。在数学上,“正交”意味着两个向量的**规格化内积(Inner Product)**为0。 - -- 规格化内积计算:将两个序列对应位置的码片相乘,然后把所有乘积加起来,最后除以序列长度 m。 -- 自己和自己做内积:一个序列和它自己做内积,结果是1。 -- 自己和反码做内积:结果是-1。 -**这个性质是CDMA能够分离信号的根本!** 它保证了用一个用户的“钥匙”(码片序列)去解别的用户信号时,得到的结果是0,从而过滤掉干扰。 diff --git "a/src/site/notes/notes/408/CSMACA\357\274\232\346\230\257\350\207\252\347\224\261\345\220\237\345\224\261\347\232\204\346\267\267\346\262\214\351\255\224\346\263\225\357\274\214\350\277\230\346\230\257\351\201\265\345\276\252\345\217\244\350\200\201\345\245\221\347\272\246\347\232\204\345\272\217\345\210\227\344\273\252\345\274\217\357\274\237\360\237\244\224.md" "b/src/site/notes/notes/408/CSMACA\357\274\232\346\230\257\350\207\252\347\224\261\345\220\237\345\224\261\347\232\204\346\267\267\346\262\214\351\255\224\346\263\225\357\274\214\350\277\230\346\230\257\351\201\265\345\276\252\345\217\244\350\200\201\345\245\221\347\272\246\347\232\204\345\272\217\345\210\227\344\273\252\345\274\217\357\274\237\360\237\244\224.md" deleted file mode 100644 index 7081747..0000000 --- "a/src/site/notes/notes/408/CSMACA\357\274\232\346\230\257\350\207\252\347\224\261\345\220\237\345\224\261\347\232\204\346\267\267\346\262\214\351\255\224\346\263\225\357\274\214\350\277\230\346\230\257\351\201\265\345\276\252\345\217\244\350\200\201\345\245\221\347\272\246\347\232\204\345\272\217\345\210\227\344\273\252\345\274\217\357\274\237\360\237\244\224.md" +++ /dev/null @@ -1,83 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/CSMACA:是自由吟唱的混沌魔法,还是遵循古老契约的序列仪式?🤔","permalink":"/408/CSMACA:是自由吟唱的混沌魔法,还是遵循古老契约的序列仪式?🤔/"} ---- - - -### 一、两种模式的核心概念与对比 - -CSMA/CA 协议根据信道访问的控制方式,主要分为两种模式:基于竞争的普通模式和无竞争的预约模式。在 IEEE 802.11 标准中,这两种模式分别通过分布式协调功能(DCF)和点协调功能(PCF)等机制实现。 - - -|对比项|普通模式 (基于DCF)|预约模式 (基于PCF/HCF)| -|---|---|---| -|**控制方式**|**分布式控制**:所有站点地位平等,自行竞争信道访问权。|**集中式控制**:由中心节点(如AP)统一协调和分配访问权限。| -|**冲突可能性**|**存在冲突**:通过冲突避免(CA)和随机退避机制降低概率,但无法根除。|**无冲突**:由中心节点精确调度,从机制上避免了冲突的发生。| -|**服务质量(QoS)**|**尽力而为**:无法提供时延或带宽保障。|**可提供保障**:能够为特定业务提供可控的低时延和确定性服务。| -|**典型应用**|适用于非实时、突发性的数据业务,如网页浏览、文件下载。|适用于对时延和抖动敏感的实时业务,如VoIP语音、视频会议。| -|**实现与支持**|**基础必备**:所有802.11设备必须支持。|**可选高级功能**:并非所有设备或AP都支持,或完全实现。| - -### 二、普通模式(争用模式)详解 - -普通模式,即**分布式协调功能 (DCF)**,是 CSMA/CA 协议的基础和默认工作方式。 - -**1. 适用场景** - -- 网络负载较轻,站点数量有限,冲突概率较低的环境。 - -- 传输对时延不敏感的业务类型,如 Web 浏览、电子邮件和文件下载。 - -- 网络中各站点以平等机会访问信道,无特殊优先级区分。 - -- 在不包含接入点(AP)的自组织网络(Ad-hoc)中,或AP不支持高级调度功能时。 - - -**2. 工作机制** - -其工作严格遵循 CSMA/CA 的核心流程: - -1. **载波侦听**:节点在发送前持续侦听信道,以判断其是否空闲。 - -2. **帧间间隔与退避**:若信道从忙碌转为空闲,节点需等待一个分布式帧间间隔(DIFS)后,再启动一个随机退避计时器。 - -3. **发送与确认**:退避计时器最先归零的节点获得发送权,发送数据帧后,等待接收方返回ACK帧以确认成功。若未收到ACK,则认为发生冲突,将增大冲突窗口并重新进入退避流程。 - - -此模式实现相对简单,控制开销小,但在高密度、高负载网络下,冲突概率剧增,性能会显著下降。 - -### 三、预约模式(非争用模式)详解 - -预约模式通过引入一个中心协调者来取代完全的自由竞争,从而为特定业务提供服务质量保障。 - -**1. 适用场景** - -- 网络负载繁重,冲突现象频繁,导致整体性能下降的环境。 - -- 承载需要严格时延和抖动保障的实时应用,如IP语音(VoIP)、实时视频会议、工业无线控制等。 - -- 网络中的接入点(AP)支持并开启了点协调功能(PCF)或混合协调功能(HCF)等高级调度机制。 - - -**2. 典型机制:点协调功能 (PCF)** - -PCF 依赖于AP作为**点协调者(Point Coordinator, PC)**,将时间划分为**争用期(Contention Period, CP)**和**非争用期(Contention-Free Period, CFP)**。 - -- 在**争用期(CP)**,网络运行方式与普通模式的DCF完全相同。 - -- 在**非争用期(CFP)**,AP掌握信道的绝对控制权。它会通过**轮询(Polling)**的方式,依次向列表中的站点授予发送权限。被轮询到的站点无需竞争即可直接发送数据,从而实现了无冲突的确定性访问。 - -### 四、应用场景与选择策略 - -在实际应用中,两种模式的选择取决于网络环境和业务需求。 - -- **家庭日常上网**(网页浏览、社交媒体、观看在线视频):网络负载通常不高,且业务对偶发延迟不敏感,因此完全运行在**普通模式(DCF)**下即可满足需求。 - -- **企业视频会议**:一个会议室内,多人同时进行高清视频会议、共享桌面和文件传输。为保障视频和语音的流畅,支持QoS的AP会将这些实时流量划分到高优先级的队列,并可能通过**预约模式(如EDCA的机制)**为其提供优先或专有的传输机会(TXOP),而文件传输等非实时业务则继续在普通模式下竞争。 - -- **工业自动化控制**(如无线机器人控制):这类场景对通信的确定性和实时性要求极为苛刻。必须采用**预约模式**或更专业的工业无线协议(常基于TDMA),任何由竞争退避带来的不确定延迟都可能导致生产事故。 - - -**总结而言,选择哪种模式的决策逻辑如下:** - -- 若网络中无特殊实时性业务,或设备/AP不支持高级调度功能,则默认工作在**普通模式**。 - -- 若网络中承载有时延敏感型业务,且设备/AP支持相关QoS机制,则应配置并优先使用**预约模式**,以保障关键业务的服务质量。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/Cache\347\274\272\345\244\261\347\253\237\345\257\271CPU\345\257\204\345\255\230\345\231\250\346\232\227\351\200\201\347\247\213\346\263\242\360\237\230\241.md" "b/src/site/notes/notes/408/Cache\347\274\272\345\244\261\347\253\237\345\257\271CPU\345\257\204\345\255\230\345\231\250\346\232\227\351\200\201\347\247\213\346\263\242\360\237\230\241.md" deleted file mode 100644 index 2f72f6e..0000000 --- "a/src/site/notes/notes/408/Cache\347\274\272\345\244\261\347\253\237\345\257\271CPU\345\257\204\345\255\230\345\231\250\346\232\227\351\200\201\347\247\213\346\263\242\360\237\230\241.md" +++ /dev/null @@ -1,31 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/Cache缺失竟对CPU寄存器暗送秋波😡","permalink":"/408/Cache缺失竟对CPU寄存器暗送秋波😡/"} ---- - - -### 一、 流程图的直接证据:不存在“二次读取” - - -当**“从主存取出AD所在块”**这个耗时最长的操作完成之后,流程图的箭头**分叉了**! - -它分成了两条并行的路径: - -- **路径 A(通往CPU)**:**`将 AD 单元内容送 CPU`** - -- **路径 B(通往Cache)**:**`在 cache 中找到一个对应的空闲行`** → **`将主存块复制到 cache 空闲行中`** - - -这两条路径最终都指向**“结束”**。 - -**这张图用最直观的方式告诉我们:** - -> 在处理Cache缺失时,从主存取回的数据块被**同时**送往了两个目的地。其中,CPU当前急需的那个特定单元(或字)的内容,被直接“抄近道”送给了CPU;而整个数据块,则被写入到Cache中对应的空行里。 - -- “Cache处理时间”包括了什么? - - 它包括了虚线框内的所有操作。主要是“从主存取块”的延迟,以及后续并行处理的耗时。它是一个从“判断出未命中”到“CPU拿到数据且Cache完成或开始填充”的总时间。 - -- 需要再重新读一遍Cache吗? - - 绝对不需要。 流程图中没有任何一个箭头,是从“将主存块复制到cache”这一步再指回到“从cache中取信息送CPU”的。两条路径是并行的,CPU拿到数据后,整个访存操作就对CPU而言结束了,它可以继续执行下一条指令了。让CPU在Cache填充完毕后再来读一次,是一种极其低效的设计,现代处理器不会这么做。 - diff --git "a/src/site/notes/notes/408/DRAM\347\273\223\346\236\204\347\273\204\346\210\220\344\273\245\345\217\212\344\270\200\344\270\252\346\225\260\346\215\256\346\265\201\347\273\217\345\245\271\345\205\250\350\272\253\347\232\204\344\276\213\345\255\220\360\237\245\265.md" "b/src/site/notes/notes/408/DRAM\347\273\223\346\236\204\347\273\204\346\210\220\344\273\245\345\217\212\344\270\200\344\270\252\346\225\260\346\215\256\346\265\201\347\273\217\345\245\271\345\205\250\350\272\253\347\232\204\344\276\213\345\255\220\360\237\245\265.md" deleted file mode 100644 index 15760fd..0000000 --- "a/src/site/notes/notes/408/DRAM\347\273\223\346\236\204\347\273\204\346\210\220\344\273\245\345\217\212\344\270\200\344\270\252\346\225\260\346\215\256\346\265\201\347\273\217\345\245\271\345\205\250\350\272\253\347\232\204\344\276\213\345\255\220\360\237\245\265.md" +++ /dev/null @@ -1,124 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/DRAM结构组成以及一个数据流经她全身的例子🥵","permalink":"/408/DRAM结构组成以及一个数据流经她全身的例子🥵/"} ---- - - - -### 一、 DRAM 内部结构图解读 -#### 1. 核心部件分析 - -- **定时和控制 (Timing and Control):** 这是DRAM的大脑。它接收外部控制信号,并生成所有内部操作所需的时间序列和控制逻辑。 - - - **RAS (Row Address Strobe, 行地址选通)**: 低电平有效。当RAS信号下降时,DRAM会锁存(Latch)地址线上的信号作为**行地址**。 - - - **CAS (Column Address Strobe, 列地址选通)**: 低电平有效。当CAS信号下降时,DRAM会锁存地址线上的信号作为**列地址**。 - - - **WE (Write Enable, 写使能)**: 控制读写模式。WE为低电平时为**写操作**,高电平时为**读操作**。 - - - **OE (Output Enable, 输出使能)**: 控制数据输出缓冲器。只有在读操作且OE有效时,数据才会被驱动到数据引脚上。 - -- **地址输入 (A₀ ~ A₁₀):** 如图所示,该芯片有11条地址线。通过地址复用技术,这11条线既可以传输行地址,也可以传输列地址。 - -- **地址缓冲器与MUX:** - - - **行/列地址缓冲器**: 临时存储从地址引脚上接收到的行地址和列地址。 - - - **MUX (Multiplexer, 多路选择器)**: 这是一个关键部件。它负责选择将哪个地址送入行译码器:是来自**地址缓冲器**的地址(用于正常读写),还是来自**刷新计数器**的地址(用于刷新)。 - -- **刷新计数器 (Refresh Counter):** - - - 这是一个内部计数器,它会自动、依次地生成所有行的地址(0, 1, 2, ..., 2047)。其唯一的目的就是为**刷新操作**提供行地址。 - -- **存储阵列 (Storage Array): `2048 × 2048 × 4`** - - - 这是DRAM的核心,由我们上一问中讲的1T1C存储元组成。 - - - `2048 × 2048`:表示这个阵列有 **2048 行** 和 **2048 列**。这与地址线数量完美对应:211=2048,因此需要11位行地址和11位列地址。 - - - `× 4`:表示这个芯片有**4个这样的存储阵列**(也称位平面)。当给出一个行、列地址时,会同时在4个阵列的对应位置进行操作,一次可以读/写**4位数据**。这决定了芯片的数据位宽为4。 - -- **译码器与读出放大器:** - - - **行译码器 (Row Decoder)**: 接收11位行地址,从2048条**字选择线 (Word Line)** 中唯一选中一条,激活一整行的存储元。 - - - **读出放大器 (Sense Amplifier) / I/O门**: 当一行被激活时,这一整行(2048×4个)存储元的数据都会被读入到读出放大器中。读出放大器负责检测微弱的电压信号、将其放大并作为临时缓存。 - - - **列译码器 (Column Decoder)**: 接收11位列地址,从读出放大器缓存的一整行数据中,选择出目标列对应的4位数据,并将其连接到数据输入/输出缓冲器。 - -- **数据缓冲器与数据线 (D₁ ~ D₄):** - - - **数据输入/输出缓冲器**: 负责管理数据进出芯片。 - - - **D₁ ~ D₄**: 芯片的4位数据引脚,用于与外部(如内存控制器)交换数据。 - - ---- - -### 二、 实例:数据的流动与生命周期 - -让我们以一个具体的例子,追踪4位数据 `1011` 在这个DRAM芯片中的完整生命旅程。 - -假设我们要将数据 `1011` **写入**到由 **行地址 5 (二进制 `0...00101`)** 和 **列地址 88 (二进制 `0...1011000`)** 指定的单元中,然后再将其**读出**。 - -#### 阶段一:写入操作 (数据的诞生) - -1. **准备阶段**: 内存控制器准备好了地址(行5,列88)和数据(`1011`)。 - -2. **发送行地址**: 控制器将**行地址 5** 放到地址线 `A₀~A₁₀` 上。 - -3. **RAS 选通**: 控制器拉低 `RAS` 信号。DRAM芯片内部的**行地址缓冲器**立即锁存地址 `5`。 - -4. **行激活**: **行译码器**解码地址 `5`,激活存储阵列中的第5条**字选择线(Word Line)**。此时,第5行所有的 `2048 × 4` 个存储元都被连接到各自的**读出放大器**上,整行数据被载入并暂存其中(这是读操作的前半部分,写操作也需要这一步,称为Read-Modify-Write)。 - -5. **发送列地址**: 控制器将**列地址 88** 放到同样的地址线 `A₀~A₁₀` 上。 - -6. **发送数据**: 控制器将数据 `1011` 放到数据线 `D₁~D₄` 上。 - -7. **CAS 与 WE 选通**: 控制器同时拉低 `CAS` 和 `WE` 信号。 - - - `CAS` 的下降使**列地址缓冲器**锁存地址 `88`。 - - - `WE` 的低电平告知**定时和控制**单元,这是一个**写操作**。 - -8. **列选择与写入**: **列译码器**解码地址 `88`。**数据输入缓冲器**接收 `1011`,并通过I/O门将其写入到**读出放大器**中第88列的位置,覆盖了原来暂存的数据。 - -9. **写回存储元**: 读出放大器中被更新的数据(`1011`)被重新写回到第5行、第88列的4个物理**存储电容 `C_s`** 中。 - -10. **操作结束**: `RAS`, `CAS`, `WE` 信号恢复高电平,一次写周期完成。数据 `1011` 作为电荷被存储在四个微小的电容器中。 - - -#### 阶段二:静默与刷新 (数据的维生) - -- **电荷泄漏**: 存储着'1'的电容开始缓慢漏电,电压下降。若不干预,数据将在几十毫秒内丢失。 - -- **刷新周期**: 在数据丢失前,DRAM芯片的**定时和控制**单元会启动一次刷新。 - - 1. 内部**刷新计数器**提供一个行地址(假设恰好是 `5`)。 - - 2. `MUX` 切换到刷新计数器通路。 - - 3. 控制器执行一个“仅RAS”周期,即只拉低`RAS`。 - - 4. 行译码器激活第5行,将整行数据(包括我们那个电压已略微衰减的 `1011`)读入**读出放大器**。 - - 5. 读出放大器将这个衰减的信号**重新放大**到标准的逻辑高/低电平,并**立即写回**到原来的存储元中。 - -- **生命延续**: 我们的数据 `1011` 被成功“续命”,电容被重新充满。这个过程对外部系统是透明的。 - [[notes/408/DRAM都会怎么洗澡🥵\|DRAM都会怎么洗澡🥵]] - -#### 阶段三:读出操作 (数据的价值实现) - -1. **行地址选通**: 与写入操作的步骤1-4完全相同。控制器发送行地址 `5`,`RAS` 下降,第5行的全部内容被读入并暂存在**读出放大器**中。这个过程也刷新了第5行的数据。 - -2. **列地址选通**: 控制器发送列地址 `88`,然后拉低 `CAS` 信号 (`WE` 保持高电平,表示读取)。 - -3. **列选择**: **列译码器**解码地址 `88`,从读出放大器中选中暂存的4位数据 `1011`。 - -4. **输出使能**: 控制器拉低 `OE` 信号,打开**数据输出缓冲器**的三态门。 - -5. **数据上路**: 数据 `1011` 被驱动到外部数据线 `D₁~D₄` 上。 - -6. **读取成功**: 内存控制器从数据线上成功读取 `1011`。 - -7. **操作结束**: `RAS`, `CAS`, `OE` 恢复高电平。读周期结束。 - diff --git "a/src/site/notes/notes/408/DRAM\351\203\275\344\274\232\346\200\216\344\271\210\346\264\227\346\276\241\360\237\245\265.md" "b/src/site/notes/notes/408/DRAM\351\203\275\344\274\232\346\200\216\344\271\210\346\264\227\346\276\241\360\237\245\265.md" deleted file mode 100644 index f7e4052..0000000 --- "a/src/site/notes/notes/408/DRAM\351\203\275\344\274\232\346\200\216\344\271\210\346\264\227\346\276\241\360\237\245\265.md" +++ /dev/null @@ -1,113 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/DRAM都会怎么洗澡🥵","permalink":"/408/DRAM都会怎么洗澡🥵/"} ---- - - -### DRAM的三种基本刷新策略 - -#### 1. 集中式刷新 (Centralized Refresh / Burst Refresh) - -- 原理与执行方式: - - 这是一种最简单直接但最“粗暴”的策略。DRAM芯片在规定的刷新周期(如64ms)内,绝大部分时间都正常响应CPU的读写请求。但在该周期的末尾,找到一个空闲时间,暂停所有对存储器的访问,然后启动一个连续的、不间断的**“刷新脉冲串 (Burst)”**,一次性地、连续地对所有行(例如全部8192行)进行刷新,直到所有行都被刷新完毕。 - -- 流程示意: - - [---------- 正常读写周期 (接近64ms) ----------][-- 死区: 连续刷新所有行 --] - -- "死时间"分析: - - 假设刷新一个行需要一个时钟周期(例如100ns),那么刷新8K(8192)行就需要: - - T死区=8192×100ns=819.2μs - - 在这长达819.2微秒的时间内,CPU或任何其他设备都无法访问内存,内存完全处于“死机”状态,不响应任何读写请求。 - -- **优点:** - - - **控制简单:** 内存控制器的逻辑非常简单,只需要一个定时器,在刷新周期结束前触发一次集中的刷新操作即可。读写逻辑和刷新逻辑完全分离。 - -- **缺点:** - - - **存在明显的“死区”**:这是其致命缺点。在刷新期间,内存完全不可用,对于需要实时响应的系统(如工业控制、实时操作系统)是绝对无法接受的。 - -- **适用场景:** - - - 由于存在严重的“死区”问题,**集中式刷新在现代通用计算机系统中基本不被采用**。它更多是作为一个理论模型,用于教学和理解刷新概念。可能仅适用于某些对实时性要求极低、且允许有固定长暂停的嵌入式或专用系统中。 - - -#### 2. 分散式刷新 (Decentralized Refresh / Distributed Refresh) - -- 原理与执行方式: - - 这种策略将刷新操作均匀地分散到整个刷新周期内。它将刷新周期(如64ms)平分给所有需要刷新的行(如8192行),计算出两次刷新操作之间的最大时间间隔。 - - 刷新间隔=8192行64ms≈7.8μs - - 这意味着,内存控制器必须保证每7.8μs就执行一次行刷新操作。这个刷新周期被穿插在正常的读写周期之间。 - -- 流程示意: - - [读/写] [读/写] ... [刷新行i] [读/写] [读/写] ... [刷新行i+1] ... - - 存储器的每个工作周期实际上由一个读/写操作和一个刷新操作构成。 - -- "死时间"分析: - - 分散式刷新没有集中的、长的“死区”。但它把刷新的时间开销“摊派”到了每一次存储器访问中,使得每个存储周期的总时间被拉长了。例如,一个正常的读写周期可能是80ns,但为了插入刷新,整个系统周期可能被定义为100ns,预留了刷新操作的时间。 - -- **优点:** - - - **无长“死区”**:系统响应不会有长时间的中断,实时性得到了保证。 - -- **缺点:** - - - **降低了整体性能:** 即使在读写不频繁、总线空闲的时候,也必须严格按照固定的时间间隔插入刷新,这会拖慢系统运行速度。在需要密集读写的场景下,频繁的刷新插入会严重影响带宽。 - - - **控制相对复杂:** 需要更精细的时序控制来保证刷新周期的精确插入。 - -- **适用场景:** - - - 适用于**对实时性要求高**、不允许出现长暂停的系统。虽然它也不是现代主流PC的选择,但其设计思想对实时系统有重要意义。 - - -#### 3. 异步式刷新 (Asynchronous Refresh / Staggered Refresh) - -- 原理与执行方式: - - 这是集中式和分散式的折中方案,也是现代计算机系统普遍采用的策略。它同样要求在64ms内刷新完所有行(如8K行),因此平均下来也是每7.8μs需要刷新一行。但它不要求严格地在固定时间点进行刷新。控制器只需要保证在一个刷新周期(64ms)内,发出了足够次数(8192次)的刷新命令即可。 - -- 流程示意: - - 它将刷新操作分解为对单行的刷新,可以在CPU不访问内存的空闲时间,或者对性能影响较小的时机,见缝插针地进行。控制器可以一次性刷新几行,也可以在一段时间内不刷新(只要不超出单行电荷泄漏的最大时限),只要最终能“完成任务”即可。 - -- "死时间"分析: - - 异步刷新既避免了集中式的长“死区”,也避免了分散式的死板。单次刷新一行的时间非常短(如100ns),对系统的影响可以降到最低。 - -- **优点:** - - - **性能影响最小:** 提供了极大的灵活性,可以在总线空闲时进行刷新,最大化了读写操作的有效带宽。这是**性能最好**的刷新方式。 - - - **实时性好:** 避免了长“死区”,保证了系统的及时响应。 - -- **缺点:** - - - **控制最复杂:** 内存控制器需要更智能的逻辑,不仅要跟踪刷新进度,还要判断总线状态,以寻找最佳的刷新时机。 - -- **适用场景:** - - - **几乎所有现代计算机系统**,包括个人电脑、服务器、智能手机等。这是因为它在性能和实时性之间取得了最佳的平衡。 - - ---- - -### 总结对比 - -|特性|集中式刷新|分散式刷新|异步式刷新| -|---|---|---|---| -|**刷新方式**|周期末尾,一次性刷新所有行|均匀分散,穿插在读写周期中|在周期内,见缝插针地刷新各行| -|**“死区”**|**长且固定**,整个系统暂停|**无**,但每个存储周期被拉长|**极短**,对系统影响微乎其微| -|**对读写影响**|影响集中在“死区”时间段|影响分散到每一次操作,整体降速|影响最小,利用空闲周期| -|**控制复杂度**|**简单**|较复杂|**最复杂**| -|**适用场景**|理论教学,极少数特定系统|对实时性有严格要求的系统|**现代通用计算机系统标准方案**| \ No newline at end of file diff --git "a/src/site/notes/notes/408/ICMP\344\272\244\350\255\246\344\275\225\346\227\266\345\257\271IP\346\225\260\346\215\256\346\212\245\345\217\221\345\207\272\347\275\232\345\215\225\345\221\242\360\237\244\224.md" "b/src/site/notes/notes/408/ICMP\344\272\244\350\255\246\344\275\225\346\227\266\345\257\271IP\346\225\260\346\215\256\346\212\245\345\217\221\345\207\272\347\275\232\345\215\225\345\221\242\360\237\244\224.md" deleted file mode 100644 index 55a9883..0000000 --- "a/src/site/notes/notes/408/ICMP\344\272\244\350\255\246\344\275\225\346\227\266\345\257\271IP\346\225\260\346\215\256\346\212\245\345\217\221\345\207\272\347\275\232\345\215\225\345\221\242\360\237\244\224.md" +++ /dev/null @@ -1,75 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/ICMP交警何时对IP数据报发出罚单呢🤔","permalink":"/408/ICMP交警何时对IP数据报发出罚单呢🤔/"} ---- - - -ICMP差错报告报文是在IP数据报的转发或交付过程中发生错误(如目的不可达、生存时间为0等)时,由路由器或目的主机发送给源主机的。但是,当网络层发现IP首部校验和错误时,会立即丢弃该数据报,并不会发送任何ICMP差错报文。 -### 一、ICMP差错报文的发送情况 - -当路由器或目的主机在处理一个IP数据报时,如果遇到无法继续处理的问题,就会生成一个ICMP差错报文,发送给该IP数据报的源主机。这个ICMP报文相当于一份“错误告知书”。 - -1. **终点不可达 (Destination Unreachable, 类型3)**:当路由器或主机无法交付数据报时发送。常见原因有: - - - **网络不可达** (代码0):路由器找不到通往目的网络的路由。 - - - **主机不可达** (代码1):路由器找到了目的网络,但在该网络内找不到目的主机。 - - - **协议不可达** (代码2):目的主机的IP层收到了数据报,但其载荷应交付的运输层协议(如TCP/UDP)未在该主机上运行。 - - - **端口不可达** (代码3):数据报已到达目的主机,指定的协议也存在,但相应的端口号没有进程在监听。 - - - **需要分片但DF位为1** (代码4):路由器需要对数据报进行分片,但IP首部的“不分片(DF)”标志位被置为1。 - -2. **超时 (Time Exceeded, 类型11)**: - - - **TTL=0**:路由器收到一个IP数据报,发现其生存时间(TTL)字段为1或0。路由器将TTL减1后变为0,此时会丢弃该数据报,并向源主机发送ICMP超时报文。`traceroute`命令就利用了这个原理。 - - - **分片重组超时**:目的主机在规定时间内没有收到一个被分片的数据报的所有分片,会丢弃已收到的分片,并向源主机发送此报文。 - -3. **参数问题 (Parameter Problem, 类型12)**:当路由器或目的主机发现IP首部中存在非法字段值或选项不完整等问题时,会丢弃该数据报并发送此报文。 - -4. **源点抑制 (Source Quench, 类型4)**:当路由器或主机由于拥塞而丢弃数据报时,向源点发送此报文,请求源点降低发送速率。**注意**:该报文在现在的互联网中已基本被弃用,被TCP的拥塞控制机制取代,但在考纲中仍可能作为历史知识点出现。 - -5. **重定向 (Redirect, 类型5)**:路由器发现一台主机使用了非最优的路径发送数据时,会向该主机发送重定向报文,告诉它下次应发往的“更近”的路由器。这要求主机和两个路由器在同一个局域网内。 - - -### 二、IP首部校验和错误:为何是例外? - -原因: - -IP首部的校验和(Checksum)字段是用于保证IP首部完整性的。路由器每转发一个数据报,都要重新计算首部校验和。如果计算出的校验和结果不为0,就说明IP首部在传输过程中出现了比特差错,已经损坏。 - -一个损坏的IP首部是不可信的。其中最关键的字段,如**源IP地址和目的IP地址**,可能已经出错了。 - -- **如果源IP地址已经损坏**,那么ICMP差错报文将无法被正确地发送回真正的源主机,反而可能会被发送到一个无辜的、错误的地址,造成网络中不必要的垃圾流量。 - -- **如果目的IP地址已经损坏**,数据报本身就已经迷路了,讨论后续处理已无意义。 - - -因此,协议设计者做出了最安全、最合理的规定:**一旦发现IP首部校验和有误,路由器或主机会立即“沉默地”丢弃该数据报,不做任何进一步处理,包括不发送ICMP差错报文。** - -**流程图:路由器处理数据报的简化逻辑** - -代码段 - -```mermaid -graph TD - A[数据报到达路由器] --> B{校验IP首部校验和}; - B -- 错误 --> C[默默丢弃数据报]; - B -- 正确 --> D{检查TTL是否 > 1}; - D -- 是 --> E[TTL减1, 重新计算校验和, 查找路由表并转发]; - D -- 否 (TTL ≤ 1) --> F[丢弃数据报]; - F --> G[向源主机发送 ICMP 超时报文]; -``` - -### 三、常考点分析与不发送ICMP的其它情况 - -除了IP首部校验和错误外,考研中还经常考察以下几种**不发送ICMP差错报文**的情况,必须牢记: - -1. **对ICMP差错报文不再发送ICMP差错报文**:为了防止两个节点之间无限循环地发送ICMP差错报文,协议规定收到ICMP差错报文后即使发现它有问题,也不再为它生成新的ICMP差错报文。 - -2. **对第一个分片之后的分片不发送ICMP差错报文**:当一个数据报被分片后,只有第一个分片(片偏移为0)携带了完整的运输层首部信息(如TCP/UDP端口号)。如果后续分片出错,由于缺乏足够信息,且为了避免对同一个数据报的多个分片都发送差错报文而导致“ICMP风暴”,规定只对第一个分片出错时才发送ICMP报文。 - -3. **对具有多播或广播地址的数据报不发送ICMP差错报文**:向一个组或网络中的所有主机报告差错会导致网络流量的急剧增加(广播风暴),这是不被允许的。 - -4. **对作为链路层广播的数据报不发送ICMP差错报文**:这与上一条原理类似。 diff --git "a/src/site/notes/notes/408/IO\345\261\202\346\254\241\347\273\223\346\236\204\344\273\245\345\217\212\346\257\217\345\261\202\347\232\204\344\275\234\347\224\250.md" "b/src/site/notes/notes/408/IO\345\261\202\346\254\241\347\273\223\346\236\204\344\273\245\345\217\212\346\257\217\345\261\202\347\232\204\344\275\234\347\224\250.md" deleted file mode 100644 index 6bb7b13..0000000 --- "a/src/site/notes/notes/408/IO\345\261\202\346\254\241\347\273\223\346\236\204\344\273\245\345\217\212\346\257\217\345\261\202\347\232\204\344\275\234\347\224\250.md" +++ /dev/null @@ -1,103 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/IO层次结构以及每层的作用","permalink":"/408/IO层次结构以及每层的作用/"} ---- - - -### **I/O层次结构及各层作用** - -#### **第一层:用户层I/O软件 (User-Level I/O Software)** - -- **作用**: 这一层是与用户程序直接交互的部分。它为程序员提供了简单、易用的接口(API),并将这些调用转换为对操作系统的服务请求(即系统调用)。它还可能涉及数据的格式化和用户态的缓冲。 - -- **在本例中的作用**: - - 1. 程序员编写的代码是 `read(fd, buf, 100)`。这是一个标准的库函数调用。 - - 2. C库(`libc`)中的`read`函数实现,会将这些参数(文件描述符、缓冲区地址、字节数)打包好,然后通过一个**陷入(trap)指令**,触发一个**系统调用**,请求内核提供读文件服务。 - - 3. **简单来说,这一层就是我们编程时使用的 `printf`、`scanf`、`read`、`write` 等函数,它们是通往操作系统内核的大门。** - - -#### **第二层:设备无关的操作系统软件 (Device-Independent OS Software)** - -- **作用**: 这是操作系统内核中处理I/O的核心部分,其目标是提供一个统一的框架来管理所有设备,而无需关心具体设备的细节。它的职责包括: - - - **统一接口**: 为所有设备驱动提供一个统一的接口(如都支持`open`, `read`, `write`等)。 - - - **设备命名与保护**: 将文件路径(如`/home/user/data.txt`)映射到具体的设备和文件。 - - - **缓冲与高速缓存 (Buffering/Caching)**: 为了提高性能,实现内核I/O缓冲区(页高速缓存),减少对物理设备的访问次数。 - - - **差错处理**: 提供通用的错误报告和处理框架。 - -- **在本例中的作用**: - - 1. 系统调用进入内核后,这一层开始工作。它根据文件描述符`fd`找到对应的文件信息,确定需要读取的是磁盘上的哪个文件,以及从文件的哪个偏移位置开始读取。 - - 2. 它计算出这100个字节的数据对应于文件的哪个**逻辑块**。 - - 3. **(关键步骤)** 它首先检查**内核的页高速缓存**,看看这个逻辑块是否已经存在于内存中。 - - - **如果命中缓存**:直接从内存中复制数据到用户缓冲区,整个I/O请求可能就此完成,无需访问磁盘。 - - - **如果未命中缓存**:它必须向下一层发出请求。它会将“文件的逻辑块号”转换为“设备的物理块号”,然后向磁盘的设备驱动程序发出一个通用的、与具体磁盘型号无关的命令,例如:“请读取逻辑块号为`LBA 12345`的数据”。 - - -#### **第三层:设备驱动程序 (Device Drivers)** - -- **作用**: 这是与硬件直接相关的“翻译官”。它接收来自上层(设备无关层)的抽象命令,并将其翻译成特定硬件控制器能够理解的具体指令。每个设备(如NVIDIA的显卡、Intel的SATA控制器)都有其专属的驱动程序。 - -- **在本例中的作用**: - - 1. 磁盘的设备驱动程序收到了“读取逻辑块号`LBA 12345`”的命令。 - - 2. 驱动程序知道当前计算机上安装的是哪款硬盘,以及如何与其控制器通信。 - - 3. 它将抽象的块号转换为该硬盘能懂的**柱面号(Cylinder)、磁头号(Head)和扇区号(Sector)**。 - - 4. 然后,它通过I/O总线,向磁盘控制器的**寄存器**中写入一连串的命令和参数。 - - 5. 发出命令后,驱动程序通常会**阻塞**等待该请求的进程(使其进入等待状态),并让出CPU。 - - -#### **第四层:中断处理程序 (Interrupt Handlers)** - -- **作用**: 当慢速的I/O设备完成其任务时,它需要一种方式来通知CPU。中断就是这个机制。中断处理程序是一段特殊的代码,它在I/O完成后被CPU执行,用于处理I/O完成后的“善后”工作。 - -- **在本例中的作用**: - - 1. 磁盘硬件完成了读取数据块的任务(通常通过DMA直接将数据放入内存的内核缓冲区)。 - - 2. 磁盘控制器向CPU发送一个**中断信号**。 - - 3. CPU立刻暂停当前正在执行的任何任务,跳转到预设的**中断服务例程**(即中断处理程序)。 - - 4. 该程序检查磁盘控制器的状态,确认数据传输成功且无错误。 - - 5. 它会**唤醒**之前因为等待这个I/O而阻塞的进程(将其状态从“等待”改为“就绪”)。 - - 6. 处理完毕后,从中断返回,CPU可以继续执行被中断的任务或调度刚被唤醒的进程。 - - -#### **第五层:硬件 (Hardware)** - -- **作用**: 物理设备本身,包括设备控制器(芯片)和设备主体(如磁盘盘片、打印机机械结构等)。它负责执行由设备驱动程序发来的电子命令。 - -- **在本例中的作用**: - - 1. 磁盘控制器接收到寄存器中的命令。 - - 2. 它驱动磁头臂移动到正确的柱面(**寻道**),选择正确的磁头。 - - 3. 等待磁盘盘片旋转,使目标扇区转到磁头下方(**旋转延迟**)。 - - 4. 读取扇区上的磁信号,转换为数据流,并通过**DMA**将其直接传输到内存中指定的位置。 - - 5. 传输完成后,向CPU发出中断信号。 - - -总结一下请求的流程: -``` - -用户程序 -> C库 -> 系统调用 -> 设备无关层 -> 设备驱动层 -> 硬件 -> [I/O操作] -> 中断 -> 中断处理层 -> 设备驱动层 -> 设备无关层 -> 返回用户程序 -``` diff --git "a/src/site/notes/notes/408/IO\346\225\260\346\215\256\350\277\233\345\205\245\345\206\205\345\255\230\345\211\215\357\274\214\347\253\237\350\246\201\345\234\250CPU\347\232\204\345\257\204\345\255\230\345\231\250\351\207\214\345\205\210\344\275\217\344\270\200\346\231\232\360\237\245\265.md" "b/src/site/notes/notes/408/IO\346\225\260\346\215\256\350\277\233\345\205\245\345\206\205\345\255\230\345\211\215\357\274\214\347\253\237\350\246\201\345\234\250CPU\347\232\204\345\257\204\345\255\230\345\231\250\351\207\214\345\205\210\344\275\217\344\270\200\346\231\232\360\237\245\265.md" deleted file mode 100644 index 226bafc..0000000 --- "a/src/site/notes/notes/408/IO\346\225\260\346\215\256\350\277\233\345\205\245\345\206\205\345\255\230\345\211\215\357\274\214\347\253\237\350\246\201\345\234\250CPU\347\232\204\345\257\204\345\255\230\345\231\250\351\207\214\345\205\210\344\275\217\344\270\200\346\231\232\360\237\245\265.md" +++ /dev/null @@ -1,73 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/IO数据进入内存前,竟要在CPU的寄存器里先住一晚🥵","permalink":"/408/IO数据进入内存前,竟要在CPU的寄存器里先住一晚🥵/"} ---- - - -### 二、 I/O控制方式与数据通路详解 -#### 1. 程序查询方式 (Programmed I/O) - -这是最原始的I/O方式。 - -- **过程**:CPU执行I/O指令后,会反复地“轮询”I/O设备的状态寄存器,看其是否准备就绪。一旦就绪,CPU再执行`IN`(输入)或`OUT`(输出)指令,以**CPU内部的累加器(Accumulator)或通用寄存器**为中介,一次传送一个字(Word)的数据。 - -- **数据通路**: - - - **输入**:I/O设备 → I/O端口数据寄存器 → **CPU累加器/GPR** → 主存 - - - **输出**:主存 → **CPU累加器/GPR** → I/O端口数据寄存器 → I/O设 - -#### 2. 中断驱动方式 (Interrupt-Driven I/O) - - -- **过程**:CPU发出I/O指令后,不再等待,而是继续执行其他程序。当I/O设备准备好数据后,会主动向CPU发送一个**中断请求**。CPU响应中断后,暂停当前任务,转而执行一段中断服务程序,在服务程序中完成一次数据的读写。 - -- **数据通路**:**与程序查询方式完全相同!** 数据传送的动作本身,仍然需要CPU执行指令,通过**累加器/GPR**来完成。 - - - **输入**:I/O设备 → I/O端口 → **CPU累加器/GPR** → 主存 - - - **输出**:主存 → **CPU累加器/GPR** → I/O端口 → I/O设备 - - -#### 3. 直接存储器存取方式 (Direct Memory Access, DMA) - -- **过程**:CPU只需向**DMA控制器(DMAC)**下达指令,告诉它“你要把哪个I/O设备的数据,传送到主存的哪个地址,传送多少个字节”。设置完成后,CPU就可以“撒手不管”了。整个数据的传输过程由DMAC全权接管,在DMAC的控制下,数据直接在I/O设备和主存之间传送。传送结束后,DMAC再通过一个中断信号通知CPU“任务已完成”。 - -- **数据通路**: - - - **输入/输出**:I/O设备 ↔ **主存** -#### 图示:中断方式 vs DMA方式的数据通路 - -``` - +-------+ 数据 +-----+ 数据 +------+ - | I/O | <--------------> | CPU | <------------> | 主存 | - +-------+ (寄存器中转) +-----+ +------+ - | ^ - |__中断请求/控制________| - 图1:中断驱动方式的数据通路 (数据经过CPU) - - - +-------+ +------+ - | I/O | <------------ 数据直接传送 -------------> | 主存 | - +-------+ +------+ - | ^ - | +---------+ | - |__ DMA请求/控制 ____| DMAC |____ 总线控制 ____| - +---------+ - ^ - |__ CPU发出指令/接收中断 __ - | - +-----+ - | CPU | - +-----+ - 图2:DMA方式的数据通路 (数据不经过CPU) -``` - ---- - -### 四、 总结与边界情况 - -|I/O控制方式|数据是否经过CPU寄存器|CPU介入程度(数据传送阶段)|硬件复杂度|适用场景| -|---|---|---|---|---| -|**程序查询**|**是**|完全占用CPU|简单|古老、极低速设备| -|**中断驱动**|**是**|每次传送一个字时介入|较简单|中低速设备| -|**DMA**|**否**|**完全不介入**|复杂 (需DMAC)|高速设备 (硬盘、网卡)| diff --git "a/src/site/notes/notes/408/IO\347\232\204\347\213\254\347\253\213\347\274\226\345\235\200\344\270\216\347\273\237\344\270\200\347\274\226\345\235\200,\347\212\266\346\200\201&\346\216\247\345\210\266\345\257\204\345\255\230\345\231\250.md" "b/src/site/notes/notes/408/IO\347\232\204\347\213\254\347\253\213\347\274\226\345\235\200\344\270\216\347\273\237\344\270\200\347\274\226\345\235\200,\347\212\266\346\200\201&\346\216\247\345\210\266\345\257\204\345\255\230\345\231\250.md" deleted file mode 100644 index 393ad8d..0000000 --- "a/src/site/notes/notes/408/IO\347\232\204\347\213\254\347\253\213\347\274\226\345\235\200\344\270\216\347\273\237\344\270\200\347\274\226\345\235\200,\347\212\266\346\200\201&\346\216\247\345\210\266\345\257\204\345\255\230\345\231\250.md" +++ /dev/null @@ -1,485 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/IO的独立编址与统一编址,状态&控制寄存器","permalink":"/408/IO的独立编址与统一编址,状态&控制寄存器/"} ---- - - -## 图片解释与实现逻辑 -图片展示了两种打印机模型,分别代表了两种不同的 I/O 编址方式和相应的 I/O 操作逻辑: - -### 打印机型号 1:统一编址方式(内存映射 I/O,Memory-Mapped I/O) -- **特点**:I/O 设备被视为内存的一部分,与内存共享同一套地址空间。CPU 访问 I/O 设备的方式与访问内存单元的方式相同,即使用普通的访存指令(如 `MOV` 指令)。 - -- **I/O 端口与地址**: - - - **数据缓冲寄存器**:地址为 `0xffff2021`。这是一个内存地址,CPU 可以直接向这个地址写入数据,这些数据就会被送到打印机的数据缓冲器。 - - - **控制/状态寄存器**:地址为 `0xffffe0f1`。这也是一个内存地址,CPU 通过读写这个地址来获取打印机状态或发送控制命令。 - -- **`print` 函数逻辑 (`print(char s[16])`)**: - - 1. `F5H->addr(0xffff2021)`: 这行代码表示将十六进制值 `F5H` 写入到地址 `0xffff2021`。根据下方的“命令字”表格,`F5H` 对应的行为是“**将假定内容写入数据缓冲寄存器**”。这里的 `F5H` 实际上是伪代码中用于表示“要打印的数据”或“一个数据字节”。 - - 2. `F6H->addr(0xffffe0f1)`: 这行代码表示将十六进制值 `F6H` 写入到地址 `0xffffe0f1`。根据下方的“命令字”表格,`F6H` 对应的行为是“**打印数据缓冲寄存器中的值**”。这意味着 CPU 通过向控制寄存器写入 `F6H` 命令,触发打印机开始打印其数据缓冲器中的内容。 - -- print 函数的整体逻辑: - - 伪代码 print(char s[16]) 意图是打印一个16字节的字符串 s。 - - - 将待打印的第一个字符 `s[0]` 写入数据缓冲寄存器 `0xffff2021`。 - - - 向控制/状态寄存器 `0xffffe0f1` 写入 `F6H` 命令,启动打印。 - - - 这个过程会循环16次,每次写入一个字符并启动打印。 - - - **缺陷**:这种伪代码简化了实际的I/O控制过程。在实际中,CPU写入数据后,通常需要查询状态寄存器以确认设备是否准备好接收下一个数据字节,或者是否打印完成,而不是简单地连续写入和触发。如果 `F6H` 命令是让打印机打印一个字节,那么每一次 `F5H` 和 `F6H` 的组合就是传输一个字节并启动打印。 - - -### 打印机型号 2:独立编址方式(I/O 端口编址,Port-Mapped I/O) -- **特点**:I/O 设备有自己独立的地址空间,与内存地址空间是分开的。CPU 访问 I/O 设备需要使用专门的 I/O 指令(如 `IN` 和 `OUT` 指令)。 - -- **I/O 端口与地址**: - - - **数据缓冲寄存器**:端口号 `20H`。CPU 需要使用 `OUT 20H, data` 这样的指令将数据写入该端口。 - - - **控制/状态寄存器**:端口号 `21H`。CPU 需要使用 `OUT 21H, command` 这样的指令向该端口发送命令。 - -- **`print` 函数逻辑 (`print(char s[16])`)**: - - 1. `F5H in 20H, 20H`:这行伪代码表示将 `F5H`(假定内容)写入到端口号为 `20H` 的数据缓冲寄存器。这里的 `20H` 表示端口地址。 - - 2. `EEH in 21H, 21H`:这行伪代码表示将 `EEH`(打印数据缓冲寄存器中的值)写入到端口号为 `21H` 的控制/状态寄存器。这里的 `21H` 表示端口地址。 - -- **`print` 函数的整体逻辑**: - - - 与打印机型号1类似,伪代码意图是打印一个16字节的字符串 `s`。 - - - 每次将一个字符写入端口 `20H`。 - - - 每次向端口 `21H` 发送 `EEH` 命令,启动打印。 - - - **缺陷**:同样简化了实际的I/O控制过程,没有考虑状态查询和同步。 - - ---- - - - -## I/O 过程中的数据流向 - -### 1. 从 CPU 到 I/O 端口(数据缓冲寄存器) -- **统一编址方式**: - - - **数据流向**:CPU -> 地址总线/数据总线 -> 内存地址译码器(或直接 I/O 地址译码器)识别为 I/O 设备地址 -> I/O 接口内部的数据缓冲寄存器。 - - - **指令**:普通的内存写指令(如 `MOV [0xffff2021], AL`)。 - -- **独立编址方式**: - - - **数据流向**:CPU -> I/O 地址总线/数据总线(或复用总线但有特殊控制信号) -> I/O 端口译码器 -> I/O 接口内部的数据缓冲寄存器。 - - - **指令**:专门的 I/O 写指令(如 `OUT 20H, AL`)。 - - -### 2. 从 CPU 到 I/O 端口(控制/状态寄存器) -- **统一编址方式**: - - - **数据流向**:与写入数据缓冲寄存器类似,只是写入的是控制命令。CPU -> 地址总线/数据总线 -> I/O 接口内部的控制/状态寄存器。 - - - **指令**:普通的内存写指令(如 `MOV [0xffffe0f1], F6H`)。 - -- **独立编址方式**: - - - **数据流向**:与写入数据缓冲寄存器类似,只是写入的是控制命令。CPU -> I/O 地址总线/数据总线 -> I/O 接口内部的控制/状态寄存器。 - - - **指令**:专门的 I/O 写指令(如 `OUT 21H, EEH`)。 - - -### 3. I/O 接口内部的数据流向(以打印为例) -- 数据写入**数据缓冲寄存器**后,就等待打印机处理。 - -- CPU 通过写入命令到**控制/状态寄存器**来驱动设备工作。例如,发送“打印”命令后,打印机接口会从其数据缓冲寄存器中取出数据,并通过打印机内部的机制(如打印头、墨水/碳粉等)将其呈现在纸张上。 - -- 打印机工作过程中,其状态(如忙、空闲、缺纸、墨尽等)会反映在**状态寄存器**中。CPU 可以通过读取状态寄存器来了解设备的工作状态,从而进行同步和错误处理。 - - -### 4. 从 I/O 设备到 CPU(数据回流,如读取状态) -- **统一编址方式**: - - - **数据流向**:I/O 接口内部的状态/数据寄存器 -> 数据总线 -> CPU 寄存器或内存单元。 - - - **指令**:普通的内存读指令(如 `MOV AL, [0xffffe0f1]`)。 - -- **独立编址方式**: - - - **数据流向**:I/O 接口内部的状态/数据寄存器 -> 数据总线 -> CPU 寄存器。 - - - **指令**:专门的 I/O 读指令(如 `IN AL, 21H`)。 - - ---- - -1. **I/O 编址方式**:这是最核心的考点。 - - - **统一编址(内存映射 I/O)**: - - - **优点**:不需要专门的 I/O 指令,CPU 访问 I/O 端口和内存一样快,程序设计更灵活。 - - - **缺点**:I/O 端口会占用一部分内存地址空间,限制了内存寻址范围;需要更复杂的 I/O 地址译码电路。 - - - **独立编址(I/O 端口编址)**: - - - **优点**:I/O 地址空间独立,不占用内存地址空间;CPU 和 I/O 设备有明确的分离。 - - - **缺点**:需要专门的 I/O 指令,且 I/O 指令通常比内存访存指令慢;编程不如统一编址灵活。 - -2. **数据流向**:考察对 CPU、总线、I/O 接口、设备之间数据传输路径的理解。 - -3. **设备控制方式**:图片中的伪代码体现了**程序查询方式**的简单逻辑(尽管简化了状态查询)。在实际考题中,会对比程序查询、中断、DMA 等方式的特点、优缺点及适用场景。 - -4. **寄存器作用**:数据缓冲寄存器(存放待传输数据)、控制寄存器(CPU 发送命令)、状态寄存器(CPU 读取设备状态)。 - -5. **指令类型**:区分统一编址下的访存指令和独立编址下的专用 I/O 指令。 - - - -## 状态寄存器状态存储与CPU查询示例 - -### 1. 状态寄存器的位定义 -假设我们有一个简单的打印机接口,其**状态寄存器(Status Register)**是一个 8 位的寄存器。我们可以定义其中一些位的含义如下: - -|位编号 (Bit)|含义|值 = 0 的意义|值 = 1 的意义| -|---|---|---|---| -|Bit 0|**忙/空闲状态 (BUSY)**|打印机空闲(Ready)|打印机忙(Busy)| -|Bit 1|**错误状态 (ERROR)**|无错误|发生错误| -|Bit 2|**缺纸状态 (PAPER_OUT)**|有纸|缺纸| -|Bit 3|**墨尽状态 (INK_LOW)**|墨水充足|墨水低或墨尽| -|...|(其他状态位)||| - -**例如:** - -- 如果状态寄存器的值为 `0000 0001` (二进制),表示只有 Bit 0 为 1,即打印机处于**忙碌状态**。 - -- 如果状态寄存器的值为 `0000 0100` (二进制),表示只有 Bit 2 为 1,即打印机**缺纸**。 - - -### 2. CPU 查询状态寄存器的工作流程(伪代码示例) -假设我们使用**统一编址方式**(内存映射 I/O),并且打印机的状态寄存器地址是 `0xFFFFF000`。 - -```c -#define PRINTER_STATUS_REG_ADDR 0xFFFFF000 // 打印机状态寄存器的内存地址 -#define PRINTER_DATA_REG_ADDR 0xFFFFF004 // 打印机数据寄存器的内存地址 -#define PRINTER_CONTROL_REG_ADDR 0xFFFFF008 // 打印机控制寄存器的内存地址 - -// 状态寄存器位掩码 (用于判断特定状态位) -#define STATUS_BUSY_MASK 0x01 // Bit 0 -#define STATUS_ERROR_MASK 0x02 // Bit 1 -#define STATUS_PAPER_MASK 0x04 // Bit 2 -#define STATUS_INK_MASK 0x08 // Bit 3 -// ... 其他位定义 - -// 打印单个字符的函数 (采用程序查询方式) -void printChar(char c) { - volatile unsigned char* status_reg = (volatile unsigned char*)PRINTER_STATUS_REG_ADDR; - volatile unsigned char* data_reg = (volatile unsigned char*)PRINTER_DATA_REG_ADDR; - volatile unsigned char* control_reg = (volatile unsigned char*)PRINTER_CONTROL_REG_ADDR; - - // 1. 等待打印机空闲 - while ((*status_reg & STATUS_BUSY_MASK) != 0) { // 循环检测BUSY位,直到它变为0 - // 打印机忙,CPU在这里空转等待 - // 可以在这里添加一些延时或简单的空操作 - } - - // 2. 检查是否有错误或其他异常状态 - if ((*status_reg & STATUS_ERROR_MASK) != 0) { - // 检测到错误,进行错误处理(例如,打印错误信息,或者尝试重置打印机) - printf("Error: Printer encountered an error!\n"); - // 这里可以添加更复杂的错误处理逻辑,例如,向控制寄存器写入重置命令 - return; // 或者退出 - } - if ((*status_reg & STATUS_PAPER_MASK) != 0) { - printf("Error: Printer is out of paper!\n"); - // 提示用户加纸,或者等待用户加纸 - return; - } - if ((*status_reg & STATUS_INK_MASK) != 0) { - printf("Warning: Printer ink is low!\n"); - // 可以继续打印,但给出警告 - } - - // 3. 打印机空闲且无严重错误,写入数据 - *data_reg = c; // 将字符写入数据寄存器 - - // 4. 发送打印命令 (假设向控制寄存器写入特定值启动打印) - // 假设写入 0x01 表示启动打印当前数据 - *control_reg = 0x01; - - // 5. 再次等待打印机空闲(直到这个字符被处理完成) - while ((*status_reg & STATUS_BUSY_MASK) != 0) { - // 等待当前字符打印完成 - } -} - -// 打印字符串的函数 -void printString(const char* str) { - for (int i = 0; str[i] != '\0'; i++) { - printChar(str[i]); - } -} - -// 主程序示例 -int main() { - // 模拟打印一些字符 - printString("Hello, 408!\n"); - printString("This is a test print.\n"); - - return 0; -} -``` - - -### 3. 工作原理和数据流向 -1. **CPU 写入指令**:当程序要打印一个字符 `c` 时,CPU 执行 `printChar(c)` 函数。 - -2. **查询状态寄存器(数据回流到 CPU)**: - - - 首先,CPU 会执行一条**读内存指令**(在统一编址下),从地址 `0xFFFFF000` 读取打印机状态寄存器的内容。 - - - 读取到的状态值会进入 CPU 的某个寄存器。 - - - CPU 通过**逻辑运算**(例如 `&` 操作和比较),检查状态值中 `BUSY` 位是否为 1。 - -3. **循环等待 (程序查询)**: - - - 如果 `BUSY` 位为 1,表示打印机正忙,CPU 会陷入一个**忙等待(busy-waiting)**循环。它会不断地重复读取状态寄存器,直到 `BUSY` 位变为 0。 - - - 在这个等待过程中,CPU 无法执行其他有用的任务,这是程序查询方式的主要缺点。 - -4. **状态判断与错误处理**: - - - 一旦打印机空闲,CPU 会进一步检查 `ERROR`、`PAPER_OUT`、`INK_LOW` 等位。 - - - 如果某个错误位被设置(例如 `PAPER_OUT` 为 1),程序可以打印相应的错误消息,甚至暂停打印,提示用户处理。 - -5. **写入数据(CPU 到 I/O)**: - - - 如果一切正常,CPU 执行一条**写内存指令**,将要打印的字符 `c` 写入到打印机数据寄存器(地址 `0xFFFFF004`)。 - -6. **发送控制命令(CPU 到 I/O)**: - - - 接着,CPU 执行一条**写内存指令**,将启动打印的命令(`0x01`)写入到打印机控制寄存器(地址 `0xFFFFF008`)。 - - - 打印机接口接收到命令后,会将 `BUSY` 位设置为 1,开始处理数据并进行打印。 - -7. **循环等待下一个空闲**:CPU 再次进入忙等待循环,等待 `BUSY` 位变为 0,表示当前字符已打印完成,可以继续处理下一个字符或任务。 - - - -## 状态寄存器和控制寄存器是融为一体的吗? -**通常情况下,状态寄存器和控制寄存器是分开的两个逻辑实体,但它们可以被设计在同一个 I/O 接口芯片中,甚至可以共享同一个物理地址或端口,通过读写操作来区分其功能。** - -- **逻辑上**:它们是两个不同的功能模块。 - - - **状态寄存器 (Status Register)**:主要由 I/O 设备或接口**写入**,CPU **读取**,用于反映设备当前的状态信息。 - - - **控制寄存器 (Control Register)**:主要由 CPU **写入**,I/O 设备或接口**读取**,用于接收 CPU 的控制命令。 - -- **物理实现上**: - - - **独立地址/端口**:最常见的设计是,它们拥有各自独立的内存地址或 I/O 端口。例如,打印机的数据缓冲寄存器可能在 `20H`,状态寄存器在 `21H`,控制寄存器在 `22H`。 - - - **共享地址/端口,读写分离**:有些设计为了节省地址空间,会将状态寄存器和控制寄存器映射到同一个物理地址或端口。在这种情况下: - - - 当 CPU **读取**该地址/端口时,读到的是**状态信息**(状态寄存器)。 - - - 当 CPU 写入该地址/端口时,写入的是控制命令(控制寄存器)。 - - 这种设计需要 I/O 接口内部的硬件电路来区分读写操作,将数据路由到正确的内部寄存器。 - - - **部分功能融合**:在某些简单的接口中,少数状态位和控制位可能在同一个寄存器中,但各自负责不同的功能。但这并不代表它们完全融合,只是对资源的复用。 - - -**结论**:可以说它们是**逻辑分离,但可能在物理实现上相邻、相关联,甚至共享地址/端口但功能互补**。 - ---- - - - -## 状态寄存器和控制寄存器的内部结构 -状态寄存器和控制寄存器都是由一系列**二进制位(bit)**组成的寄存器。每个位或几个位组合起来代表一个特定的状态信息或控制命令。 - -### 1. 状态寄存器 (Status Register) 的内部结构 -状态寄存器是一个用于**反映设备当前工作状态**的位集合。每个位通常代表一个布尔型的状态信息(真/假,有/无)。 - -|位编号 (Bit)|含义|值 = 0 的意义|值 = 1 的意义|举例说明(打印机)| -|---|---|---|---|---| -|Bit 0|**忙/空闲 (BUSY)**|设备空闲(Ready)|设备忙碌(Busy)|CPU 写入数据或命令后,设备将此位设为 1,完成后设为 0。| -|Bit 1|**数据就绪 (DRDY)**|数据寄存器无有效数据|数据寄存器有新数据可读|设备完成一次数据读取/转换后,设为 1,CPU 读取后设为 0。| -|Bit 2|**错误 (ERROR)**|无错误|发生错误|设备检测到故障时设为 1,CPU 可读取以进行错误处理。| -|Bit 3|**缺纸 (PAPER_OUT)**|纸张正常|纸张不足或用尽|打印机纸仓状态传感器检测到缺纸时设为 1。| -|Bit 4|**墨尽 (INK_LOW)**|墨水充足|墨水不足或用尽|墨盒传感器检测到墨量低时设为 1。| -|Bit 5|**完成 (DONE)**|未完成当前操作|当前操作已完成|设备完成一个任务(如打印一页)后设为 1。| -|Bit 6|**中断请求 (IRQ)**|无中断请求|正在请求中断|设备需要 CPU 服务时,将此位设为 1。| -|...|(其他状态位)|||| - - -### 2. 控制寄存器 (Control Register) 的内部结构 -控制寄存器是一个用于**向设备发送命令和配置其工作模式**的位集合。CPU 通过设置这些位来改变设备的行为。 - -|位编号 (Bit)|含义|值 = 0 的意义|值 = 1 的意义|举例说明(打印机)| -|---|---|---|---|---| -|Bit 0|**启动/停止 (START)**|停止操作|启动操作|CPU 写入 1 启动打印,写入 0 停止。| -|Bit 1|**中断使能 (INT_EN)**|禁用中断|使能中断|CPU 写入 1 允许设备在完成时产生中断,写入 0 禁用。| -|Bit 2|**复位 (RESET)**|正常工作|复位设备|CPU 写入 1 来重置设备到初始状态。| -|Bit 3|**打印模式 (MODE1)**|(例如) 普通模式|(例如) 草稿模式|CPU 通过设置这些位来选择打印质量、单双面等。| -|Bit 4|**打印模式 (MODE2)**|(例如) 文本模式|(例如) 图片模式|| -|Bit 5|**数据传输方向 (DIR)**|(例如) 输入方向|(例如) 输出方向|用于双向传输设备,CPU 可控制数据流向。| -|...|(其他控制位)|||| - ---- - - -### 表格来展示其结构示例 -以下表格展示了一个假设的 I/O 接口中,状态寄存器和控制寄存器的**位级结构**: - -#### 示例 1:状态寄存器 (8 位) -|位编号 (Bit)|7|6|5|4|3|2|1|0| -|---|---|---|---|---|---|---|---|---| -|**功能**|未用|**IRQ**|**完成**|**墨尽**|**缺纸**|**错误**|**数据就绪**|**忙/空闲**| -|**读写属性**|只读|只读|只读|只读|只读|只读|只读|只读| - -- **解释**:CPU 读取整个 8 位寄存器的值,然后通过位掩码和逻辑与操作来判断各个位的状态。例如,`status & 0x01` 检查忙/空闲位。 - - -#### 示例 2:控制寄存器 (8 位) -|位编号 (Bit)|7|6|5|4|3|2|1|0| -|---|---|---|---|---|---|---|---|---| -|**功能**|未用|未用|**传输方向**|**打印模式2**|**打印模式1**|**复位**|**中断使能**|**启动/停止**| -|**读写属性**|只写|只写|只写|只写|只写|只写|只写|只写| - -- **解释**:CPU 写入整个 8 位寄存器的值,通过设置不同的位组合来发送命令或配置设备。例如,写入 `0x01` 启动设备,写入 `0x04` 复位设备。 - - -### 注意: -- 这些位定义是**接口设计者约定**的,不同设备和接口会有不同的位定义。 - -- 某些位可能是**读/写**的(例如,可以读取当前配置,也可以写入新配置)。 - -- 在实际中,为了提供更丰富的状态和更复杂的控制,寄存器可能会有 16 位、32 位甚至更多。 - -- **共享地址的情况**:如果状态寄存器和控制寄存器共享一个物理地址 `0xABCD`: - - - `result = READ(0xABCD)`:这将从**状态寄存器**读取值。 - - - `WRITE(0xABCD, command_value)`:这将把 `command_value` 写入**控制寄存器**。 - - ---- - - - -## 详细解释:映射到同一个端口的机制 -当状态寄存器和控制寄存器共享同一个 I/O 端口地址或内存映射地址时,其内部机制如下: - -1. **物理实现**: - - - 在 I/O 接口内部,实际上仍然存在两个独立的硬件寄存器:一个用于存储**状态信息**(状态寄存器),另一个用于存储**控制命令**(控制寄存器)。它们是独立的存储单元,其内容是各自独立的,互不影响。 - - - 它们被设计成**响应同一个外部地址线和数据线的组合**。 - -2. **读写操作的区分**: - - - **CPU 的控制信号**:CPU 在执行 I/O 操作时,除了将地址放到地址总线上,还会发出**读控制信号**(如 `MEMR#` 或 `IOR#`)或**写控制信号**(如 `MEMW#` 或 `IOW#`)。 - - - **I/O 接口内部的逻辑电路**:I/O 接口的地址译码电路在识别到匹配的地址后,还会同时检测 CPU 发出的读写控制信号。 - - - 如果检测到是**读操作信号**:那么 I/O 接口会将**状态寄存器**中的当前内容放到数据总线上,供 CPU 读取。此时,控制寄存器不会被访问。 - - - 如果检测到是**写操作信号**:那么 I/O 接口会将数据总线上的内容(CPU 要写入的命令)写入到**控制寄存器**中。此时,状态寄存器不会被修改。 - -### 示例图示(逻辑示意图) -``` -+-------------------------------------------------------------+ -| CPU | -| | -| 地址总线 (I/O Port Address) | -| 数据总线 (Data) | -| 读控制信号 (Read Signal) ----> | -| 写控制信号 (Write Signal) ----> | -+-------------------------------------------------------------+ - | | | | - | | | | - V V V V -+-------------------------------------------------------------+ -| I/O 接口芯片 | -| | -| +-------------------------+ +-------------------------+ | -| | | | | | -| | **地址译码电路** | | **读写控制逻辑** | | -| | (识别共享端口地址 21H) | | (根据读写信号决定路由) | | -| | | | | | -| +----------|--------------+ +---------|---------------+ | -| | | | -| | V | -| | +---------------------+ | -| | | | | -| +----------------->| **数据通路多路选择器**| | -| | (Mux / Demux) | | -| | | | -| +----------|----------+ | -| | | -| +--------------------+--------------------+ -| | | | -| V V | -| +-----------------+ +-----------------+ | -| | | | | | -| | **状态寄存器** | | **控制寄存器** | | -| | (Status Register)| | (Control Register)| | -| | (由设备更新, CPU读) | | (CPU写, 设备执行) | | -| | | | | | -| +-----------------+ +-----------------+ | -| | -+-------------------------------------------------------------+ - | - V - +---------------+ - | I/O 设备 | - +---------------+ -``` - - -### 举一个具体的例子 -假设打印机的**状态寄存器**和**控制寄存器**都映射到同一个 I/O 端口 `21H` (独立编址方式)。 - -1. **CPU 想读取打印机的状态(例如,是否空闲)**: - - - CPU 发出一条 `IN AL, 21H` 指令。 - - - 这条指令会使 CPU 将端口地址 `21H` 放到 I/O 地址总线上,并发出一个**I/O 读(IOR#)**控制信号。 - - - 打印机 I/O 接口的地址译码电路识别到 `21H`。 - - - I/O 接口内部的读写控制逻辑检测到是**读操作**。 - - - 于是,接口将**状态寄存器**中当前的 8 位状态信息放到数据总线上。 - - - CPU 从数据总线上读取这些数据,存入 `AL` 寄存器。此时 `AL` 寄存器中存放的是打印机的**状态**。 - -2. **CPU 想向打印机发送一个控制命令(例如,启动打印)**: - - - CPU 发出一条 `OUT 21H, EEH` 指令(假设 `EEH` 是启动打印命令)。 - - - 这条指令会使 CPU 将端口地址 `21H` 放到 I/O 地址总线上,将数据 `EEH` 放到数据总线上,并发出一个**I/O 写(IOW#)**控制信号。 - - - 打印机 I/O 接口的地址译码电路识别到 `21H`。 - - - I/O 接口内部的读写控制逻辑检测到是**写操作**。 - - - 于是,接口将数据总线上的 `EEH` 写入到**控制寄存器**中。 - - - 此时,控制寄存器中存放的是 `EEH`,打印机根据这个命令开始执行打印任务。状态寄存器的内容在此操作中不会改变(除非打印机本身的工作状态因命令而改变)。 - - -### 总结 -- **寄存器内容不相同**:状态寄存器和控制寄存器虽然可能共享同一个端口,但它们各自存储着不同的信息。状态寄存器存储的是设备当前的运行状态,由设备更新;控制寄存器存储的是 CPU 发送的命令或配置,由 CPU 写入。 - -- **读写操作是关键**:CPU 发出的**读写控制信号**是区分访问的是状态寄存器还是控制寄存器的核心机制。I/O 接口内部的硬件逻辑根据这些信号将数据路由到正确的寄存器。 diff --git "a/src/site/notes/notes/408/IO\350\256\276\345\244\207\346\240\271\346\215\256\346\225\260\346\215\256\347\232\204\345\255\230\345\217\226\345\222\214\344\274\240\350\276\223\350\277\233\350\241\214\347\232\204\345\210\206\347\261\273.md" "b/src/site/notes/notes/408/IO\350\256\276\345\244\207\346\240\271\346\215\256\346\225\260\346\215\256\347\232\204\345\255\230\345\217\226\345\222\214\344\274\240\350\276\223\350\277\233\350\241\214\347\232\204\345\210\206\347\261\273.md" deleted file mode 100644 index e72cbe1..0000000 --- "a/src/site/notes/notes/408/IO\350\256\276\345\244\207\346\240\271\346\215\256\346\225\260\346\215\256\347\232\204\345\255\230\345\217\226\345\222\214\344\274\240\350\276\223\350\277\233\350\241\214\347\232\204\345\210\206\347\261\273.md" +++ /dev/null @@ -1,124 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/IO设备根据数据的存取和传输进行的分类","permalink":"/408/IO设备根据数据的存取和传输进行的分类/"} ---- - - -### **一、 常见的I/O设备有哪些?** - -首先,我们可以按功能直观地列举一些常见的I/O设备: - -1. **人机交互设备 (Human Interface Devices)**: - - - **输入**: 键盘、鼠标、触摸板、触摸屏、麦克风、摄像头、扫描仪。 - - - **输出**: 显示器、打印机、音箱、耳机、投影仪。 - -2. **存储设备 (Storage Devices)**: - - - 硬盘驱动器(HDD)、固态硬盘(SSD)、U盘、SD卡、光驱(CD/DVD/Blu-ray)、磁带机。 - -3. **网络通信设备 (Network Communication Devices)**: - - - 网卡(NIC)、调制解调器(Modem)、Wi-Fi适配器、蓝牙适配器。 - -4. **其他设备**: - - - 各种传感器(如温度、GPS)、时钟、定时器等。 - - ---- - -### **二、 I/O设备的分类、特点及举例** - -尽管I/O设备五花八门,但从操作系统I/O子系统的角度看,最重要、最根本的分类方式是根据其**数据交换的单位和特性**来划分。主要分为以下三大类: - -#### **1. 块设备 (Block Devices)** - -- **核心特点**: - - - **数据单位**: 信息存储在大小固定的**块 (Block)**中。块是这类设备进行数据读写的基本单位。 - - - **可寻址性**: 每个块都有自己唯一的**地址**。你可以直接访问任意一个块,而无需访问它前面的块。 - - - **访问方式**: 支持**随机访问 (Random Access)** 或直接访问。可以进行`seek`操作来定位到任意块。 - - - **I/O命令**: 其接口中的命令通常是“读/写一整块”或“读/写连续的多个块”。 - - - **独立性**: 通常是可以独立于操作系统工作的设备(例如,你可以把一块硬盘拆下来装到另一台电脑上)。 - - - **传输速率**: 通常较高。 - -- **典型设备举例**: - - - **硬盘驱动器 (HDD)** - - - **固态硬盘 (SSD)** - - - **U盘、SD卡等闪存设备** - - - **光盘 (CD/DVD/Blu-ray)** - - -**本质上,所有用于持久化存储、构成文件系统的设备,都是块设备。** - -#### **2. 字符设备 (Character Devices)** - -- **核心特点**: - - - **数据单位**: 数据以**字符 (Character)或字节 (Byte)** 为单位,形成一个**数据流**。 - - - **可寻址性**: **不可寻址**。它不具备块的概念,你无法直接定位到“第n个字节”。 - - - **访问方式**: 主要是**顺序访问 (Sequential Access)**。数据像水流一样,只能一个接一个地被读取或写入。通常不支持`seek`操作。 - - - **I/O命令**: 其接口中的命令是`get`(获取一个字符)或`put`(发送一个字符)。操作系统和库函数通常会对此进行缓冲,使用户可以按行读写。 - - - **传输速率**: 差异极大,从很慢(键盘)到很快(显卡帧缓冲)都有可能。 - -- **典型设备举例**: - - - **键盘、鼠标**: 产生一个字符/坐标的数据流。 - - - **打印机**: 接收一个字符流并打印。 - - - **串行端口 (COM)**: 在设备间传输字节流。 - - - **声卡**: 产生或接收音频采样数据流。 - - -**本质上,所有以数据流方式进行交互的、非存储类的设备,大多属于字符设备。** - -#### **3. 网络设备 (Network Devices)** - -- **核心特点**: - - - 网络设备足够特殊,以至于操作系统通常将其作为独立的一类来处理,而不是简单归为块设备或字符设备。 - - - **数据单位**: 数据以大小可变的**网络包 (Packet)** 为单位进行交换。 - - - **寻址方式**: 它有自己独特的地址(如MAC地址、IP地址),但这不是块地址。 - - - **访问方式**: 它的接口(API)与前两者完全不同。程序员不使用标准的`read`/`write`,而是使用专门的**套接字接口 (Socket API)**,如`send()`和`receive()`。 - - - **交互特性**: 交互是**异步和不可靠**的(对于UDP等协议)。数据包可能丢失、乱序或重复。需要复杂的协议栈(如TCP/IP)来处理这些问题。 - -- **典型设备举例**: - - - **以太网卡 (NIC)** - - - **Wi-Fi 适配器** - - - **蜂窝网络调制解调器 (Cellular Modem)** - - ---- - -### **总结与对比表格** - -|特性|块设备 (Block Device)|字符设备 (Character Device)|网络设备 (Network Device)| -|---|---|---|---| -|**基本数据单位**|**块 (Block)**,大小固定|**字节/字符 (Byte/Character)**,形成数据流|**包 (Packet)**,大小可变| -|**是否可寻址**|**是**,每个块都有唯一的地址|**否**|**是**,但使用IP/MAC等网络地址| -|**主要访问方式**|**随机访问** (Random Access)|**顺序访问** (Sequential Access)|**面向报文/流** (Message/Stream Oriented)| -|**标准I/O接口**|`read`, `write`, `seek`|`read`, `write` (通常不支持`seek`)|独立的**套接字(Socket)接口** (`send`, `receive`)| -|**典型设备**|硬盘 (HDD/SSD)、U盘、光驱|键盘、鼠标、打印机、串口|网卡、Wi-Fi适配器| diff --git "a/src/site/notes/notes/408/IP\345\234\260\345\235\200\345\235\227\345\210\206\351\205\215\347\232\204\345\234\260\345\235\200\344\270\215\351\207\215\345\217\240\345\222\214\350\267\257\347\224\261\350\201\232\345\220\210\347\232\204\344\270\215\345\274\225\345\205\245\345\244\232\344\275\231\345\234\260\345\235\200.md" "b/src/site/notes/notes/408/IP\345\234\260\345\235\200\345\235\227\345\210\206\351\205\215\347\232\204\345\234\260\345\235\200\344\270\215\351\207\215\345\217\240\345\222\214\350\267\257\347\224\261\350\201\232\345\220\210\347\232\204\344\270\215\345\274\225\345\205\245\345\244\232\344\275\231\345\234\260\345\235\200.md" deleted file mode 100644 index 2fefbaf..0000000 --- "a/src/site/notes/notes/408/IP\345\234\260\345\235\200\345\235\227\345\210\206\351\205\215\347\232\204\345\234\260\345\235\200\344\270\215\351\207\215\345\217\240\345\222\214\350\267\257\347\224\261\350\201\232\345\220\210\347\232\204\344\270\215\345\274\225\345\205\245\345\244\232\344\275\231\345\234\260\345\235\200.md" +++ /dev/null @@ -1,93 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/IP地址块分配的地址不重叠和路由聚合的不引入多余地址","permalink":"/408/IP地址块分配的地址不重叠和路由聚合的不引入多余地址/"} ---- - - -### 一、 地址的不重叠 (Non-overlapping Addresses) - -#### 1. 定义与内涵 - -“地址不重叠”原则,指的是在进行IP地址块**分配**时,任意两个分配出去的地址块,其覆盖的IP地址范围**不能有任何交集**。每一个IP地址在特定的管理域内,都必须被唯一地划分给一个子网或一个组织。 - -这确保了IP地址分配的**唯一性**和**排他性**。 - -#### 2. 作用与重要性 - -此原则的根本目的是为了**消除路由的模糊性,保证转发的确定性**。 - -试想,如果网络中存在两个重叠的地址块,并且它们被分配给了不同的组织或网络区域,那么当一个数据包的目的地址恰好落在这个重叠区域内时,路由器会面临一个无法解决的难题:这个数据包到底应该发往哪个方向? - -- **路由冲突**:互联网上的路由器可能会学习到两条去往同一个地址(或地址段)但指向不同方向的路径。 - -- **转发不一致**:这会导致数据包的转发路径变得不稳定,可能这次发往A处,下次发往B处,造成连接中断或数据丢失。 -#### 3. 示例 - -假设一个网络管理员需要为两个部门分配地址: - -- **正确的分配(不重叠)**: - - - 分配给部门A:`192.168.0.0/24` (范围: `192.168.0.0` ~ `192.168.0.255`) - - - 分配给部门B:`192.168.1.0/24` (范围: `192.168.1.0` ~ `192.168.1.255`) - - - 这两个地址块边界清晰,没有任何交集。 - -- **错误的分配(重叠)**: - - - 分配给部门A:`192.168.0.0/23` (范围: `192.168.0.0` ~ `192.168.1.255`) - - - 分配给部门B:`192.168.1.0/24` (范围: `192.168.1.0` ~ `192.168.1.255`) - - - 此时,部门B的整个地址空间完全被包含在了部门A的地址空间内。当一个去往`192.168.1.50`的数据包到达路由器时,路由器会因无法确定唯一路径而产生路由冲突。 - - ---- - -### 二、 聚合之后不引入多余地址 (No Extraneous Addresses after Aggregation) - -#### 1. 定义与内涵 - -此原则指的是,在将多个离散、连续的较小地址块**聚合(或称“超网化”,Supernetting)成一个更大的地址块,并向外界通告一条总的路由时,这个聚合后的“超网”地址块,其范围必须恰好且仅仅**覆盖了所有原始的小地址块。 - -#### 2. 作用与重要性 - -此原则的根本目的是为了**保证路由的准确性,防止产生“路由黑洞”或“路由劫持”**。 - -如果聚合后的范围过大,包含了本不属于该组织或区域的“多余”地址,那么当这个聚合路由被通告到互联网后,会发生以下情况: - -- **吸引错误流量**:外部路由器会认为那些“多余”的地址也归属于这个组织,从而将去往这些地址的流量错误地发送过来。 - -- **形成路由黑洞**:由于这些“多余”的地址在该组织内部实际上并不存在,或者没有具体的转发路径,这些被错误吸引过来的流量最终将被路由器丢弃,形成一个有去无回的“黑洞”。 -#### 3. 示例 - -假设一个机构拥有以下两个**连续的**C类地址块: - -- `202.118.16.0/24` - -- `202.118.17.0/24` - - -机构希望向其上游ISP通告一条聚合路由。 - -- **二进制分析**: - - ``` - 202.118.00010000.0 (16) - 202.118.00010001.0 (17) - ``` - - 观察其第三个字节的二进制位,可以发现它们的前23位是完全相同的 (`...0001000...`)。因此,可以将它们聚合。 - -- **正确的聚合(精确)**: - - - 聚合后的路由为 `202.118.16.0/23`。 - - - 这个`/23`的地址块覆盖的范围是 `202.118.16.0` ~ `202.118.17.255`,不多不少,**刚刚好**覆盖了原始的两个`/24`地址块。 - -- **错误的聚合(引入多余地址)**: - - - 如果网络工程师错误地计算,将其聚合成 `202.118.16.0/22`。 - - - 这个`/22`的地址块覆盖的范围是 `202.118.16.0` ~ `202.118.19.255`。 - - - 这个聚合路由除了包含原有的`16`和`17`网段外,还**额外引入**了`202.118.18.0/24` 和 `202.118.19.0/24` 这两个可能属于其他机构的地址空间。这就违反了原则,会导致去往`18`和`19`网段的流量被错误地吸引过来并丢弃。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/IP\346\225\260\346\215\256\346\212\245\347\232\204\344\270\244\345\274\240\350\241\250\357\274\232\350\267\257\347\224\261\350\241\250&ARP\350\241\250.md" "b/src/site/notes/notes/408/IP\346\225\260\346\215\256\346\212\245\347\232\204\344\270\244\345\274\240\350\241\250\357\274\232\350\267\257\347\224\261\350\241\250&ARP\350\241\250.md" deleted file mode 100644 index 56a3340..0000000 --- "a/src/site/notes/notes/408/IP\346\225\260\346\215\256\346\212\245\347\232\204\344\270\244\345\274\240\350\241\250\357\274\232\350\267\257\347\224\261\350\241\250&ARP\350\241\250.md" +++ /dev/null @@ -1,103 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/IP数据报的两张表:路由表&ARP表","permalink":"/408/IP数据报的两张表:路由表&ARP表/"} ---- - - -### 一、 路由表 (Routing Table) - -路由表是网络层进行路径选择的**核心依据**,它本质上是一份“网络交通地图”,告诉路由器去往不同目标网络应该走哪条路。 - -- **作用位置**:主要存在于**路由器**中,主机中也有一张简化的路由表(通常只包含本地网络和默认网关)。 - -- **核心作用**:当路由器收到一个IP数据包时,它会提取出数据包的**目的IP地址**,并在此表中查找匹配的条目,以决定将该数据包从哪个**物理接口**发送给**哪一个下一跳(Next Hop)**设备。 - -- **表的结构与展示**: - - -|目的网络地址 (Destination Network)|子网掩码 (Netmask)|下一跳 (Next Hop)|转发接口 (Interface)|度量值 (Metric)| -|---|---|---|---|---| -|`192.168.1.0`|`255.255.255.0`|`On-link` / `0.0.0.0`|`Eth0`|1| -|`172.16.0.0`|`255.255.0.0`|`10.0.0.2`|`Eth1`|20| -|`0.0.0.0`|`0.0.0.0`|`202.118.1.1`|`WAN0`|10| - -**【字段解析】** - -1. **目的网络地址 (Destination Network)**:标识目标主机所在的网段地址。 - -2. **子网掩码 (Netmask)**:与目的IP地址进行“与”运算,以判断该IP是否属于此目的网络。这是进行**最长前缀匹配**的关键。 - -3. **下一跳 (Next Hop)**:指示数据包应该发往的下一个路由器的IP地址。 - - - 如果目标网络与路由器**直接相连**(如上表第一行),此项通常为`On-link`或全零,表示可以直接通过接口发送到目标主机。 - - - 如果目标网络是**远程网络**,此项则为通往该网络的下一个路由器的IP地址。 - -4. **转发接口 (Interface)**:路由器用于发送数据包的物理端口(如 `Eth0`、`WAN0` 等)。 - -5. **度量值 (Metric)**:(可选) 一个表示路径“成本”的数值,由路由协议(如RIP的跳数、OSPF的Cost)计算得出。当存在多条去往同一目的地的路径时,路由器会优先选择度量值最低的路径。 - - -**特别注意**:最后一行 `0.0.0.0/0` 是**默认路由(Default Route)**。当在路由表中找不到任何与目的IP匹配的条目时,数据包将按照默认路由的指示进行转发,这通常指向互联网出口。 - ---- - -### 二、 ARP缓存表 (ARP Cache) - -如果说路由表是宏观的“跨省地图”,那么ARP表就是微观的“同城街道门牌号手册”。它的存在是为了解决一个根本问题:**IP地址和MAC地址的转换**。 - -- **作用位置**:存在于所有启用IP协议的**主机**和**路由器**中。 - -- **核心作用**:在**同一个局域网(广播域)**内,当一台设备(主机或路由器)知道目标设备的IP地址,但要实际发送数据(封装成以太网帧)时,它必须知道对方的**MAC地址**。ARP表就存储了这种 **IP地址到MAC地址的映射关系**。 - -- **表的结构与展示**: - - -|IP地址 (Internet Address)|MAC地址 (Physical Address)|类型 (Type)| -|---|---|---| -|`192.168.1.1`|`00-aa-00-62-c6-09`|`动态 (dynamic)`| -|`192.168.1.101`|`00-bb-00-73-d8-12`|`动态 (dynamic)`| -|`192.168.1.254`|`00-cc-00-84-e9-23`|`静态 (static)`| - -**【字段解析】** - -1. **IP地址 (Internet Address)**:局域网内某台设备的IP地址。 - -2. **MAC地址 (Physical Address)**:与该IP地址对应的设备的物理网卡地址。 - -3. **类型 (Type)**: - - - **动态 (Dynamic)**:通过**ARP(地址解析协议)**广播请求,并从对方的ARP应答中自动学习并缓存的条目。动态条目有一个**生存时间(TTL)**,超时后会老化并被删除。 - - - **静态 (Static)**:由网络管理员手动配置的永久性条目,不会老化。 - - ---- - -### 三、 两张表的协同工作流程 - -这两张表在数据转发过程中紧密配合,缺一不可。我们以“主机A(`192.168.1.10`)向远程的主机B(`172.16.10.20`)发送数据”为例,看看路由器R1(网关`192.168.1.1`)是如何工作的: - -1. **数据包到达路由器**:主机A将IP包封装在以太网帧中,目的MAC是其网关R1的MAC地址,发送给R1。 - -2. **查找路由表**:R1接收到帧,解封装后看到IP包的目的地址是`172.16.10.20`。它立即查询自己的**路由表**。 - -3. **匹配路由条目**:R1在路由表中进行最长前缀匹配,找到了 `172.16.0.0/16` 这一条目。 - - - **获得转发信息**:路由表告诉R1:“你应该把这个包发给下一跳路由器,它的IP地址是`10.0.0.2`,并且要从你的`Eth1`接口发出去。” - -4. **查找ARP缓存表**:现在,R1知道了下一跳的IP地址(`10.0.0.2`),但要封装成帧发送出去,它还需要`10.0.0.2`这个IP对应的**MAC地址**。于是,R1开始查询自己的**ARP缓存表**。 - -5. **获取下一跳MAC地址**: - - - **情况A:ARP表中有记录**。R1直接从表中查到`10.0.0.2`对应的MAC地址。 - - - **情况B:ARP表中无记录**。R1会通过`Eth1`接口发送一个ARP广播请求:“谁是`10.0.0.2`?请把你的MAC地址告诉我。” 目标路由器`10.0.0.2`收到后会单播回复其MAC地址。R1收到后,将此映射关系存入ARP缓存表。 - -6. **封装并转发**:R1获取到下一跳的MAC地址后,将原始IP包重新封装成一个新的以太网帧,其中: - - - 源MAC地址是R1的`Eth1`接口的MAC地址。 - - - 目的MAC地址是下一跳路由器`10.0.0.2`的MAC地址。 - - - 最后,将这个新帧从`Eth1`接口发送出去。 - \ No newline at end of file diff --git "a/src/site/notes/notes/408/OSPF&BGP\357\274\232OpenFlow\346\210\221\344\273\254\344\274\232\346\255\273\345\220\227\360\237\230\255.md" "b/src/site/notes/notes/408/OSPF&BGP\357\274\232OpenFlow\346\210\221\344\273\254\344\274\232\346\255\273\345\220\227\360\237\230\255.md" deleted file mode 100644 index 21147bc..0000000 --- "a/src/site/notes/notes/408/OSPF&BGP\357\274\232OpenFlow\346\210\221\344\273\254\344\274\232\346\255\273\345\220\227\360\237\230\255.md" +++ /dev/null @@ -1,94 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/OSPF&BGP:OpenFlow我们会死吗😭","permalink":"/408/OSPF&BGP:OpenFlow我们会死吗😭/"} ---- - - -简单来说,答案是:**不完全是取代关系,而是一种根本性的架构变革。OpenFlow将传统路由协议的“决策”功能从路由器本身抽离,集中到了一个“大脑”中,从而改变了网络的控制方式。** - -### 一、 传统路由协议:分布式的“割据” - -在传统网络中,每一台路由器都是一个独立的“诸侯”,它既有负责打仗的“军队”(**数据平面**,负责根据指令快速转发数据包),也有负责思考战略的“军师”(**控制平面**,负责运行路由协议)。 - -- **工作方式**: - - 1. **分布式智能**:每台路由器独立运行路由协议(如内部的OSPF、外部的BGP)。 - - 2. **邻里交谈**:它们通过与相邻路由器“交谈”(交换路由信息),逐步学习并构建出自己视角下的“网络地图”。 - - 3. **本地决策**:当收到一个数据包时,路由器根据自己本地维护的路由表(这份地图),独立做出下一跳的转发决策。 - -- **特点**: - - - **健壮可靠**:去中心化的设计使得网络非常健壮,单点故障影响有限。 - - - **配置复杂**:网络策略的变更需要在大量设备上逐一进行配置,效率低下且容易出错。 - - - **视野局限**:每台路由器都只有“局部视野”,无法从全局视角进行最优的路径规划和流量调度。 - - -**传统路由协议(如OSPF/BGP)的角色**:它们就是这些“军师”们之间沟通、协商、学习网络地图时所使用的**语言和规则**。 - ---- - -### 二、 OpenFlow与SDN:中央集权的“帝国时代” - -**软件定义网络(Software-Defined Networking, SDN)** 是一种全新的网络架构思想,它提出要将“军师”和“军队”彻底分家。 - -- **核心思想**:**控制平面与数据平面分离**。 - - 1. **数据平面(军队)**:网络中的交换机、路由器被“简化”成纯粹的转发设备(称为**转发元件**),只负责忠实地执行指令。 - - 2. **控制平面(大脑/中央司令部)**:所有“思考”和“决策”的工作,全部集中到一个基于软件的**SDN控制器**上。 - -- OpenFlow的角色: - - OpenFlow协议,就是这个“中央司令部”向“军队”下达命令时所使用的标准通信协议。它是一种南向接口(Southbound Interface)。 - - - 控制器通过OpenFlow协议,向网络中的交换机下发**流表(Flow Table)**。 - - - 流表精确地定义了“符合什么样特征(如源IP、目的端口等)的数据包,应该执行何种操作(如转发到端口3、丢弃、修改头部等)”。 - - - 交换机收到数据包后,不再自行思考,而是机械地查询流表并执行相应操作。 - - -**所以,OpenFlow本身不是路由协议,它是实现SDN集中控制的一种工具/语言。** - ---- - -### 三、 OpenFlow与路由协议的真实关系 - -#### 1. 架构上的“革命”而非功能的“替换” - -在纯粹的SDN网络中,路由计算的逻辑被移入到了SDN控制器中。控制器可以基于全局网络拓扑(通过LLDP等协议发现),运行传统的路由算法(如Dijkstra),或者更复杂的自定义算法,来计算出最优路径。然后,它将计算结果转化为具体的流表规则,通过OpenFlow下发到各个交换机。 - -在这种模式下,路由器不再需要运行OSPF等协议,因为路径计算已经由“中央大脑”完成了。从这个角度看,**SDN控制器中的“路由应用”取代了分布式运行的“路由协议”**。OpenFlow则负责将这一决策结果传达下去。 - -#### 2. “竞合”与“共存”的混合模式 - -在现实世界,尤其是大型网络中,纯粹的SDN网络很少见。更多的是SDN网络与传统网络共存的混合模式。在这种模式下,OpenFlow和传统路由协议可以协同工作。 - -- **场景:SDN网络与外部互联网的边界** - - - SDN网络的边界设备(可以是支持OpenFlow的路由器)可以同时运行**BGP协议**。 - - - 它通过BGP与外部的传统路由器“交谈”,学习来自互联网的路由信息。 - - - 然后,它将这些路由信息上报给SDN控制器。 - - - SDN控制器接收到这些外部路由后,结合其掌握的内部网络全局状态(如链路负载、时延等),做出更智能、更精细的流量工程决策,再通过OpenFlow协议调整内部网络的转发路径。 - - -在这种模式下,BGP负责“对外沟通、获取情报”,SDN控制器负责“内部的全局运筹帷幄”,OpenFlow负责“命令的上传下达”。它们不是取代关系,而是**分工协作、优势互补**的关系。 - -### 四、 总结对比 - -|特性|传统路由协议 (OSPF, BGP等)|OpenFlow / SDN| -|---|---|---| -|**控制平面**|**分布式**,位于每一台网络设备上|**集中式**,位于独立的SDN控制器上| -|**角色定位**|定义**设备间**如何交换路由信息,以**构建转发表**的协议|定义**控制器与设备间**如何通信,以**下发流表**的协议| -|**决策依据**|主要基于**目的IP地址**|可基于任意的**流信息**(L2/L3/L4多层信息)| -|**网络视野**|**局部视野**,设备只了解其邻居和学习到的路径|**全局视野**,控制器俯瞰整个网络拓扑和状态| -|**灵活性**|较低,策略变更需逐台配置|**极高**,可通过软件编程实现任意复杂的网络逻辑| -|**关系**|**不是简单的取代关系**。SDN的路由应用在功能上取代了路由协议,而在混合网络中,两者可以**共存与协作**。|| - -**结论**:OpenFlow的出现,并非为了在功能上“一对一”地替换掉OSPF或BGP,而是伴随SDN架构,从根本上改变了网络的控制范式。它将路由的智能从分散的个体手中收归中央,实现了全局的视野和灵活的编程能力,从而开启了网络自动化和智能化的新时代。在可预见的未来,两者长期共存、协同工作的混合模式将是主流。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/PCB\345\275\223\344\270\255\345\255\230\345\202\250\344\272\206\344\273\200\344\271\210.md" "b/src/site/notes/notes/408/PCB\345\275\223\344\270\255\345\255\230\345\202\250\344\272\206\344\273\200\344\271\210.md" deleted file mode 100644 index 0a96d89..0000000 --- "a/src/site/notes/notes/408/PCB\345\275\223\344\270\255\345\255\230\345\202\250\344\272\206\344\273\200\344\271\210.md" +++ /dev/null @@ -1,170 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/PCB当中存储了什么","permalink":"/408/PCB当中存储了什么/"} ---- - - -## PCB 中都存放了什么?(详细讲解) -**进程控制块(Process Control Block, PCB)** 是操作系统为每个进程维护的一个数据结构,它包含了操作系统管理和调度该进程所需的所有信息。PCB 是进程存在的唯一标志,进程的绝大部分信息都存放在 PCB 中。 - -PCB 中主要包含以下几类信息: - -### 1. 进程标识符(Process Identification Information) -用于唯一地标识一个进程。 - -- **进程 ID(PID)**:系统分配给每个进程的唯一数字标识符。 - -- **父进程 ID(PPID)**:创建当前进程的父进程的 ID。 - -- **用户 ID(UID)**:拥有该进程的用户 ID。 - -- **组 ID(GID)**:拥有该进程的用户所属的组 ID。 - -- **子进程列表**:指向其所有子进程 PCB 的指针或 ID 列表(可选,有时也通过其他方式维护)。 - - -### 2. 进程状态信息(Process State Information) -记录进程当前的运行状态,用于调度和状态转换。 - -- **进程状态**:如就绪(Ready)、运行(Running)、阻塞(Blocked/Waiting)、新建(New)、终止(Terminated)等。这是最基本的进程状态。 - - -### 3. CPU 现场信息(CPU State Information / Context Information) -这部分信息是进程进行上下文切换时必须保存和恢复的,以确保进程能够从上次中断的地方继续执行。 - -- **程序计数器(Program Counter, PC)**:指向下一条要执行指令的地址。 - -- **寄存器集合**: - - - **通用寄存器**:如 AX, BX, CX, DX 等(x86 架构),用于存放数据和地址。 - - - **状态寄存器(PSW/FLAGS)**:存放条件码、模式位等,反映CPU当前状态。 - - - **栈指针(Stack Pointer, SP)**:指向当前进程的用户栈顶。 - - - **段寄存器**:如 CS, DS, SS, ES(在分段内存管理中),存放段的基址。 - - - **内存管理相关寄存器**:这非常关键。 - - - **页表基址寄存器(Page Table Base Register, PTBR)**:存放该进程**页表的物理起始地址**。在 x86 架构中通常是 **CR3 寄存器**。当进程调度时,操作系统会将该值加载到 CPU 相应的寄存器中,使 MMU 能够正确进行地址转换。 - - - **段表基址寄存器**:在分段管理中,存放段表的物理起始地址。 - - - **(可选)限定寄存器/界限寄存器**:用于分段管理或基址寄存器/界限寄存器组合方式的内存保护。 - -- **其他控制信息**:如中断屏蔽字(Interrupt Mask Word)等。 - - -### 4. 进程调度信息(Process Scheduling Information) -调度程序根据这些信息来选择下一个要执行的进程。 - -- **进程优先级**:用于决定调度顺序。 - -- **调度参数**:如时间片大小、已运行时间、等待时间等。 - -- **事件信息**:如果进程处于阻塞状态,需要记录它正在等待什么事件发生(例如,等待 I/O 完成、等待信号量)。 - -- **队列指针**:指向调度队列(如就绪队列、各种等待队列)中的下一个 PCB,将 PCB 连接成链表。 - - -### 5. 内存管理信息(Memory Management Information) -描述进程的地址空间情况。 - -- **页表或段表指针**:指向该进程的页表或段表的地址。这是虚拟地址到物理地址映射的关键。 - -- **内存分配情况**:已分配的内存块大小和起始地址(对于采用段式管理或混合管理的文件系统)。 - -- **边界寄存器信息**:定义进程代码、数据、栈等段的起始地址和大小。 - - -### 6. 文件管理信息(File Management Information / I/O Status Information) -记录进程打开的文件和 I/O 设备的使用情况。 - -- **已打开文件列表(或文件描述符表)**:一个数组或链表,记录了该进程当前所有打开文件的信息。每个条目通常是一个**指向系统级打开文件表中相应条目的指针或索引**。 - -- **I/O 状态信息**:记录进程未完成的 I/O 操作、已分配的 I/O 设备等。 - - -### 7. 记账信息(Accounting Information) -用于统计进程资源使用情况。 - -- **CPU 使用时间**:进程已占用 CPU 的总时间。 - -- **时间限制**:进程允许运行的最长时间。 - -- **各种资源使用量**:如打印页数、磁盘用量等。 - - ---- - - - -## 示例图示(PCB 逻辑结构) -``` -+------------------------------------+ -| 进程控制块 (PCB) | -+------------------------------------+ -| | -| 1. 进程标识符信息 | -| - 进程ID (PID) | -| - 父进程ID (PPID) | -| - 用户ID (UID) | -| - 组ID (GID) | -| - ... | -| | -| 2. 进程状态信息 | -| - 进程状态 (就绪/运行/阻塞等) | -| | -| 3. CPU 现场信息 (CPU Context) | -| - 程序计数器 (PC) | -| - 各种通用寄存器 (AX, BX, ...) | -| - 状态寄存器 (PSW/FLAGS) | -| - 栈指针 (SP) | -| - **页表基址寄存器 (PTBR/CR3)** | -| - (分段管理中的段寄存器等) | -| - ... | -| | -| 4. 进程调度信息 | -| - 进程优先级 | -| - 调度参数 (时间片、已运行时间) | -| - 事件信息 (等待的事件) | -| - 队列指针 (指向下一PCB) | -| | -| 5. 内存管理信息 | -| - **页表/段表指针 (基地址)** | -| - 内存段的界限/大小 | -| - ... | -| | -| 6. 文件管理/I/O 状态信息 | -| - 已打开文件列表 (FD 表) | -| - I/O 设备分配情况 | -| - ... | -| | -| 7. 记账信息 | -| - CPU 使用时间 | -| - 各种资源使用量 | -| - ... | -| | -+------------------------------------+ -``` - ---- - - -1. **核心概念**:PCB 是进程的唯一标志,理解其重要性。 - -2. **内容分类**:常考 PCB 中包含哪些类型的信息,例如“下列哪项信息不属于 PCB 内容?”或“PCB 中最能体现进程动态性的是?”(答案通常是进程状态或 CPU 现场信息)。 - -3. **进程切换(上下文切换)**:当进程切换时,需要保存和恢复哪些信息?这是必考点。答案就是 PCB 中的 **CPU 现场信息**(特别是 PC、寄存器和页表基址等)。 - -4. **PCB 的位置**:通常位于内核空间,受到操作系统的保护,用户进程无法直接访问。 - -5. **与进程、程序的关系**: - - - **程序**:是静态的指令集合,可以理解为代码本身。 - - - **进程**:是程序的一次执行过程,是动态的,具有生命周期。PCB 是进程的实体。 - - - 一个程序可以对应多个进程(多次运行)。 - - - 一个进程只对应一个 PCB。 - diff --git "a/src/site/notes/notes/408/ROM\351\205\261\347\232\204\350\264\236\346\223\215\351\224\201\357\274\232CPU\347\232\204\345\206\231\346\214\207\344\273\244\346\230\257\345\246\202\344\275\225\350\242\253\346\227\240\346\203\205\346\213\222\347\273\235\347\232\204\360\237\233\241\357\270\217.md" "b/src/site/notes/notes/408/ROM\351\205\261\347\232\204\350\264\236\346\223\215\351\224\201\357\274\232CPU\347\232\204\345\206\231\346\214\207\344\273\244\346\230\257\345\246\202\344\275\225\350\242\253\346\227\240\346\203\205\346\213\222\347\273\235\347\232\204\360\237\233\241\357\270\217.md" deleted file mode 100644 index 1ed9a8d..0000000 --- "a/src/site/notes/notes/408/ROM\351\205\261\347\232\204\350\264\236\346\223\215\351\224\201\357\274\232CPU\347\232\204\345\206\231\346\214\207\344\273\244\346\230\257\345\246\202\344\275\225\350\242\253\346\227\240\346\203\205\346\213\222\347\273\235\347\232\204\360\237\233\241\357\270\217.md" +++ /dev/null @@ -1,106 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/ROM酱贞操锁:CPU的写指令是如何被无情拒绝的🛡️","permalink":"/408/ROM酱贞操锁:CPU的写指令是如何被无情拒绝的🛡️/"} ---- - - - -**RAM和ROM确实统一编址,但写保护并非由指令决定,而是由“硬件物理特性”和“操作系统逻辑权限”这两把锁共同实现的;当有指令试图写入ROM地址时,要么被ROM芯片在物理上直接无视,要么被操作系统的内存管理单元(MMU)提前拦截并产生异常。** - ---- - -### 一、 统一编址:一个地址空间下的“两家人” - -首先,我们来解决“统一编址”的问题。 -- **地址译码器 (Address Decoder)**:这是关键的硬件电路,相当于小区的“物业管理员”。当CPU在地址总线上发出一个地址时(比如`0x0000_1234`),地址译码器会根据这个地址的高位部分,判断它属于哪个区域。 - - - 例如,假设系统设计`0x0000_0000`到`0x0007_FFFF`是ROM的地址范围,`0x0008_0000`到`0xFFFF_FFFF`是RAM的范围。 - - - 当地址落在前一个范围时,译码器会发出一个**片选信号(Chip Select, CS)**去“激活”ROM芯片。 - - - 当地址落在后一个范围时,它就去激活对应的RAM芯片。 - - -这样,尽管RAM和ROM是不同的物理器件,但在CPU看来,它们共同组成了一个连续、无缝的地址空间,这就是“统一编址”。 - -代码段 - -``` - +-------+ 地址总线 (例如: 0x0001000) - | CPU |---------------------------------+ - +-------+ | - | ^ V - 控制总线 | | 数据总线 +-------------------+ - (R/W#) | | | 地址译码器 | - V | +-------------------+ - +-------+ CS_ROM=1 (激活) | | CS_RAM=0 (不理) - | ROM |<----------------------------+ | - +-------+ | - +-------+ - | RAM |<--+ - +-------+ -``` - ---- - -### 二、 访问权限:硬件与软件的双重保险 - -如果CPU想改写一个属于ROM的地址,会发生什么?这里有两道防火墙。 - -#### 第一道防火墙:ROM芯片的物理特性(硬件层 - 硬道理) - -这是最底层、最根本的保护。 - -- **CPU的写操作**:当CPU执行一条写指令(如 `STORE R1, 0x1234`)时,它会做三件事: - - 1. 在**地址总线**上放出地址 `0x1234`。 - - 2. 在**数据总线**上放出要写入的数据。 - - 3. 在**控制总线**上发出一个**写信号**(例如,将`R/W#`信号线置为低电平,表示“写”)。 - -- **ROM的反应**: - - 1. 地址译码器看到地址 `0x1234`,激活了ROM芯片(`CS_ROM=1`)。 - - 2. ROM芯片接收到了写信号(`R/W#`为低电平)。 - - 3. **关键来了**:ROM(Read-Only Memory)的内部电路是被物理“写死”的(通过熔丝或掩膜技术)。它的设计**根本就没有响应“写信号”的功能**。无论控制总线上是什么信号,它的存储内容都不会、也不可能改变。 - - 4. **结果**:写指令“执行”了,CPU的时钟周期照常走完,但对于ROM芯片来说,这个写信号就像耳边风一样,它完全无动于衷。数据总线上的数据被白白晾在那里,最终消失。**写操作在物理层面静默地失败了(Silently Fails)**。 - - -#### 第二道防火墙:操作系统的内存保护(软件层 - 软约束) - -在有操作系统的现代计算机中,还有一层更智能、更主动的保护。 - -- **内存管理单元 (MMU)**:这是CPU内部的一个硬件模块,它与操作系统协同工作,负责将程序使用的**虚拟地址**翻译成**物理地址**。 - -- **页表 (Page Table)**:操作系统为每个进程维护一个页表,记录了虚拟页面到物理页面的映射关系。关键在于,页表的每一项(Page Table Entry, PTE)中都包含了**访问权限位(Permission Bits)**,如:`可读(R)`、`可写(W)`、`可执行(X)`。 - -- **操作系统的工作**:在系统启动时,操作系统会把存放固件(如BIOS)的ROM区域映射到自己的内核空间,并在对应的页表项中,将该区域的权限明确设置为**只读(W=0)**。 - -- **试图写入ROM时的过程(在有OS的机器上)**: - - 1. 一个程序(通常在用户态)试图执行一条写指令,写入一个指向ROM的虚拟地址。 - - 2. CPU将这个虚拟地址交给MMU进行翻译。 - - 3. MMU在页表中找到对应的PTE,检查其权限位。 - - 4. MMU发现该页面的**`W`位是0**,表示“不可写”。 - - 5. **关键来了**:MMU不会继续把地址发往总线,而是立即**中止**这个操作,并向CPU核心发出一个**内部中断/异常(Exception/Trap)**,这个异常通常被称为**“页错误(Page Fault)”**或**“保护错误(Protection Fault)**。 - - 6. CPU捕获到这个异常后,会立即从用户态切换到**内核态**,并跳转到操作系统预设好的异常处理程序。 - - 7. **结果**:操作系统接管控制权,它发现是一个非法的写操作,通常会向该程序发送一个信号(如Linux下的`SIGSEGV`),导致程序因**“段错误(Segmentation Fault)”**而被强制终止。 - - ---- - -### 三、 总结与考点分析 - -|保护层次|实现者|保护机制|发生时机|结果| -|---|---|---|---|---| -|**硬件物理保护**|ROM芯片自身|物理电路无写入功能|写信号到达芯片时|写操作静默失败,数据未改变| -|**OS逻辑保护**|操作系统 + MMU|页表权限位检查|MMU地址翻译时|产生硬件异常,程序被OS终止| diff --git "a/src/site/notes/notes/408/STDM\347\232\204\345\274\202\346\255\245\344\275\240\347\234\237\347\232\204\347\237\245\351\201\223\346\230\257\344\273\200\344\271\210\347\232\204\345\274\202\346\255\245\345\220\227\360\237\244\224.md" "b/src/site/notes/notes/408/STDM\347\232\204\345\274\202\346\255\245\344\275\240\347\234\237\347\232\204\347\237\245\351\201\223\346\230\257\344\273\200\344\271\210\347\232\204\345\274\202\346\255\245\345\220\227\360\237\244\224.md" deleted file mode 100644 index c62e270..0000000 --- "a/src/site/notes/notes/408/STDM\347\232\204\345\274\202\346\255\245\344\275\240\347\234\237\347\232\204\347\237\245\351\201\223\346\230\257\344\273\200\344\271\210\347\232\204\345\274\202\346\255\245\345\220\227\360\237\244\224.md" +++ /dev/null @@ -1,101 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/STDM的异步你真的知道是什么的异步吗🤔","permalink":"/408/STDM的异步你真的知道是什么的异步吗🤔/"} ---- - - -### 一、原理 - -要彻底理解STDM,最好的方法就是将它与我们熟悉的(同步)时分复用(TDM)**进行对比。 - -#### 1. 回顾:传统的TDM(同步时分复用) - -想象一个有4条车道(用户A, B, C, D)的收费站,要汇入一条主干道。TDM的规则是: - -- **严格轮流**:主干道的时间被切分成一个个“帧”,每个帧里有4个固定的“时隙”,分别**永久预留**给A, B, C, D。 - -- **不管你来不来,位置都给你留着**:即使某一时刻C车道没有车,C的时隙也会在主干道上**空着跑过去**。 - -- **结果**:信道利用率低,存在大量浪费。 - - -``` -TDM 帧结构: -| A1 | B1 | C1 | D1 | A2 | B2 | C2 | D2 | ... -^ ^ ^ ^ -时隙1 时隙2 时隙3 时隙4 (固定分配) - -如果C没有数据,则C1时隙为空,造成浪费。 -``` - -#### 2. 登场:STDM(统计时分复用) - -STDM则像一个**智能的动态调度系统**,它的规则是: - -- **按需服务,先到先得**:不再为每个用户预留固定时隙。而是谁有数据要发,就立刻把它装入下一个可用的时隙。 - -- **插队抢跑,提高效率**:如果C没有数据,系统会直接跳过C,让后面有数据的用户(比如D或下一个A)立刻“插队”使用这个时隙。 - -- **结果**:信道几乎永远是“满载”的,利用率极高。 - - -#### 3. STDM是如何实现的? - -STDM的实现依赖于一个核心设备——**统计复用器(Statistical Multiplexer)**,其实现过程如下: - -1. **设置缓冲区**:统计复用器为每条输入线路都提供一个缓冲区,用于暂存用户发来的数据。 - -2. **动态扫描**:复用器不断地循环扫描各条输入线路的缓冲区。 - -3. **按需组帧**: - - - 一旦发现某个用户的缓冲区中有数据,就立即取出这些数据。 - - - **关键一步**:为取出的数据块附加上一个**地址信息**(或称为“标志”),指明这个数据块是属于哪个用户的。 - - - 将“地址+数据”作为一个整体,放入一个STDM帧中。 - -4. **发送帧**:将组装好的STDM帧发送到高速线路上。 - - -为什么必须加地址信息? - -因为在STDM中,时隙的位置不再与用户身份绑定。接收端收到一个数据块时,它无法像TDM那样通过“数位置”(比如第3个时隙就是C的)来判断数据来源。因此,必须在数据中明确地“贴上标签”,告诉接收端“我是A发来的”、“我是D发来的”。 - -### 二、图示说明 - -**STDM工作原理图** - -``` - +-----------+ -A --> | Buffer A | --\ - +-----------+ \ - +-----------+ \ +-------------------+ 高速线路 -B --> | Buffer B | ------->| 统计复用器 |------------> | D1 | C1 | B1 | A1 | ... - +-----------+ / | (扫描、加地址、组帧)| - +-----------+ / +-------------------+ -C --> | Buffer C | --/ - +-----------+ - +-----------+ -D --> | Buffer D | --/ - +-----------+ - -假设某一时刻,A, B, C, D都有数据,则STDM帧可能是 | A1 | B1 | C1 | D1 | -假设下一时刻,只有A和D有数据,则STDM帧可能是 | A2 | D2 | A3 | D3 | (B和C的时隙被动态利用了) -``` - -- **注意**:这里的`A1`, `B1`等都代表一个包含**地址和数据**的完整数据单元。 - -|特性|(同步)TDM|统计时分复用 (STDM)| -|---|---|---| -|**分配方式**|**静态分配**,预留固定时隙|**动态分配**,按需分配时隙| -|**信道利用率**|低,有数据空闲时浪费严重|**高**,信道利用率得到极大提升| -|**帧结构**|时隙位置隐含地址,无额外开销|**必须包含地址字段**,有额外开销| -|**总速率关系**|输出线路速率 ≥ 所有输入线路速率之和|输出线路速率可以 < 所有输入线路速率之和| -|**是否拥塞**|永远不会拥塞|当瞬时输入速率 > 输出速率时,**可能因缓冲区满而拥塞丢包**| -### 三、易错点 - -- **STDM的开销**:STDM的高效率是有代价的,这个代价就是每个数据单元都必须增加地址信息,这本身是一种**开销(Overhead)**。当传输的数据块本身很小时,地址信息的占比就会变大,从而降低了实际的净荷效率。 - -- **拥塞问题**:STDM的设计基于一个“统计”假设,即所有用户不太可能在同一时刻都达到峰值速率。但万一这个小概率事件发生了呢?如果所有用户同时以最大速率发送数据,其总和超过了输出线路的承载能力,统计复用器的缓冲区就会被迅速填满,最终导致**数据丢失(丢包)**。这是STDM为了追求高效率而必须承担的风险。 - -- **名称混淆**:“异步时分复用”这个别名容易让人与物理层的“异步传输”(使用起始/停止位)混淆。一定要分清:STDM的“异步”指的是**时隙分配的非同步性**,而不是比特传输的同步方式。 diff --git "a/src/site/notes/notes/408/close()\344\270\216write()\346\223\215\344\275\234\347\232\204\346\200\235\350\200\203\360\237\244\224.md" "b/src/site/notes/notes/408/close()\344\270\216write()\346\223\215\344\275\234\347\232\204\346\200\235\350\200\203\360\237\244\224.md" deleted file mode 100644 index d5ceb43..0000000 --- "a/src/site/notes/notes/408/close()\344\270\216write()\346\223\215\344\275\234\347\232\204\346\200\235\350\200\203\360\237\244\224.md" +++ /dev/null @@ -1,78 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/close()与write()操作的思考🤔","permalink":"/408/close()与write()操作的思考🤔/"} ---- - - -### 1. 写文件操作 (`write`):高效但“懒惰”的数据搬运工 - -当我们调用`write()`函数向一个文件写入数据时,为了**极大地提高I/O效率**,操作系统通常不会立即把数据写入到慢速的磁盘上。磁盘I/O是一项非常耗时的操作(涉及寻道、旋转等机械动作)。如果每次`write`一两个字节就启动一次磁盘操作,系统性能将不堪设想。 - -因此,`write()`的典型流程是: - -1. **数据从用户空间到内核空间**:程序调用`write()`时,数据从用户程序的缓冲区被复制到操作系统内核开辟的**I/O缓冲区(或称为页高速缓存 Page Cache)**中。 - -2. **快速返回**:一旦数据被复制到内核缓冲区,`write()`系统调用就可以**立即返回**,告诉用户程序“写入成功”。 - -3. **延迟写入 (Delayed Write)**:此时,数据只是存在于内存的内核缓冲区中,并没有被真正写入磁盘。内核会“攒”着这些数据,等到缓冲区满了、或者系统空闲时、或者定时任务触发时,再把整个缓冲区的数据一次性地、以一个或多个磁盘块为单位,高效地写入磁盘。 - - -**`write()`操作的核心特征**: - -- **异步性**: 对用户程序来说,`write()`的返回并不意味着数据已经安全地落在了磁盘上。 - -- **效率优先**: 它的主要目的是快速地将数据从用户进程“接管”过来,然后通过缓冲和批量写入的机制来优化性能。 - -- **不保证最终一致性**: 如果在`write()`调用后、数据被刷盘前,系统突然断电,那么这部分写入的数据将会**丢失**。 - - ---- - -### 2. 关闭文件操作 (`close`):确保善始善终的“收尾总管” - -`close()`操作标志着一个进程对该文件访问的结束。因此,它必须承担起“善后”的全部责任,确保文件在磁盘上的状态是完整和一致的。 - -`close()`的典型流程是一个严谨的三部曲: - -1. **第一步(最重要):刷新内核数据缓冲区 (Flush Dirty Buffers)** - - - 操作系统会检查与该文件相关联的内核I/O缓冲区中,是否存在“脏数据”(即被修改过但尚未写入磁盘的数据)。 - - - 如果存在,操作系统会**强制启动磁盘I/O**,将所有这些脏数据块**全部写回**到磁盘上。这个过程是**同步**的,`close()`会等待这个写操作完成后再继续。 - - - **这步操作保证了所有之前`write()`的数据被永久化存储。** - -2. **第二步:写回文件的控制信息(元数据 Metadata)** - - - 在确保了文件**内容**的完整性之后,操作系统会更新并写回文件的**“属性”**。这些控制信息存储在磁盘上的**文件控制块(FCB)**或**i-node**中。 - - - 需要更新的典型信息包括: - - - **文件大小**: 经过多次写入,文件的尺寸可能已经改变。 - - - **最后修改时间**: 记录文件内容最后一次被改变的时间戳。 - - - **磁盘块指针**: 如果文件变大,可能需要分配新的数据块,这些新块的地址需要记录到i-node的指针列表中。 - - - **为什么在此时写回?** 因为只有在所有数据都写入完毕后,文件的最终大小和状态才被完全确定。在每次`write`后都更新元数据会带来巨大的、不必要的I/O开销。 - -3. **第三步:释放内存中的控制结构** - - - 在确保磁盘上的数据和元数据都一致后,操作系统会释放为这次文件打开而分配的内存资源。 - - - **在进程的“打开文件表”中,删除对应的表项**,从而释放该进程的文件描述符(fd)。 - - - **在系统的“打开文件表”中,将对应表项的引用计数减1**。如果引用计数变为0(表示系统中已没有任何进程打开该文件),则释放这个系统级的表项。 - - ---- - -### **总结与辨析表格** - -|特性|写文件操作 (`write`)|关闭文件操作 (`close`)| -|---|---|---| -|**核心任务**|将数据从用户空间**传递**到内核缓冲区|**最终化**文件在磁盘上的状态,并**释放**内存资源| -|**数据同步性**|**通常是异步的**。调用返回不代表数据已落盘|**同步的**。会强制将缓冲数据刷到磁盘| -|**操作对象**|主要操作对象是**文件内容数据**|操作对象包括**数据**、**元数据**和**内核控制结构**| -|**对控制信息**|可能只更新内存中的副本(如当前文件指针)|**将最终的控制信息(大小、时间等)写回磁盘**| -|**I/O时机**|不一定会立即触发磁盘I/O|**会触发**必要的磁盘I/O以确保数据一致性| -|**资源管理**|占用和使用内核资源(如文件表项、缓冲区)|**释放**内核为此次文件打开所占用的资源| diff --git a/src/site/notes/notes/408/demo1111.md b/src/site/notes/notes/408/demo1111.md deleted file mode 100644 index 6948300..0000000 --- a/src/site/notes/notes/408/demo1111.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/demo","permalink":"/408/demo/"} ---- - -11111 \ No newline at end of file diff --git "a/src/site/notes/notes/408/read()\346\223\215\344\275\234\347\232\204\345\267\245\344\275\234\346\265\201\347\250\213.md" "b/src/site/notes/notes/408/read()\346\223\215\344\275\234\347\232\204\345\267\245\344\275\234\346\265\201\347\250\213.md" deleted file mode 100644 index 9190d2f..0000000 --- "a/src/site/notes/notes/408/read()\346\223\215\344\275\234\347\232\204\345\267\245\344\275\234\346\265\201\347\250\213.md" +++ /dev/null @@ -1,85 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/read()操作的工作流程","permalink":"/408/read()操作的工作流程/"} ---- - - -### **读文件操作 (`read`) 的完整工作流程** - -假设一个用户程序已经通过`open()`函数成功打开了一个文件,并获得了对应的文件描述符 `fd`。现在,程序调用 `read(fd, buf, n)`,意图从文件中读取 `n` 个字节到用户指定的缓冲区 `buf` 中。 - -#### **第一步:用户程序发起调用** - -- 用户进程在**用户态**下,调用C库函数 `read()`。这个库函数会打包好参数(文件描述符`fd`、目标缓冲区地址`buf`、要读取的字节数`n`),准备请求操作系统服务。 - - -#### **第二步:系统调用与上下文切换** - -- 库函数通过一条特殊的**陷入指令 (trap)**,将CPU的执行状态从**用户态**切换到**内核态**。 - -- 操作系统的**系统调用处理程序**接管控制权,根据传入的系统调用号,找到并开始执行内核中对应的 `sys_read()` 函数。 - - -#### **第三步:内核中定位文件和读取位置** - -- 内核首先需要知道“读哪里”。它会执行以下查找: - - 1. 使用 `fd` 作为索引,在当前进程的**“进程级打开文件表”**中找到对应的表项。 - - 2. 通过该表项中的指针,找到在**“系统级打开文件表”**中的对应表项。 - - 3. 这个系统级表项非常关键,它包含了文件的**当前读写位置(文件偏移量 a.k.a. file offset or pointer)**。同时,它还包含一个指向该文件在内存中的**i-node(或v-node)**的指针。 - - -#### **第四步:检查内核I/O缓冲区(页高速缓存)—— 关键性能点** - -- 这是整个流程的核心决策点。操作系统**不会**立即去访问磁盘。 - -- 操作系统会根据上一步获取的**“文件偏移量”**和要读取的字节数 `n`,计算出需要读取的数据位于文件的哪一个或哪几个**逻辑块 (logical block)** 中。 - -- 然后,它会去检查内核的 **I/O缓冲区(也称页高速缓存,Page Cache)**,看看这些逻辑块是否因为之前的读写操作而**已经存在于内存中**。 - - - **情况A:缓存命中 (Cache Hit)** - - - 如果所有需要的数据块都已经在内核缓冲区中,这是**最理想**的情况。 - - - 系统将**直接跳过第五步**(物理I/O),避免了与慢速磁盘的任何交互。 - - - **情况B:缓存未命中 (Cache Miss)** - - - 如果需要的数据块(或其中一部分)不在缓冲区中,那么操作系统**必须**启动一次物理I/O操作,从磁盘上将数据加载进来。 - - -#### **第五步:启动物理I/O(仅在缓存未命中时发生)** - -- **地址转换**: 操作系统通过查询内存中的文件i-node,将文件的**逻辑块号**转换为实际的**物理磁盘块地址**。 - -- **发出I/O请求**: 内核向**磁盘驱动程序**发出一个读请求,指明要从哪个磁盘的哪个物理地址开始,读取多少个数据块。 - -- **DMA传输**: 现代操作系统普遍使用**DMA (Direct Memory Access)** 方式进行数据传输。 - - 1. CPU向DMA控制器下达指令,告诉它“把磁盘的X地址的数据,传送到内存的Y地址(即内核I/O缓冲区的地址)”。 - - 2. 之后,CPU就可以**被释放**去执行其他进程,无需再关心这个耗时的数据传输过程。 - - 3. DMA控制器会全权负责将数据从磁盘搬运到内存的内核缓冲区。 - -- **I/O完成中断**: 当DMA传输完成后,磁盘控制器会向CPU发送一个**中断信号**,通知操作系统“数据已经准备好了”。 - - -#### **第六步:数据从内核缓冲区复制到用户缓冲区** - -- 无论是缓存命中直接得到数据,还是缓存未命中后通过DMA从磁盘加载了数据,此时,要读取的数据都**已经位于内核的I/O缓冲区中**。 - -- 内核将从内核缓冲区中,根据用户请求的字节数 `n`,将数据**复制**到用户程序在第一步调用时指定的 `buf` 缓冲区中。 - - -#### **第七步:更新状态并返回** - -- **更新文件偏移量**: 内核会更新系统级打开文件表中的**文件偏移量**,将其增加实际读取到的字节数,以便下一次`read`调用能从正确的位置开始。 - -- **更新访问时间**: 可能会更新i-node中的“最后访问时间”。 - -- **返回用户态**: 内核完成所有工作后,执行**上下文切换**,将CPU控制权交还给用户进程,并从内核态切换回用户态。 - -- `read()`函数返回,其返回值是**实际读取到的字节数**(可能小于请求的`n`,例如读到了文件末尾)。 - diff --git "a/src/site/notes/notes/408/signal\346\223\215\344\275\234\344\270\216V\346\223\215\344\275\234.md" "b/src/site/notes/notes/408/signal\346\223\215\344\275\234\344\270\216V\346\223\215\344\275\234.md" deleted file mode 100644 index dec65d4..0000000 --- "a/src/site/notes/notes/408/signal\346\223\215\344\275\234\344\270\216V\346\223\215\344\275\234.md" +++ /dev/null @@ -1,76 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/signal操作与V操作","permalink":"/408/signal操作与V操作/"} ---- - - -### 核心区别:有无“记忆”功能与状态改变 - -这是两者最根本的区别,也是所有其他差异的根源。 - -- **V 操作 (信号量)**: **具有记忆性**。`V(S)` 操作的本质是 `S.value++`。即使当前没有任何进程在等待该信号量,`V` 操作执行后,信号量 `S` 的值依然会加1。这个增加的值会被“记忆”下来,供后续的 `P` 操作使用。**它改变的是信号量本身的状态(资源计数)**。 - - - **比喻**: `V` 操作就像往一个票箱里放一张票。不管现在有没有人在排队等票,这张票都会被放进去,供下一个来的人取用。 - -- **signal 操作 (管程)**: **不具有记忆性**。`signal(c)` 操作的本质是唤醒一个因条件 `c` 不满足而等待的进程。如果当前**没有**任何进程在条件变量 `c` 的等待队列上等待,那么 `signal` 操作将**不产生任何效果**,如同空操作一样,这个信号会立即被“丢弃”。**它试图改变的是另一个进程的状态(从等待到就绪),而不是条件变量本身的状态**。 - - - **比喻**: `signal` 操作就像在候车室里喊一声:“车来了!” 如果有人在等车,他就会被唤醒准备上车;如果候车室里根本没人等这趟车,你喊的这一声就没有任何作用,声音消散了就没了。 - - ---- - -### 运行环境与耦合度的区别 - -- **V 操作**: 是一个独立的操作,可以在程序的任何地方调用(只要能访问到信号量变量)。它与 `P` 操作松散地耦合,共同作用于一个全局或共享的信号量。 - -- **signal 操作**: 必须在**管程内部**使用。它总是与管程的互斥锁以及特定的条件变量 (`condition variable`) 紧密耦合。一个进程必须首先获得管程的互斥访问权,才能在管程的某个过程中调用 `wait` 或 `signal`。 - - ---- - -### 导致进程状态改变的逻辑区别 - -- **V 操作**: `V(S)` 执行后,`S.value++`。如果 `S.value <= 0`,则说明之前有进程因 `P(S)` 而阻塞,此时 `V` 操作会唤醒一个等待的进程。唤醒的**直接原因**是 `S.value` 的值从负数向0靠近,代表“资源”从无到有。 - -- **signal 操作**: `signal(c)` 的执行逻辑是直接检查条件变量 `c` 的等待队列是否为空。如果不为空,则唤醒一个进程。唤醒的**直接原因**是另一个进程认为“等待的条件可能已经满足了”,并发出一个“通知”。 - - ---- - -### 对当前执行进程的影响区别 - -`V` 操作执行后,当前进程会继续执行,这是毫无疑问的。但 `signal` 操作执行后,当前进程(发信号者)和被唤醒进程(等待者)谁继续在管程内执行,这是一个重要的分歧点,在理论上分为两种模型: - -1. **Hoare 风格 (霍尔风格)**: **立即切换,发信号者让权**。 - - - 当进程 `A` 执行 `signal(c)` 唤醒了等待的进程 `B` 时,`A` 会**立即阻塞**自己,将管程的控制权(互斥锁)直接交给 `B`。 - - - `B` 被唤醒后立刻在管程内继续执行。 - - - **优点**: `B` 被唤醒时,它所等待的条件**一定**是真的。因为它是在 `A` 创造条件后、其他任何进程进入前立即执行的。因此,等待处的代码可以用 `if` 来判断。 - - - **缺点**: 实现复杂,涉及两次额外的进程上下文切换(A->B,之后B退出或等待时又要唤醒A),开销较大。 - -2. **Mesa 风格 (米萨风格)**: **发信号者继续,等待者等待**。 - - - 当进程 `A` 执行 `signal(c)` 唤醒了 `B` 时,`A` **并不会阻塞**,而是继续执行它在管程内的后续代码。 - - - 被唤醒的 `B` 只是从条件等待队列移动到管程的**入口等待队列**,与其他希望进入管程的进程一起排队,等待 `A` 最终释放管程锁。 - - - **缺点**: 当 `B` 最终被调度并重新获得管程锁时,它等待的条件**可能已经再次变为假**(因为在 `A` 发信号和 `B` 恢复执行之间,可能有其他进程进入管程并改变了状态)。因此,等待处的代码必须使用 `while` 循环来重新检查条件。 - - - **优点**: 实现简单,上下文切换开销小。现代编程语言(如 Java 的 `notify()`)大多采用这种风格。 - - -### 总结对比表格 - -| 特性 | V 操作 (信号量) | signal 操作 (管程) | -| ----------- | ------------------------------------ | -------------------------------------------------------------------------------------------- | -| **核心机制** | **有记忆性**,对资源计数器 `S.value` 进行 `++` 操作 | **无记忆性**,仅当有进程等待时才起作用,否则信号丢失 | -| **作用对象** | 直接改变信号量 `S` 的状态值 | 试图改变另一个进程的状态,对条件变量 `c` 本身无状态改变 | -| **运行环境** | 独立,可在任何能访问信号量的地方调用 | 必须在管程内部,与管程互斥锁和条件变量强耦合 | -| **对当前进程影响** | 发信号的进程**总是继续执行** | **不一定**。Hoare风格下发信号者阻塞,Mesa风格下发信号者继续执行 | -| **被唤醒后的状态** | 被唤醒的进程变为就绪态,等待CPU调度 | 被唤醒的进程状态取决于管程风格 (Hoare:立即执行;Mesa:变为就绪态,等待管程锁) | -| **使用范式** | 成对的 `P`/`V` 操作,用于实现复杂的同步互斥逻辑 | `wait`/`signal` 成对出现,用于在管程内部管理"条件不满足则等待"的逻辑 | -| **常见代码模式** | `P(S)` ... `V(S)` | `while(!condition) wait(c);` ... `signal(c);` (Mesa风格) / `if(!condition) wait(c);` (Hoare风格) | - -总而言之,`V` 操作是一个相对“低级”和“原始”的原子操作,它通过直接增减资源计数来协调进程。而 `signal` 是一个“高级”的、封装在管程结构内的通知机制,它将互斥和同步逻辑分离,使得程序结构更清晰,更不易出错。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/\344\270\200\344\270\252\346\225\264\346\225\260\347\232\204\345\245\207\345\271\273\346\274\202\346\265\201\357\274\232\351\200\224\347\273\217\345\217\202\346\225\260\344\270\223\350\275\246`$a0`\357\274\214\344\274\232\346\231\244\350\277\220\347\256\227\346\240\270\345\277\203ALU\357\274\214\346\234\200\345\220\216\346\220\255\344\270\212`$v0`\350\277\224\347\250\213\347\232\204\345\205\250\350\277\207\347\250\213\345\244\247\346\217\255\347\247\230\360\237\230\262.md" "b/src/site/notes/notes/408/\344\270\200\344\270\252\346\225\264\346\225\260\347\232\204\345\245\207\345\271\273\346\274\202\346\265\201\357\274\232\351\200\224\347\273\217\345\217\202\346\225\260\344\270\223\350\275\246`$a0`\357\274\214\344\274\232\346\231\244\350\277\220\347\256\227\346\240\270\345\277\203ALU\357\274\214\346\234\200\345\220\216\346\220\255\344\270\212`$v0`\350\277\224\347\250\213\347\232\204\345\205\250\350\277\207\347\250\213\345\244\247\346\217\255\347\247\230\360\237\230\262.md" deleted file mode 100644 index d036191..0000000 --- "a/src/site/notes/notes/408/\344\270\200\344\270\252\346\225\264\346\225\260\347\232\204\345\245\207\345\271\273\346\274\202\346\265\201\357\274\232\351\200\224\347\273\217\345\217\202\346\225\260\344\270\223\350\275\246`$a0`\357\274\214\344\274\232\346\231\244\350\277\220\347\256\227\346\240\270\345\277\203ALU\357\274\214\346\234\200\345\220\216\346\220\255\344\270\212`$v0`\350\277\224\347\250\213\347\232\204\345\205\250\350\277\207\347\250\213\345\244\247\346\217\255\347\247\230\360\237\230\262.md" +++ /dev/null @@ -1,407 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/一个整数的奇幻漂流:途经参数专车`$a0`,会晤运算核心ALU,最后搭上`$v0`返程的全过程大揭秘😲","permalink":"/408/一个整数的奇幻漂流:途经参数专车`$a0`,会晤运算核心ALU,最后搭上`$v0`返程的全过程大揭秘😲/"} ---- - - -## 第一节:寄存器在加载/存储架构中的角色 - -在深入探讨MIPS(无内部互锁流水级的微处理器)指令集的具体细节之前,必须首先理解寄存器在中央处理器(CPU)设计中的根本性作用,尤其是在MIPS所属的精简指令集计算(RISC)加载/存储(Load/Store)架构中。 - -### 1.1 CPU的高速工作区:定义寄存器 - -寄存器是位于CPU内部的一小块、速度极快的存储区域,通常由触发器(flip-flops)构成 1。它们是CPU进行算术和逻辑运算时直接访问的数据存储单元。与相对缓慢的随机存取存储器(RAM)相比,寄存器访问几乎没有延迟,这使得它们成为CPU处理数据时的首选工作空间 2。 - -寄存器可大致分为两类:通用寄存器(General-Purpose Registers, GPRs)和专用寄存器(Special-Purpose Registers, SPRs)。专用寄存器被硬件设计用于特定任务,例如程序计数器(Program Counter, PC)用于存放下一条待执行指令的地址,指令寄存器(Instruction Register, IR)用于存放当前正在执行的指令 2。而通用寄存器则具备高度的灵活性,程序员(或编译器)可以根据需要用它们来存储操作数、内存地址或计算的中间结果 1。MIPS架构的核心正是其一组丰富的通用寄存器。 - -### 1.2 MIPS作为RISC加载/存储架构 - -MIPS是一种典型的RISC架构,其设计哲学之一便是遵循加载/存储模型 5。这意味着,除了专门用于在寄存器和内存之间传输数据的加载(load)和存储(store)指令外,所有其他的计算指令——如算术运算(加、减、乘)和逻辑运算(与、或、非)——都只能对存放在寄存器中的数据进行操作 5。 - -这种设计选择极大地简化了指令集和CPU的硬件逻辑。然而,它也给软件带来了新的要求:任何存储在内存中的数据,在被处理之前,必须首先通过加载指令被读入到某个寄存器中;计算完成后,若要将结果持久化,则必须通过存储指令写回内存。 - -这种架构设计与寄存器的重要性之间存在着一种共生关系。由于算术逻辑单元(ALU)无法直接操作内存,对内存中变量的任何计算都至少需要三步:加载数据到寄存器、在寄存器上执行计算、将结果存回内存。这个过程比某些复杂指令集计算(CISC)架构中允许的内存到内存操作更为冗长。为了弥补这一点并实现高性能,系统必须被设计为尽可能长时间地将常用变量保留在寄存器中,以最大限度地减少与慢速主存的交互。因此,MIPS架构中庞大且管理良好的寄存器文件并非一个偶然特征,而是其核心加载/存储设计理念的直接产物和必然要求。正是这种设计,催生了一套复杂而精密的软件约定——应用程序二进制接口(ABI)——来高效地管理这些宝贵的寄存器资源。 - -## 第二节:MIPS通用寄存器文件概览 - -MIPS架构提供了一个包含32个通用寄存器的寄存器文件,为程序员和编译器提供了一个宽裕的计算平台。理解这些寄存器的基本属性和命名约定是掌握MIPS编程的第一步。 - -### 2.1 32位寄存器文件 - -标准的32位MIPS架构(MIPS32)提供了32个通用寄存器,编号从0到31,每个寄存器都是32位宽 7。在汇编语言中,它们通常用美元符号 - -`$`后跟数字来表示,例如`$0`、`$8`、`$31`。尽管从硬件角度看,除了一个特例(`$0`)外,ALU可以对任何一个GPR进行操作,但软件层面为它们赋予了不同的角色和使用约定,使得它们在实践中并非完全“通用” 9。 - -### 2.2 不可变的常量:寄存器`$zero` (`$0`) - -寄存器`$0`是一个特例,它在硬件层面被永久地连接到数值0 7。任何向 - -`$0`写入数据的尝试都会被硬件忽略,读取`$0`将永远得到0。 - -这个看似简单的设计实际上是一种精妙的硬件优化。在编程中,将变量初始化为0、与0进行比较、或清空一个寄存器是极为常见的操作。如果没有`$zero`寄存器,这些操作将需要额外的指令。例如,要将寄存器`$t0`清零,可能需要执行一条加载立即数的指令 `li $t0, 0`。要比较`$s0`是否等于0,则需要先将0加载到一个临时寄存器中,再进行比较:`li $t0, 0`,然后 `beq $s0, $t0, label`。 - -通过在硬件中提供一个恒为0的`$zero`寄存器,这些常见操作得到了极大的简化和加速。清空一个寄存器可以直接使用 `move $t0, $zero`(这通常是 `addu $t0, $zero, $zero` 的伪指令),与0的比较则简化为一条指令 `beq $s0, $zero, label`。因此,`$zero`寄存器不仅是一个便利的工具,更是一个经过深思熟虑的硬件设计,旨在减少指令数量、降低最常见操作的执行开销,从而提升整体性能。 - -### 2.3 寄存器约定表 - -为了确保由不同程序员、不同编译器生成的代码能够正确地协同工作,MIPS社区建立了一套被称为应用程序二进制接口(ABI)的软件约定 12。这套约定详细规定了每个寄存器的推荐用途。虽然硬件不强制执行这些规则,但违反它们会导致程序在与标准库或其他模块链接时出现不可预测的错误 9。 - -下表全面总结了MIPS32架构下最常见的O32 ABI中的寄存器约定。 - -**表1:MIPS32通用寄存器约定(O32 ABI)** - -|寄存器编号|符号名 (别名)|ABI角色|保存策略| -|---|---|---|---| -|`$0`|`$zero`|常量0|-| -|`$1`|`$at`|汇编器临时寄存器 (Assembler Temporary)|-| -|`$2`-`$3`|`$v0`-`$v1`|函数返回值 (Values)|调用者保存| -|`$4`-`$7`|`$a0`-`$a3`|函数参数 (Arguments)|调用者保存| -|`$8`-`$15`|`$t0`-`$t7`|临时寄存器 (Temporaries)|调用者保存| -|`$16`-`$23`|`$s0`-`$s7`|保存寄存器 (Saved)|被调用者保存| -|`$24`-`$25`|`$t8`-`$t9`|临时寄存器 (Temporaries)|调用者保存| -|`$26`-`$27`|`$k0`-`$k1`|操作系统内核保留|-| -|`$28`|`$gp`|全局指针 (Global Pointer)|被调用者保存| -|`$29`|`$sp`|栈指针 (Stack Pointer)|被调用者保存| -|`$30`|`$fp`|帧指针 (Frame Pointer)|被调用者保存| -|`$31`|`$ra`|返回地址 (Return Address)|被调用者保存| - -资料来源:综合自 8 - -这张表是理解MIPS编程的关键。它不仅列出了寄存器的符号名,更重要的是揭示了它们在函数调用、数据存储和系统交互中的预设角色,以及由“调用者保存”和“被调用者保存”构成的精细分工。后续章节将对这些分类进行深入剖析。 - -## 第三节:功能分类与应用程序二进制接口(ABI) - -MIPS寄存器的功能分配并非由硬件强制规定,而是由ABI这一软件层面的“契约”来定义的。本节将深入探讨ABI的核心概念,并根据寄存器在其中的功能角色进行系统性分类。 - -### 3.1 ABI:代码互操作性的契约 - -ABI是一套低层次的规范,它定义了编译后的二进制代码模块之间如何交互,包括函数调用约定、数据类型的大小和对齐方式、寄存器使用规范等 12。遵循统一的ABI是保证一个程序能够正确链接和调用外部库(例如标准C库)或与不同编译器(如GCC)生成的代码协同工作的前提 17。对于构建任何稳定、可移植的软件而言,遵守ABI约定是强制性的,而非可选项 10。 - -### 3.2 用于子程序链接的寄存器(函数调用接口) - -函数调用是程序结构化的基本单元,ABI为此专门指定了一组寄存器来管理调用过程中的控制流和数据流。 - -- **参数寄存器 (`$a0`-`$a3`)**: 这四个寄存器用于向函数传递前四个32位的参数 5。如果函数参数超过四个,多余的参数需要通过栈来传递。寄存器 - - `$a0`用于传递第一个参数,`$a1`用于第二个,以此类推。 - -- **返回值寄存器 (`$v0`-`$v1`)**: 这两个寄存器用于函数返回结果。通常,`$v0`用于返回主要的32位返回值(例如一个整数或一个指针),`$v1`可用于返回第二个值或一个64位结果的高32位 5。对于更大的返回类型, - - `$v0`通常用于返回一个指向内存中结果对象的指针。 - -- **返回地址寄存器 (`$ra`)**: 当执行`jal`(Jump and Link,跳转并链接)指令调用一个函数时,硬件会自动将下一条指令的地址(即`PC+4`)存入`$ra`寄存器,然后才跳转到目标函数 5。当函数执行完毕后,通过执行 - - `jr $ra`(Jump Register,跳转寄存器)指令,程序控制权便能精确地返回到调用点之后的位置。 - - -### 3.3 责任分工:调用者保存与被调用者保存的寄存器 - -为了在函数调用过程中高效地保护寄存器中的数据,ABI引入了一套精巧的责任分工机制,将保存寄存器的任务分配给调用函数(caller)和被调用函数(callee)。这种机制旨在最小化不必要的内存访问,从而优化性能。 - -- 临时寄存器(调用者保存, Caller-Saved) ($t0-$t9): - - 这些寄存器被视为“易失性”的或“草稿”寄存器 15。被调用的函数可以自由地使用和修改它们,而无需事先保存其内容。因此,如果一个 - - **调用者**在某个`$t`寄存器中存放了重要数据,并且希望这个数据在函数调用后仍然有效,那么**调用者**自己有责任在执行`jal`指令前将该寄存器的值保存到栈中,并在函数返回后将其恢复 15。 - -- 保存寄存器(被调用者保存, Callee-Saved) ($s0-$s7): - - 这些寄存器被视为“非易失性”的 15。ABI向调用者保证,在函数调用结束后,这些寄存器的值将与调用前保持一致。因此,如果一个 - - **被调用者**需要使用某个`$s`寄存器进行计算,它**必须**在函数序言(prologue)中首先将该寄存器的原始值保存到自己的栈帧中,并在函数尾声(epilogue)中将其恢复,然后才能返回 14。 - - -这种分工是一种高效的性能调优策略。每一次寄存器的保存(store)和恢复(load)都对应一次内存访问,而内存访问远慢于寄存器操作。该约定的目标是确保只有在绝对必要时才执行这些慢速操作。 - -- 对于生命周期短暂、仅在函数内部使用的临时变量,使用`$t`寄存器是最高效的,因为被调用函数可以自由使用它们而无需任何栈操作。 - -- 对于生命周期较长、需要跨越函数调用的变量(如循环计数器),使用`$s`寄存器更为明智。调用者可以放心地将这些变量存放在`$s`寄存器中,因为它知道被调用的函数不会破坏它们。即使被调用函数需要使用同一个`$s`寄存器,也只有它自己会执行一次保存和恢复操作。 - - -这种机制避免了调用者“防御性地”保存所有它关心的寄存器,也避免了被调用者“盲目地”保存所有它可能用到的寄存器。每一方都有明确且最小化的责任,从而将函数调用过程中的内存流量降至最低。 - -### 3.4 用于内存组织的指针 - -为了有效地管理程序在内存中的布局,特别是栈和全局数据区,ABI指定了几个寄存器作为专用指针。 - -- **栈指针 (`$sp`, `$29`)**: 该寄存器始终指向当前栈顶。在MIPS架构中,栈从高地址向低地址“向下”生长 5。当一个函数被调用时,它会在其序言中通过减少 - - `$sp`的值来为自己的栈帧分配空间;在尾声中通过增加`$sp`的值来释放空间。 - -- **帧指针 (`$fp`, `$30`)**: 该寄存器通常指向当前函数栈帧中的一个固定位置,例如栈帧的底部。当栈指针`$sp`为了计算复杂表达式或为嵌套调用准备参数而动态变化时,`$fp`提供了一个稳定的基地址来访问局部变量和传入的参数 8。虽然现代编译器有时会优化掉 - - `$fp`的使用,但理解其概念对于掌握栈管理至关重要。在不使用帧指针的约定中,`$30`寄存器通常被当作一个额外的被调用者保存寄存器`$s8`使用 10。 - -- **全局指针 (`$gp`, `$28`)**: 该寄存器指向静态数据区中间的一个64KB大小的内存块 8。通过使用 - - `$gp`作为基地址,程序可以用一条`lw`(load word)或`sw`(store word)指令,配合一个16位的偏移量,高效地访问这个区域内的全局变量和常量。 - - -### 3.5 系统保留寄存器 - -为了保证操作系统和汇编工具的正常工作,ABI保留了几个寄存器,应用程序代码绝不能使用它们。 - -- **汇编器临时寄存器 (`$at`, `$1`)**: 专为汇编器保留。汇编器常将一些复杂的伪指令(pseudo-instructions)翻译成一串真实的MIPS指令。在这个过程中,`$at`被用作一个临时的草稿寄存器 8。程序员绝不应直接使用 - - `$at`,因为它的值可能在任何时候被汇编器生成的代码所覆盖。 - -- **内核保留寄存器 (`$k0`-`$k1`, `$26`-`$27`)**: 专为操作系统内核保留。这些寄存器可用于中断处理程序和异常处理例程。应用程序代码必须避免使用它们,因为操作系统可能在任何时候(如发生中断或上下文切换时)改变它们的值,从而导致应用程序逻辑错误 5。 - - -## 第四节:实践中的数据流:一个递归阶乘函数 - -理论知识只有在实践中才能得到真正的检验。本节将通过一个具体的递归函数示例,逐步追踪数据在寄存器和内存之间的流动,从而将前述所有关于ABI和寄存器约定的概念融会贯通。递归函数是理想的示例,因为它同时扮演了调用者和被调用者的角色,完美地展示了整个ABI机制的动态过程。 - -### 4.1 MIPS栈帧的剖析 - -在追踪示例之前,必须先了解MIPS栈帧(Stack Frame)的结构。栈帧是每次函数调用时在栈上为其分配的私有内存空间,用于存储局部变量、保存的寄存器以及传递给其他函数的参数 15。 - -**表2:标准MIPS栈帧结构(O32 ABI)** - -|栈帧内容 (地址由高到低)|说明| -|---|---| -|**...**|**(来自调用者的栈帧)**| -|**参数区**|为本函数将要调用的其他函数预留的参数空间 (如果参数>4个)| -|**寄存器保存区**|用于保存被调用者保存的寄存器 (`$s0`-`$s7`, `$fp`, `$ra`)| -|**局部变量区**|用于存储函数的局部变量| -|**...**|**(当前栈顶)**| - -- **指针**: `$fp`(如果使用)通常指向帧的固定基址,而`$sp`则动态地指向当前栈顶(已分配空间的最低地址)。 - -- **分配与释放**: 函数在其序言中通过递减`$sp`来分配栈帧,在其尾声中通过递增`$sp`来释放。 - - -理解栈帧的静态布局是理解其在函数调用和返回过程中动态变化的基础。 - -### 4.2 示例代码:`int factorial(int n)` - -我们将分析一个简单的C语言递归阶乘函数及其对应的MIPS汇编代码。 - -**C语言代码:** - -C - -``` -int factorial(int n) { - if (n < 2) return 1; - return n * factorial(n - 1); -} -``` - -**MIPS汇编代码 (带注释):** - -代码段 - -``` -factorial: - # --- Prologue (函数序言) --- - # 分配栈帧 (8字节: 4字节存$ra, 4字节存$s0) - addiu $sp, $sp, -8 - # 保存返回地址和$s0 (因为我们将修改$s0且会进行嵌套调用) - sw $ra, 4($sp) - sw $s0, 0($sp) - - # --- Body (函数体) --- - # 将参数n从$a0移动到$s0, 以便在调用后能保留n的值 - move $s0, $a0 - - # 检查基本情况: if (n < 2) - slti $t0, $a0, 2 # if $a0 < 2, set $t0 = 1 - beq $t0, $zero, L1 # if $t0 == 0 (n >= 2), branch to L1 - - # Base Case (基本情况): return 1 - li $v0, 1 # 将返回值1放入$v0 - j epilogue # 跳转到尾声准备返回 - -L1: # Recursive Step (递归步骤) - # 计算 n - 1 - addiu $a0, $a0, -1 - # 递归调用 factorial(n - 1) - jal factorial - - # --- Post-call (调用后) --- - # 递归调用返回后, 结果在$v0中 - # 执行 n * factorial(n-1) - # 从栈中恢复n的值到$s0 (此时$a0已被覆盖) - # 注意: 这里的lw指令实际上可以省略, 因为$s0在函数体内没有被修改过 - # 但为了清晰展示, 我们保留它 - lw $s0, 0($sp) - mul $v0, $s0, $v0 # $v0 = n * (result of recursive call) - -epilogue: - # --- Epilogue (函数尾声) --- - # 恢复$ra和$s0 - lw $ra, 4($sp) - lw $s0, 0($sp) - # 释放栈帧 - addiu $sp, $sp, 8 - # 返回调用者 - jr $ra -``` - -### 4.3 逐步执行追踪:`main` 调用 `factorial(3)` - -以下将通过一系列ASCII图示,详细追踪`main`函数调用`factorial(3)`时,寄存器和栈的状态变化。 - -**阶段1: `main` 调用 `factorial(3)`** - -- `main`函数将参数`3`放入`$a0`。 - -- `main`执行`jal factorial`。硬件将`main`中`jal`指令的下一条指令地址存入`$ra`。 - - -``` - Registers before call: - $a0 = 3 - $ra = (address in main after jal) - $sp = 0x7ffffffc (initial stack top) - - Stack: - -|... | (High Address) -| (empty) | - +--------------+ <--- $sp - (Low Address) -``` - -**ASCII图示 1: 初始调用** - -**阶段2: `factorial(3)` 的序言** - -- `factorial(3)`开始执行。它需要保存`$ra`(因为它将进行递归调用)和参数`n`(因为`$a0`将被下一次调用覆盖,而`n`在调用后仍需使用)。 - -- 执行`addiu $sp, $sp, -8`,分配8字节栈帧。 - -- 执行`sw $ra, 4($sp)`和`sw $s0, 0($sp)`(这里我们假设`$s0`在进入函数前的值需要保存,且`n`被移入`$s0`)。实际上代码是先`move $s0, $a0`,再保存`$s0`,但为了简化,我们视为保存`n`。 - - -``` - Registers in factorial(3): - $a0 = 3, $s0 = 3 - $sp = 0x7ffffff4 - - Stack: - -|... | - +--------------+ 0x7ffffffc - -| ret addr to main | <-- 4($sp) - +--------------+ - -| n=3 (from $s0) | <-- 0($sp) - +--------------+ <--- $sp -``` - -**ASCII图示 2: `factorial(3)` 的栈帧** - -**阶段3: `factorial(3)` 进行递归调用 `factorial(2)`** - -- 计算`n-1`,即`3-1=2`,并将结果`2`放入`$a0`。 - -- 执行`jal factorial`。硬件将`factorial(3)`中`jal`指令的下一条指令地址(即`mul`指令的地址)存入`$ra`。 - - -``` - Registers before 2nd call: - $a0 = 2 - $ra = (address of 'mul' in factorial(3)) - $sp = 0x7ffffff4 -``` - -**ASCII图示 3: 第二次调用前** - -**阶段4: `factorial(2)` 的序言和对 `factorial(1)` 的调用** - -- `factorial(2)`执行其序言,在`factorial(3)`的栈帧之上创建自己的栈帧。 - -- 它保存自己的返回地址(指向`factorial(3)`中的`mul`)和自己的`n`值(`2`)。 - -- 然后计算`n-1`(即`1`),放入`$a0`,并调用`factorial(1)`。 - - -``` - Registers in factorial(2): - $a0 = 2, $s0 = 2 - $sp = 0x7fffffeC - - Stack: - -|... | - +--------------+ 0x7ffffffc - -| ret addr to main | - +--------------+ - -| n=3 | - +--------------+ 0x7ffffff4 - -| ret addr to f(3) | <-- 4($sp) - +--------------+ - -| n=2 | <-- 0($sp) - +--------------+ <--- $sp -``` - -**ASCII图示 4: 栈深度为2** - -**阶段5: `factorial(1)` - 达到基本情况** - -- `factorial(1)`检查到`n < 2`,满足基本情况。 - -- 它不进行递归调用,而是将返回值`1`放入`$v0`。 - -- 它执行尾声:恢复寄存器(在本例中无事可做),释放自己的栈帧(`addiu $sp, $sp, 8`),然后执行`jr $ra`返回到`factorial(2)`。 - - -``` - Registers after f(1) returns: - $v0 = 1 - $sp = 0x7ffffff4 (stack pointer restored) - Control is back in factorial(2). -``` - -**ASCII图示 5: 开始回溯** - -**阶段6: `factorial(2)` 的尾声** - -- 执行流回到`factorial(2)`的`jal`指令之后。 - -- 它从自己的栈帧中恢复`n`的值(`2`)到`$s0`。 - -- 执行乘法:`$v0 = $s0 * $v0`,即`2 * 1 = 2`。结果`2`存入`$v0`。 - -- 执行尾声:从栈中恢复它自己的`$ra`和`$s0`,释放栈帧,然后`jr $ra`返回到`factorial(3)`。 - - -``` - Registers after f(2) returns: - $v0 = 2 - $sp = 0x7ffffffc (stack pointer restored) - Control is back in factorial(3). -``` - -**ASCII图示 6: 进一步回溯** - -**阶段7: `factorial(3)` 的尾声并返回`main`** - -- 过程重复。`factorial(3)`从栈中恢复`n`的值(`3`)。 - -- 执行乘法:`$v0 = $s0 * $v0`,即`3 * 2 = 6`。结果`6`存入`$v0`。 - -- 执行尾声:恢复寄存器,释放栈帧,`jr $ra`返回到`main`。 - - -``` - Registers after f(3) returns to main: - $v0 = 6 - $sp = 0x7ffffffc (stack restored to initial state) - - Stack: - -|... | - +--------------+ <--- $sp - (empty again) -``` - -**ASCII图示 7: 最终返回** - -至此,`main`函数在`$v0`寄存器中得到了最终结果`6`。这个详细的追踪过程生动地展示了`$a0`, `$v0`, `$ra`, `$sp`, `$s0`, `$t0`等寄存器如何在ABI的指导下协同工作,通过栈来保存上下文,从而成功地实现了复杂的递归调用。 diff --git "a/src/site/notes/notes/408/\344\270\200\346\235\241\346\214\207\344\273\244\347\232\204\350\257\236\347\224\237\345\222\214\350\247\243\345\211\226\344\273\216\345\246\202\344\275\225\350\256\276\350\256\241\345\210\260CPU\350\247\243\347\240\201\360\237\244\224.md" "b/src/site/notes/notes/408/\344\270\200\346\235\241\346\214\207\344\273\244\347\232\204\350\257\236\347\224\237\345\222\214\350\247\243\345\211\226\344\273\216\345\246\202\344\275\225\350\256\276\350\256\241\345\210\260CPU\350\247\243\347\240\201\360\237\244\224.md" deleted file mode 100644 index fe0bc94..0000000 --- "a/src/site/notes/notes/408/\344\270\200\346\235\241\346\214\207\344\273\244\347\232\204\350\257\236\347\224\237\345\222\214\350\247\243\345\211\226\344\273\216\345\246\202\344\275\225\350\256\276\350\256\241\345\210\260CPU\350\247\243\347\240\201\360\237\244\224.md" +++ /dev/null @@ -1,76 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/一条指令的诞生和解剖从如何设计到CPU解码🤔","permalink":"/408/一条指令的诞生和解剖从如何设计到CPU解码🤔/"} ---- - - -### 一条指令的诞生与“宿命” ---- - -### 第一部分:指令的“诞生” - -#### 1. 顶层:“鸡生蛋还是蛋生鸡”的设计 - -在设计一套全新的指令集时,设计师首先面临一个类似“鸡生蛋还是蛋生鸡”的哲学抉择,这个抉择决定了指令长度的基本形态: - -- **RISC哲学(先定“饭碗”大小)**:以硬件执行效率为最高优先级。设计师会先定下一个**固定的、规整的指令长度**(例如,所有指令均为32位)。这个“饭碗”的大小是首要约束。然后,设计师在这个固定的空间内,去“精打细算”地分配给操作码、寄存器编号等字段,看最多能塞下多少种指令。**在这里,是指令字长和格式,反过来限制了指令的数量。** - -- **CISC哲学(先看“要吃多少菜”)**:以软件编程的便利性和代码密度为优先。设计师会先列出所有希望实现的强大功能,为每一条复杂指令“量身定做”最合适的格式。这导致了指令的长度根据其功能而**变化**。**在这里,是指令的数量和功能需求,共同决定了每一条指令各自的(可变的)长度。** - - -#### 2. 具体因素:构成指令“胖瘦”的“零部件” - -无论采用何种哲学,一条指令的长度都是其内部各个“零部件”长度的总和。 - -- **操作码(Opcode)**:告诉CPU“做什么”。指令系统的**指令总数**决定了操作码的最小长度。指令越多,操作码字段就需要越长。 - -- **操作数(Operand)**:告诉CPU“对谁做”。这是影响长度的最主要因素。 - - - **操作数个数**:三地址、二地址、一地址、零地址指令所包含的地址字段数量不同,直接影响总长度。 - - - **寻址方式**:获取操作数的方法极大地影响着地址字段的长度。 - - - **寄存器寻址**:字段最短,只需几位来编码寄存器号。 - - - **直接寻址**:字段最长,需要包含一个完整的主存地址(如32位或64位)。 - - - **立即数寻址**:指令中直接包含了数据,数据多长,指令就相应地加长。 - -### 第二部分:指令的“解码” - -CPU(特别是处理变长指令的CISC CPU)是如何“测量”出一条指令的长度的。 - -CPU事先并不知道指令有多长,它依赖于硬件**指令译码器(Instruction Decoder)**进行“走一步,看一步”的串行解码。 - -1. **取指与缓冲**:CPU从内存中一次性抓取一块数据(包含多条指令)到内部的指令缓冲区。 - -2. **前缀解析**:译码器从缓冲区的当前指针位置开始,逐字节检查是否存在可选的前缀(如x86的`66H`, `REX`等),每识别一个前缀,指针就后移一字节。 - -3. **操作码解码(最关键一步)**:译码器读取到第一个非前缀字节,即**操作码**。根据这个操作码的值,译码器通过内部的硬连线逻辑(像查字典一样)瞬间得知该指令的“模板”——它后面是否需要ModR/M字节?需要多长的立即数? - -4. **后续字段解析**:根据操作码提供的“情报”,译码器继续向后读取并解析ModR/M、SIB、地址偏移量、立即数等后续字节。每解析一部分,它就更清楚指令还剩下多少未读部分。 - -5. **确定边界**:当一条指令所需的所有部分都被“吃掉”后,译码器就知道了这条指令的总长度。当前指针指向的位置,就是下一条指令的开始。这个“拆盲盒”的过程至此完成一轮。 - - ---- - -### 第三部分:执行中的安全与保障 - -- **“如果PC指针指错了,指到了一个数据(机器数)区怎么办?”** - - - **正常情况**:这不会发生。**程序计数器(PC)**是CPU执行流程的“神圣向导”,它被编译器、加载器和操作系统设置为永远指向代码段。数据和指令虽然共用内存,但在正常的程序流程中“井水不犯河水”。 - - - **异常情况**:如果因程序BUG(如缓冲区溢出)导致PC被篡改,指向了数据区。CPU会“盲目信任”PC,将数据当成指令进行解码。这通常会因为数据组成不了合法指令而触发**“非法指令”硬件异常**,导致程序被操作系统强制终止,这是一种**硬件级的保护机制**。 - -- **“可以通过PC的递增数量来判断指令长度吗?”** - - - **不能,因果关系正好相反**。正确的顺序是: - - 1. PC指向指令的**起始地址** `P_start`。 - - 2. **指令译码器**工作,确定了该指令的**长度 `L`**。 - - 3. PC根据这个已知的长度 `L` **进行更新**:`PC = P_start + L`。 - - - 所以,是**译码器确定的指令长度,决定了PC应该增加多少**,而不是反过来。 - \ No newline at end of file diff --git "a/src/site/notes/notes/408/\344\270\255\346\226\255\344\277\241\345\217\267\347\232\204\344\274\230\345\205\210\347\272\247,\345\244\232\351\207\215\344\270\255\346\226\255\347\232\204\345\244\204\347\220\206.md" "b/src/site/notes/notes/408/\344\270\255\346\226\255\344\277\241\345\217\267\347\232\204\344\274\230\345\205\210\347\272\247,\345\244\232\351\207\215\344\270\255\346\226\255\347\232\204\345\244\204\347\220\206.md" deleted file mode 100644 index f843d3d..0000000 --- "a/src/site/notes/notes/408/\344\270\255\346\226\255\344\277\241\345\217\267\347\232\204\344\274\230\345\205\210\347\272\247,\345\244\232\351\207\215\344\270\255\346\226\255\347\232\204\345\244\204\347\220\206.md" +++ /dev/null @@ -1,90 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/中断信号的优先级,多重中断的处理","permalink":"/408/中断信号的优先级,多重中断的处理/"} ---- - - -### 一、中断优先级的确定机制 - -#### 1. **硬件优先级编码** -- **中断控制器固定优先级**: - 在传统8259A中断控制器中,IRQ0-IRQ7的优先级固定为递减顺序(IRQ0最高,IRQ7最低)。现代APIC架构支持动态配置优先级寄存器(如LAPIC的TPR,Task Priority Register),通过4位字段定义16级优先级。 -- **中断向量表自然顺序**: - 当多个中断具有相同的用户分配优先级时,其冲突通过中断向量表(IVT)地址顺序解决,地址较低的中断优先级更高。例如,x86架构中除零异常(INT 0)的向量地址为0,优先级高于系统调用(INT 80h)。 - - -#### 2. **软件动态配置** -- **优先级分组与仲裁**: - 操作系统可通过中断控制器寄存器(如ARM GIC的IPRIORITYR)动态调整中断优先级。例如,Linux内核通过`irq_set_priority()`函数修改中断优先级。 -- **任务优先级匹配机制**: - CPU通过TPR寄存器屏蔽低于当前任务优先级的中断。例如,当TPR=0x40时,仅允许优先级高于4(即数值小于4)的中断被处理。 - - -#### 3. **设备特性优先级** -- **关键外设高优先级**: - 系统通常将关键设备(如时钟中断、NMI)设为最高优先级。例如,x86架构的时钟中断(IRQ0)优先级高于键盘中断(IRQ1)。 - ---- - - - -### 二、多中断冲突的处理策略 - -#### 1. **硬件仲裁机制** -- **抢占式嵌套中断**: - 高优先级中断可抢占低优先级中断处理流程。例如,PSoC® 4处理器支持嵌套中断,允许更高优先级中断在低优先级ISR执行期间被响应。 -- **中断屏蔽与排队**: - 中断控制器(如GIC)通过挂起寄存器(Pending Register)记录未处理中断,按优先级排序并逐个提交给CPU。 - - -#### 2. **软件调度策略** -- **中断下半部机制**: - 将非紧急处理逻辑延迟到软中断(Softirq)或工作队列(Workqueue)中执行。例如,Linux网卡驱动的NAPI机制通过`napi_schedule()`将数据包处理推迟到软中断上下文。 -- **优先级继承与恢复**: - 在实时系统中,通过优先级继承(Priority Inheritance)防止优先级反转。例如,当低优先级任务持有高优先级任务所需资源时,临时提升低优先级任务优先级。 - - -#### 3. **异常情况处理** -- **中断丢失风险**: - 若中断控制器未实现排队机制(如部分8位微控制器),多个同优先级中断可能因共享中断线导致丢失。需通过`request_irq()`注册共享中断处理链表解决。 -- **死锁预防**: - 在原子上下文中(如ISR),禁止调用可能引起阻塞的操作(如`spin_lock()`无超时版本)。应改用`spin_lock_irqsave()`防止中断嵌套导致死锁。 - ---- - - - -### 三、典型实现与边界条件 - -#### 1. **x86架构中断优先级管理** -- **LAPIC优先级控制**: - TPR寄存器的4位优先级字段(P[7:4])定义16级优先级(0-15,数值越小优先级越高)。例如,TPR=0x10时允许处理优先级0-3的中断。 -- **中断嵌套示例**: - 当CPU处理低优先级中断(如IRQ11)时,若收到高优先级中断(如IRQ1),LAPIC会触发中断嵌套,保存当前ISR状态并跳转至高优先级ISR。 - - -#### 2. **ARM架构中断优先级管理** -- **GIC优先级寄存器**: - 每个中断源对应一个8位优先级寄存器(IPRIORITYR),支持256级优先级。通过`GICD_IPRIORITYRn`寄存器配置,数值越小优先级越高。 -- **抢占阈值设置**: - CPU通过写`ICCPMR`寄存器设置中断屏蔽阈值,仅允许优先级高于该阈值的中断被处理。 - - -#### 3. **历史差异与误用风险** -- **8259A固定优先级局限性**: - 传统PC中,IRQ0(时钟)优先级固定高于IRQ7(并口),无法动态调整,可能导致非关键中断阻塞关键任务。 -- **KPTI对中断延迟的影响**: - Meltdown漏洞修复后的内核页表隔离(KPTI)机制需切换CR3寄存器,增加中断处理延迟约15%。 - ---- - - - -### 四、设计原则与最佳实践 -| 原则 | 实现方法 | -|--------------------|--------------------------------------------------------------------------| -| **最小化ISR执行时间** | 将耗时操作(如数据拷贝)移至软中断或工作队列,确保ISR在原子上下文中快速返回 | -| **优先级划分合理性** | 关键中断(如时钟、电源监控)设为最高优先级,避免低优先级中断阻塞系统核心功能 | -| **并发控制** | 使用自旋锁(Spinlock)保护共享数据结构,防止中断嵌套导致的数据竞争 | -| **动态调整能力** | 支持运行时优先级调整(如通过`irq_set_priority()`),适应动态负载变化 | - -**结论**:中断优先级的确定需结合硬件特性(如中断控制器架构)与软件策略(如优先级继承),并通过抢占式嵌套、下半部机制等技术平衡实时性与系统稳定性。开发者必须严格遵循原子上下文约束,合理划分中断处理逻辑,以避免死锁、中断丢失等关键错误。 diff --git "a/src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\345\222\214\345\255\220\347\250\213\345\272\217\350\260\203\347\224\250\345\257\271\345\257\204\345\255\230\345\231\250\347\232\204\344\277\235\345\255\230\346\203\205\345\206\265.md" "b/src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\345\222\214\345\255\220\347\250\213\345\272\217\350\260\203\347\224\250\345\257\271\345\257\204\345\255\230\345\231\250\347\232\204\344\277\235\345\255\230\346\203\205\345\206\265.md" deleted file mode 100644 index 1677e35..0000000 --- "a/src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\345\222\214\345\255\220\347\250\213\345\272\217\350\260\203\347\224\250\345\257\271\345\257\204\345\255\230\345\231\250\347\232\204\344\277\235\345\255\230\346\203\205\345\206\265.md" +++ /dev/null @@ -1,115 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/中断处理和子程序调用对寄存器的保存情况","permalink":"/408/中断处理和子程序调用对寄存器的保存情况/"} ---- - - -子程序调用与中断处理在**寄存器值的保存机制**上存在显著差异,主要体现在**保存主体、保存范围、触发条件及恢复时机**等方面。以下是详细分析: - ---- - - - -### 一、子程序调用的寄存器保存机制 - -#### 1. **保存主体:程序员/编译器显式管理** -- **调用约定(Calling Convention)决定保存策略**: - 子程序调用需遵循特定调用约定(如System V AMD64 ABI),明确哪些寄存器由调用者(caller)保存,哪些由被调用者(callee)保存。例如: - - **调用者保存寄存器**(如x86-64的`RAX`, `RCX`, `RDX`, `RDI`, `RSI`):子程序可能修改这些寄存器,调用方需在调用前压栈保存。 - - **被调用者保存寄存器**(如`RBP`, `RBX`, `R12-R15`):子程序内部若需使用这些寄存器,必须显式保存至栈,返回前恢复 。 -- **示例(x86-64汇编)**: - ```asm - call my_subroutine - my_subroutine: - push rbp ; 保存基址寄存器(被调用者保存) - mov rbp, rsp ; 建立新栈帧 - ; ... 子程序逻辑 ... - pop rbp ; 恢复基址寄存器 - ret - ``` - - -#### 2. **保存范围:有限且选择性保存** -- **仅保存必要寄存器**: - 子程序仅需保存其实际使用的寄存器,而非所有寄存器。例如,若子程序仅修改`RAX`,则无需保存`RBX` 。 -- **栈作为保存媒介**: - 寄存器值通常压入调用栈(用户栈),通过`push`/`pop`指令或`mov`指令直接写入栈空间 。 - - -#### 3. **触发条件与恢复时机** -- **显式调用与返回**: - 通过`call`指令触发调用,`ret`指令恢复程序计数器(PC),并依赖程序员显式恢复寄存器 。 - ---- - - - -### 二、中断处理的寄存器保存机制 - -#### 1. **保存主体:硬件与内核协同自动完成** -- **硬件自动保存部分寄存器**: - 当中断发生时,CPU自动将关键寄存器(如`EFLAGS`, `CS`, `EIP`)压入内核栈,确保中断返回后程序可继续执行。例如,x86架构通过任务状态段(TSS)切换栈,并自动保存上下文 。 -- **内核负责保存剩余寄存器**: - 中断处理程序入口(如Linux的`entry_SYSCALL_64`)需通过宏(如`SAVE_ALL`)保存通用寄存器(`RAX`, `RCX`, `RDX`等),防止中断处理破坏用户态数据 。 - - -#### 2. **保存范围:全寄存器上下文** -- **完整上下文保存**: - 中断处理程序需保存所有可能被修改的寄存器,包括通用寄存器、状态寄存器及架构特定寄存器(如x86的`CR2`用于缺页异常地址)。例如,PA-RISC架构通过“影子寄存器”(Shadow Registers)直接保存通用寄存器,减少手动操作 。 -- **内核栈作为保存媒介**: - 所有寄存器值保存至内核栈,与用户栈物理隔离,确保安全 。 - - -#### 3. **触发条件与恢复时机** -- **异步中断触发**: - 中断由外部事件(如I/O完成、定时器)或异常(如缺页、除零错误)触发,不可预测 。 -- **恢复由硬件与内核共同保障**: - 通过`iret`(x86)或`eret`(MIPS)指令恢复上下文,硬件弹出自动保存的寄存器,内核代码恢复通用寄存器 。 - ---- - - - -### 三、关键区别与边界条件 -| 维度 | 子程序调用 | 中断处理 | -| -------- | --------------------- | -------------------- | -| **保存主体** | 程序员/编译器显式管理 | 硬件自动+内核代码显式保存 | -| **保存范围** | 选择性保存(调用约定定义) | 全寄存器上下文保存 | -| **触发条件** | 同步调用(`call`指令) | 异步中断信号或异常 | -| **保存媒介** | 用户栈 | 内核栈 | -| **恢复机制** | 显式`pop`或栈帧回退(`leave`) | 硬件指令(如`iret`)+内核代码恢复 | -| **性能开销** | 低(仅必要操作) | 高(完整上下文切换) | - -#### 特殊架构差异: -- **寄存器窗口(Register Windows)**: - 如SPARC架构通过寄存器窗口切换减少保存/恢复操作,仅在窗口溢出时访问栈 。 -- **影子寄存器**: - PA-RISC架构为中断处理提供专用影子寄存器,避免手动保存通用寄存器 。 - ---- - - - -### 四、误用风险与例外情况 -1. **子程序调用中的未保存寄存器**: - - 若被调用者未按约定保存`callee-saved`寄存器,可能导致调用方数据损坏(如`RBX`被意外修改)。 -2. **中断处理中的嵌套中断**: - - 若中断处理未正确屏蔽同级中断,可能导致寄存器保存冲突(需通过`CLI`/`STI`或优先级机制避免)。 -3. **KPTI(内核页表隔离)的影响**: - - 现代系统为防御Meltdown漏洞,中断返回时需切换页表(CR3),额外增加上下文保存开销 。 - ---- - - - -### 五、总结:设计原则与最佳实践 -1. **子程序调用**: - - 严格遵循调用约定,明确保存责任; - - 避免过度保存(如无意义的`push`/`pop`),优化性能 。 -2. **中断处理**: - - 确保`SAVE_ALL`/`RESTORE_ALL`完整性,防止上下文丢失; - - 优先使用硬件支持机制(如影子寄存器)降低延迟 。 -3. **混合场景**: - - 在中断处理中调用子程序时,需确保子程序为可重入(Reentrant),避免使用静态变量 。 - -**最终结论**: -子程序调用的寄存器保存是**程序员可控的有限操作**,而中断处理的保存是**硬件与内核协同的全面保护**。两者的设计目标截然不同:前者追求效率,后者强调安全性与正确性。 diff --git "a/src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\347\250\213\345\272\217.md" "b/src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\347\250\213\345\272\217.md" deleted file mode 100644 index 4ba1084..0000000 --- "a/src/site/notes/notes/408/\344\270\255\346\226\255\345\244\204\347\220\206\347\250\213\345\272\217.md" +++ /dev/null @@ -1,152 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/中断处理程序","permalink":"/408/中断处理程序/"} ---- - - -### 一、中断处理程序的严格定义 -**中断处理程序**(Interrupt Service Routine, ISR)是操作系统内核中**专门响应和处理中断事件的代码模块**,其本质是**硬件中断信号触发后,CPU自动跳转执行的固定入口函数**。 -- **形式化定义**: - 当硬件或软件产生中断信号时,CPU通过中断向量表(Interrupt Vector Table)定位到预先注册的ISR地址,并切换至特权模式执行该程序。例如,x86架构中,中断向量表是一个包含256个表项的数组,每个表项指向一个ISR的入口地址。 - ---- - - - -### 二、中断处理程序的体系结构地位 - -#### 1. **在操作系统中的层级** -- **核心态(Kernel Mode)组件**: - ISR运行于CPU的最高特权级(如x86的Ring 0),可直接访问硬件寄存器和内核数据结构,属于操作系统内核的**底层硬中断处理层**。 -- **中断机制的核心实现**: - 它是中断机制(Interrupt Mechanism)的**软件实现主体**,与中断控制器(如APIC)、中断描述符表(IDT)共同构成完整的中断响应体系。 - - -#### 2. **与异常处理程序的关系** -- **严格区分中断(Interrupt)与异常(Exception)**: - - **中断**:异步事件(如I/O完成、定时器触发),由外部硬件发起; - - **异常**:同步事件(如除零错误、缺页异常),由指令执行引发。 - 两者均通过中断向量表分发,但异常处理程序需额外处理错误码压栈、指令重启等问题。 - ---- - - - -### 三、中断处理程序的执行流程 - -#### 1. **中断响应阶段** -1. **硬件自动保存上下文**: - CPU将标志寄存器(EFLAGS)、代码段寄存器(CS)、指令指针(EIP)压入内核栈,确保后续恢复执行流。 -2. **中断号获取与向量计算**: - 从中断控制器(如PIC或APIC)读取中断号(如键盘中断为IRQ1,对应中断号33),计算ISR入口地址。 -3. **特权级切换**: - 若当前处于用户态(CPL=3),CPU自动切换到内核栈,并加载内核态段寄存器值。 - - -#### 2. **中断处理阶段** -1. **高级可编程中断控制器(APIC)应答**: - 向APIC发送EOI(End Of Interrupt)信号,解除中断屏蔽。 -2. **共享中断处理(可选)**: - 对于共享中断线(如PCIe设备),依次调用所有注册的ISR,通过`request_irq()`注册的共享中断处理链表实现。 -3. **关键操作执行**: - - **I/O设备状态读取**:如读取硬盘控制器状态寄存器,确认DMA传输完成; - - **软中断(Softirq)或任务队列(Tasklet)调度**:将耗时操作延迟到下半部处理; - - **唤醒等待队列**:如读取字符设备缓冲区满后,唤醒`wait_event()`阻塞的进程。 - - -#### 3. **中断返回阶段** -1. **上下文恢复**: - 通过`iret`指令弹出EIP、CS、EFLAGS,恢复被中断的执行流。 -2. **抢占式调度检查**: - 若中断返回前发生调度标记(如`reschedule`被置位),则触发上下文切换。 - ---- - - - -### 四、中断处理程序的关键特性 - -#### 1. **原子性与不可抢占性** -- **原子执行要求**: - ISR必须在**关中断上下文**(atomic context)中执行,期间禁止同一CPU上的中断嵌套(可通过`spin_lock_irqsave()`实现)。 -- **例外情况**: - 快速中断(FIQ,ARM架构)允许更高优先级中断抢占,但需手动保存更多寄存器。 - - -#### 2. **性能约束** -- **最小化执行时间**: - ISR应仅完成紧急操作(如ACK硬件中断),耗时任务通过软中断、工作队列(Workqueue)异步处理。例如,网卡驱动的ISR仅清理硬件队列并触发NAPI(New API)软中断。 -- **硬实时风险**: - 长时间ISR可能导致系统失去响应,违反硬实时系统的截止时间(Deadline)要求。 - - -#### 3. **重入性与并发问题** -- **单次执行保证**: - 同一ISR在单CPU系统上不可重入,需通过`spinlock`保护共享数据;SMP系统中可通过中断绑定(如`irqset_affinity()`)避免并发。 - ---- - - - -### 五、中断处理程序的典型应用场景 - -#### 1. **设备驱动中的中断处理** -- **示例:字符设备读取** - ```c - // 简化的伪代码 - void serial_isr() { - char data = read_register(UART_RBR); // 读取接收缓冲区 - if (data == '\n') { - wake_up_interruptible(&tty_wait); // 唤醒等待队列 - } - schedule_work(&process_data_work); // 调度下半部处理 - } - ``` - 此ISR仅完成数据读取和唤醒操作,实际数据处理由工作队列异步执行。 - - -#### 2. **时钟中断与时基管理** -- **周期性时钟中断**: - 每个CPU核心的本地定时器(Local APIC Timer)定期触发时钟中断(如每10ms一次),更新`jiffies`计数器,驱动进程调度和时间片管理。 - - -#### 3. **异常处理与错误恢复** -- **缺页异常(Page Fault)**: - 当进程访问未映射虚拟地址时,触发#PF异常,ISR调用`do_page_fault()`分配物理页框并建立页表映射,实现虚拟内存按需加载。 - ---- - - - -### 六、常见误区与纠正 - -#### 1. **误区:ISR可以睡眠** -- **错误场景**: - 在ISR中调用`copy_from_user()`(可能引发缺页)或`kmalloc()`(GFP_KERNEL分配标志)。 -- **后果**: - 内核会触发`BUG_ON(in_interrupt())`并崩溃,因睡眠操作需切换到进程上下文。 -- **正确做法**: - 使用原子上下文兼容的API(如`kmalloc(..., GFP_ATOMIC)`),或通过工作队列延迟操作。 - - -#### 2. **误区:中断号与IRQ编号等价** -- **历史差异**: - 在x86 PIC模式下,IRQ0-15对应中断号32-47(因前32个保留给异常),而APIC模式下中断号可动态配置。 -- **现代系统**: - ACPI MADT表描述了GSI(Global System Interrupt)到APIC ID/Vector的映射,需通过`irq_desc`动态管理。 - - -#### 3. **误区:所有中断都需立即处理** -- **正确实践**: - 对于高频率中断(如网卡收包),采用**NAPI机制**(New API)结合轮询与中断,减少上下文切换开销。例如,Linux的`net_rx_action`软中断处理网络包。 - ---- - - - -### 七、总结:中断处理程序的地位与作用 -| 维度 | 地位与作用 | -|--------------------|----------------------------------------------------------------------------| -| **系统稳定性保障** | 作为硬件事件与操作系统之间的第一道接口,确保异步事件(如I/O完成、错误)及时响应。 | -| **并发控制基础** | 通过中断屏蔽、自旋锁等机制,协调硬件与CPU的并发访问,防止数据竞争。 | -| **资源管理核心** | 触发DMA完成、内存分配、进程唤醒等关键资源操作,支撑虚拟内存、进程调度等子系统。 | -| **性能瓶颈点** | 不当的ISR设计(如过长执行时间)会导致系统吞吐量下降,需严格遵循“上半部-下半部”分离原则。 | diff --git "a/src/site/notes/notes/408/\344\270\273\345\255\230\345\222\214\345\244\226\345\255\230\347\232\204\346\225\260\346\215\256\344\272\244\346\215\242\345\215\225\344\275\215.md" "b/src/site/notes/notes/408/\344\270\273\345\255\230\345\222\214\345\244\226\345\255\230\347\232\204\346\225\260\346\215\256\344\272\244\346\215\242\345\215\225\344\275\215.md" deleted file mode 100644 index 8942728..0000000 --- "a/src/site/notes/notes/408/\344\270\273\345\255\230\345\222\214\345\244\226\345\255\230\347\232\204\346\225\260\346\215\256\344\272\244\346\215\242\345\215\225\344\275\215.md" +++ /dev/null @@ -1,54 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/主存和外存的数据交换单位","permalink":"/408/主存和外存的数据交换单位/"} ---- - - - -### 1. 主存 (Main Memory) 的访问单位 - -- **从CPU(硬件)的角度看:字节 (Byte) 或 字 (Word)** - - - **可寻址的最小单位是“字节 (Byte)”** - - - **CPU进行数据存取的单位通常是“字 (Word)”**。 - - ---- - -### 2. 外存 (External Storage) 的访问单位 - -- **从硬件(磁盘驱动器)的角度看:扇区 (Sector)** - - - 磁盘上的数据在物理上被划分为一个个大小固定的**扇区**。扇区是磁盘可以进行读/写的**最小物理单位**。典型的扇区大小为512字节或4KB。磁盘控制器一次只能读/写整数个扇区,无法只读一个扇区中的某几个字节。 - -- **从操作系统(文件系统)的角度看:块 (Block) 或 簇 (Cluster)** - - - 操作系统为了方便管理,屏蔽掉底层硬件的细节,它将磁盘空间在逻辑上划分为一个个的**“块”**(在UNIX/Linux中称为Block)或**“簇”**(在Windows中称为Cluster)。 - - - 一个块通常由**一个或多个连续的扇区**组成(块的大小是扇区大小的整数倍,且通常是2的幂,如1KB, 2KB, 4KB)。 - - - 操作系统对磁盘进行I/O操作(文件读写)的**基本单位是“块”**。即使你只想读取文件中的1个字节,操作系统也必须将包含这个字节的**整个块**从磁盘读入到内存的缓冲区中,然后再从中提取出你需要的字节。同理,写数据也是以块为单位。 - -- **为什么这么做?** - - - **提高效率**: 磁盘I/O的寻道和旋转延迟非常高,远大于数据传输时间。如果每次只读写一个字节,那绝大部分时间都将浪费在寻道和旋转上。一次性读写一个较大的数据块,可以大大摊销这些机械延迟的成本,提高I/O吞吐率。 - - - **简化管理**: 以块为单位进行地址映射和空间管理,比以字节或扇区为单位要简单得多,可以减少文件系统元数据(如FAT表、i-node)的大小。 - - ---- - -### 总结与对比表格 - -|存储介质|硬件/物理访问单位|操作系统/逻辑访问单位| -|---|---|---| -|**主存 (内存)**|**字节 (Byte)** 是最小可寻址单位。 **字 (Word)** 是CPU一次操作传送的数据单位。|通常与硬件层面一致,操作系统可以直接按**字节**或**字**进行内存分配和访问。| -|**外存 (磁盘)**|**扇区 (Sector)** 是磁盘控制器进行读/写的最小物理单位。|**块 (Block) / 簇 (Cluster)** 是文件系统进行I/O操作的最小逻辑单位。| - -**核心要点**: - -- **CPU可以直接访问主存的任一字节。** - -- **CPU不能直接访问外存,必须通过操作系统发出I/O请求。** - -- **操作系统对外存的访问是成块进行的,无法只读/写一个字节或一个扇区。** \ No newline at end of file diff --git "a/src/site/notes/notes/408/\344\275\217\345\235\200\345\206\263\345\256\232\345\221\275\350\277\220\357\274\232\344\270\272\344\273\200\344\271\210\344\275\240\347\232\204\345\217\230\351\207\217\350\246\201\350\242\253\345\206\205\345\255\230\350\256\277\351\227\256\344\270\244\346\254\241\346\211\215\350\202\257\345\207\272\351\227\250\357\274\237\360\237\230\255.md" "b/src/site/notes/notes/408/\344\275\217\345\235\200\345\206\263\345\256\232\345\221\275\350\277\220\357\274\232\344\270\272\344\273\200\344\271\210\344\275\240\347\232\204\345\217\230\351\207\217\350\246\201\350\242\253\345\206\205\345\255\230\350\256\277\351\227\256\344\270\244\346\254\241\346\211\215\350\202\257\345\207\272\351\227\250\357\274\237\360\237\230\255.md" deleted file mode 100644 index d498efb..0000000 --- "a/src/site/notes/notes/408/\344\275\217\345\235\200\345\206\263\345\256\232\345\221\275\350\277\220\357\274\232\344\270\272\344\273\200\344\271\210\344\275\240\347\232\204\345\217\230\351\207\217\350\246\201\350\242\253\345\206\205\345\255\230\350\256\277\351\227\256\344\270\244\346\254\241\346\211\215\350\202\257\345\207\272\351\227\250\357\274\237\360\237\230\255.md" +++ /dev/null @@ -1,65 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/住址决定命运:为什么你的变量要被内存访问两次才肯出门?😭","permalink":"/408/住址决定命运:为什么你的变量要被内存访问两次才肯出门?😭/"} ---- - - -### 核心问题解答:为何地址会影响存取周期? - -主存地址之所以影响存取周期数,是因为现代CPU与内存之间的数据交换并非一个字节一个字节地进行,而是一次取一个固定大小的“数据块”;如果一个变量的存放位置刚好跨越了两个“数据块”的边界,那么即使这个变量本身很小,也必须分两次访问才能把它完整地取出来。 - -这个现象,在计算机体系结构中被称为**数据对齐(Data Alignment)**问题。 - ---- - -### 分析 - - -#### 第一步:分析硬件配置 - -- **存储器总线宽度是64位**:这意味着CPU与主存之间的数据通路是64位宽,即8个字节(8B)。可以把这条通路想象成一条8车道并排的高速公路,**一次数据传输(一个总线周期),最多可以运送8个字节的数据**。 - -- **内存条由8个16M x 8位的DRAM芯片构成16M x 64位的内存**:这是典型的**位扩展**设计。8个8位的芯片并联工作,每个芯片贡献8位,共同组成一个64位的数据字。当CPU访问内存时,这8个芯片会被同时选中,同时工作,一次性提供64位的数据。 - -- **支持突发传送方式(Burst Mode)**:这是一种高效的数据传输模式。当CPU需要读取一块连续数据时(比如一个缓存行),它只需给出首地址,内存系统就能自动地、连续地传输后续的数据块,而无需CPU为每个数据块都发出一次读命令。 - - -从以上配置我们可以得出一个至关重要的结论:这个内存系统的**存取粒度**或**访问基本单位**是**64位(8字节)**。它在设计上就是为了高效地进行8字节对齐的访问。内存地址可以被看作是一系列连续的、8字节大小的“格子”。 - -#### 第二步:剖析变量x和y的“住址”问题 - -1. **分析变量x (double)** - - - **大小**:`double`类型通常占用**8个字节**。 - - - **地址**:`2026 0000H`。 - - - **对齐分析**:这个地址的最后一位是`0`,可以被8整除。这意味着变量x的起始地址**恰好是一个8字节数据块的起始地址**。它完整地、不多不少地占据了从`2026 0000H`到`2026 0007H`这一个8字节的“格子”。 - - - **存取过程**:CPU只需要发起一次对`2026 0000H`地址的读操作,内存系统就能在一个存取周期内,通过64位总线将这完整的8个字节一次性传送给CPU。 - - - **结论**:因此,**读取变量x只需要一个存取周期**。 - -2. **分析变量y (int)** - - - **大小**:`int`类型通常占用**4个字节**。 - - - **地址**:`2026 1006H`。 - - - **对齐分析**:这个地址不能被8整除,甚至不能被4整除。让我们看看它在8字节的“格子”里是怎么存放的: - - - 我们知道内存的“格子”是从`...0H`和`...8H`开始的。地址`2026 1006H`属于从`2026 1000H`到`2026 1007H`这个格子。 - - - 变量y占用的4个字节地址是:`2026 1006H`, `2026 1007H`, `2026 1008H`, `2026 1009H`。 - - - **问题出现了!** 它的前2个字节(`...1006H`, `...1007H`)位于`2026 1000H`开始的内存块中,而后2个字节(`...1008H`, `...1009H`)则位于下一个内存块(从`2026 1008H`开始)中。 - - - 这种情况,我们称之为**“跨界存储”**或**“非对齐访问”**。 - - - **存取过程**:由于变量y的数据被分割在了两个不同的8字节对齐块中,CPU无法通过一次读操作就获取完整的数据。它必须: - - 1. **第一次存取**:发起对`2026 1000H`地址所在块的读取,得到y的前半部分。 - - 2. **第二次存取**:发起对`2026 1008H`地址所在块的读取,得到y的后半部分。 - - - **结论**:因此,**“读取变量y需要两个存取周期”是正确的**。 - diff --git "a/src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\350\241\250\345\275\223\344\270\255\347\232\204\350\241\250\351\241\271\345\222\214\351\241\265\350\241\250\347\232\204\351\241\265\347\233\256\345\275\225\351\241\271\347\232\204\351\200\273\350\276\221\345\234\260\345\235\200\347\273\223\346\236\204\347\273\204\346\210\220\360\237\230\241.md" "b/src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\350\241\250\345\275\223\344\270\255\347\232\204\350\241\250\351\241\271\345\222\214\351\241\265\350\241\250\347\232\204\351\241\265\347\233\256\345\275\225\351\241\271\347\232\204\351\200\273\350\276\221\345\234\260\345\235\200\347\273\223\346\236\204\347\273\204\346\210\220\360\237\230\241.md" deleted file mode 100644 index fc72f1e..0000000 --- "a/src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\350\241\250\345\275\223\344\270\255\347\232\204\350\241\250\351\241\271\345\222\214\351\241\265\350\241\250\347\232\204\351\241\265\347\233\256\345\275\225\351\241\271\347\232\204\351\200\273\350\276\221\345\234\260\345\235\200\347\273\223\346\236\204\347\273\204\346\210\220\360\237\230\241.md" +++ /dev/null @@ -1,216 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/区分页目录表当中的表项和页表的页目录项的逻辑地址结构组成😡","permalink":"/408/区分页目录表当中的表项和页表的页目录项的逻辑地址结构组成😡/"} ---- - - -### **场景设定** - -- **体系结构**: 32位系统。 - -- **内存管理**: 二级页式存储管理。 - -- **页面大小**: 4KB (212 字节)。 - -- **表项大小**: 页目录项(PDE) 和 页表项(PTE) 都是4字节(32位)。 - -- **已知条件**: - - - 当前进程的**页目录表基地址寄存器(PDTR)** 的值为 `0x00100000` (页目录表存放在这个物理地址开始的地方)。 - - - 我们需要转换一个逻辑地址:`0x016843C5`。 - - ---- - -### **第一步:逻辑地址的结构分解** - -首先,我们必须把逻辑地址 `0x016843C5` 按照二级分页的结构进行拆分。 - -1. 转换为二进制: - - 0x016843C5 = 0000 0001 0110 1000 0100 0011 1100 0101 - -2. 按结构拆分: - - 根据我们之前定义的结构 (p1: 10位, p2: 10位, d: 12位) 进行切分。 - - - **一级页目录号 (p1)**: `0000 0001 01` (二进制) = `5` (十进制) - - - **二级页表号 (p2)**: `10 1000 0100` (二进制) = `644` (十进制) - - - **页内偏移 (d)**: `0011 1100 0101` (二进制) = `0x3C5` (十六进制) = `965` (十进制) - - -我们可以用一个表格清晰地展示这个分解结果: - -#### **表格一:逻辑地址 `0x016843C5` 的结构** - -|部分名称|二进制表示|十六进制表示|十进制值|作用| -|---|---|---|---|---| -|**一级页目录号 (p1)**|`0000 0001 01`|`0x005`|5|用于在**页目录表**中查找PDE| -|**二级页表号 (p2)**|`10 1000 0100`|`0x284`|644|用于在**二级页表**中查找PTE| -|**页内偏移 (d)**|`0011 1100 0101`|`0x3C5`|965|最终在物理页帧内的偏移地址| - ---- - -### **第二步:查找页目录项 (PDE)** - -现在,CPU的内存管理单元(MMU)开始工作。 - -1. **定位页目录表**: 从**页目录表基地址寄存器(PDTR)** 中获取页目录表的物理基地址:`0x00100000`。 - -2. **计算PDE地址**: - - - 我们要找的PDE是页目录表中的第 `p1` (`=5`) 项。 - - - 每个PDE大小为4字节。 - - - 所以,目标PDE的物理地址 = 基地址 + 索引 * 表项大小 - - = 0x00100000 + 5 * 4 - - = 0x00100014 - -3. 获取PDE内容: - - MMU从物理地址 0x00100014 处读取到4个字节的PDE内容。我们假设读到的内容是 0x00099067。 - - -让我们用表格来展示这张“页目录表”中我们关心的这一项: - -#### **表格二:页目录表中的第5项 (PDE)** - -|物理地址|索引 (p1)|PDE内容 (十六进制)|PDE内容 (二进制)| -|---|---|---|---| -|`0x00100014`|5|`0x00099067`|`0000 0000 0000 1001 1001 0000 0110 0111`| - -**解读这个PDE (`0x00099067`)**: - -- **页表的物理基地址 (高20位)**: `0x00099`。这意味着我们需要的二级页表,存放在物理页帧号为 `0x00099` 的地方。其物理基地址是 `0x00099000`。 - -- **存在位 P (bit 0)**: `1`,表示这个二级页表在内存中,有效。 - -- 其他控制位... - - ---- - -### **第三步:查找页表项 (PTE)** - -现在我们已经拿到了二级页表的“指针”,继续寻址。 - -1. **定位二级页表**: 根据上一步PDE提供的信息,我们知道二级页表的物理基地址是 `0x00099000`。 - -2. **计算PTE地址**: - - - 我们要找的PTE是这张二级页表中的第 `p2` (`=644`) 项。 - - - 每个PTE大小为4字节。 - - - 所以,目标PTE的物理地址 = 基地址 + 索引 * 表项大小 - - = 0x00099000 + 644 * 4 - - = 0x00099000 + 2576 (十进制) - - = 0x00099000 + 0xA10 (十六进制) - - = 0x00099A10 - -3. 获取PTE内容: - - MMU从物理地址 0x00099A10 处读取到4个字节的PTE内容。我们假设读到的内容是 0x00785067。 - - -#### **表格三:二级页表中的第644项 (PTE)** - -|物理地址|索引 (p2)|PTE内容 (十六进制)|PTE内容 (二进制)| -|---|---|---|---| -|`0x00099A10`|644|`0x00785067`|`0000 0000 0111 1000 0101 0000 0110 0111`| - -**解读这个PTE (`0x00785067`)**: - -- **物理页帧号 (高20位)**: `0x00785`。这**就是我们最终要找的数据所在的物理页帧号**!它的物理基地址是 `0x00785000`。 - -- **存在位 P (bit 0)**: `1`,表示这个数据页在内存中,有效。 - -- 其他控制位... - - ---- - -### **第四步:形成最终物理地址** - -寻宝图的最后一步,拼接最终地址。 - -1. **获取物理页基地址**: 从上一步的PTE中,我们得到了物理页帧号 `0x00785`,其对应的物理基地址是 `0x00785000`。 - -2. **获取页内偏移**: 从第一步分解逻辑地址时,我们得到了页内偏移 `d`,其值为 `0x3C5`。 - -3. **计算最终物理地址**: - - - 最终物理地址 = 物理页基地址 + 页内偏移 - - = 0x00785000 + 0x3C5 - - = 0x007853C5 - - -**结论**:逻辑地址 `0x016843C5` 经过二级页表的转换,最终映射到了物理地址 `0x007853C5`。 - -### **问题一:“页目录项都是20bit吗?”** - -**答:不是。这是一个非常普遍的误解,但非常关键。** - -一个**页目录项 (PDE)** 本身是一个完整的数据结构,在32位系统中,它通常是**32位(即4字节)**。 - -你提到的“20bit”是**页目录项内部一个非常重要的字段**的长度,即**“页表的物理基地址”**字段。 - -让我们把一个32位的页目录项(PDE)想象成一个信息卡片,这张卡片被分成了两部分: - -1. **核心信息区(20位)**: 用来存放它所指向的那个**二级页表的物理基地址**(或者说是物理页帧号)。 - -2. **控制/状态信息区(12位)**: 用来存放各种标志位,如**存在位(P)、读写权限位(R/W)、用户/超级用户位(U/S)**等。 - - -所以,完整的关系是: - -一个32位的页目录项 = 20位的页表基地址 + 12位的控制位 - -这个结构与页表项(PTE)非常相似,PTE也是一个32位的项,由20位的物理页帧号和12位的控制位组成。 - -为什么是20位? - -因为在我们的例子中(32位系统,4KB页面),物理地址也是32位。一个物理页帧的地址由于是4K对齐的,所以其低12位必然全为0。因此,我们只需要存储其高20位(即物理页帧号PFN)就足以定位这个页帧了。20位页帧号 + 12个0 = 32位物理基地址。 - ---- - -### **问题二:“页目录项应该不包括页内偏移量吧?”** - -**答:你说的完全正确!** - -**页目录项 (PDE) 和页表项 (PTE) 的结构中,绝对不包含页内偏移量。** - -原因在于它们在地址转换过程中扮演的角色是完全不同的: - -1. 页目录项(PDE)和页表项(PTE)的使命: - - 它们的唯一使命是将逻辑地址中的“页号”部分翻译成物理地址中的“页帧号”部分。它们是“页到帧”的翻译官。 - -2. 页内偏移(d)的使命: - - 页内偏移量的使命是在已经找到的那个正确的物理页帧内部,定位到具体的字节地址。它是“帧内寻址”的导航员。 - - -**地址转换流程清晰地体现了这一点:** - -1. CPU拿出逻辑地址中的 `p1` 部分去查**页目录表**,找到对应的 **PDE**。 - -2. 从PDE中取出**页表的地址**。 - -3. CPU拿出逻辑地址中的 `p2` 部分去查这个**页表**,找到对应的 **PTE**。 - -4. 从PTE中取出**最终物理页帧的地址**。 - -5. **直到此时**,CPU才拿出逻辑地址中的 `d` (页内偏移) 部分,与上一步得到的物理页帧地址相加,形成最终的物理地址。 - \ No newline at end of file diff --git "a/src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\351\241\271\345\222\214\351\241\265\350\241\250\351\241\271\347\232\204\347\273\223\346\236\204\360\237\230\241.md" "b/src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\351\241\271\345\222\214\351\241\265\350\241\250\351\241\271\347\232\204\347\273\223\346\236\204\360\237\230\241.md" deleted file mode 100644 index 58d9fd4..0000000 --- "a/src/site/notes/notes/408/\345\214\272\345\210\206\351\241\265\347\233\256\345\275\225\351\241\271\345\222\214\351\241\265\350\241\250\351\241\271\347\232\204\347\273\223\346\236\204\360\237\230\241.md" +++ /dev/null @@ -1,216 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/区分页目录表当中的表项和页表的页表项的逻辑地址结构组成😡","permalink":"/408/区分页目录表当中的表项和页表的页表项的逻辑地址结构组成😡/"} ---- - - -### **场景设定** - -- **体系结构**: 32位系统。 - -- **内存管理**: 二级页式存储管理。 - -- **页面大小**: 4KB (212 字节)。 - -- **表项大小**: 页目录项(PDE) 和 页表项(PTE) 都是4字节(32位)。 - -- **已知条件**: - - - 当前进程的**页目录表基地址寄存器(PDTR)** 的值为 `0x00100000` (页目录表存放在这个物理地址开始的地方)。 - - - 我们需要转换一个逻辑地址:`0x016843C5`。 - - ---- - -### **第一步:逻辑地址的结构分解** - -首先,我们必须把逻辑地址 `0x016843C5` 按照二级分页的结构进行拆分。 - -1. 转换为二进制: - - 0x016843C5 = 0000 0001 0110 1000 0100 0011 1100 0101 - -2. 按结构拆分: - - 根据我们之前定义的结构 (p1: 10位, p2: 10位, d: 12位) 进行切分。 - - - **一级页目录号 (p1)**: `0000 0001 01` (二进制) = `5` (十进制) - - - **二级页表号 (p2)**: `10 1000 0100` (二进制) = `644` (十进制) - - - **页内偏移 (d)**: `0011 1100 0101` (二进制) = `0x3C5` (十六进制) = `965` (十进制) - - -我们可以用一个表格清晰地展示这个分解结果: - -#### **表格一:逻辑地址 `0x016843C5` 的结构** - -|部分名称|二进制表示|十六进制表示|十进制值|作用| -|---|---|---|---|---| -|**一级页目录号 (p1)**|`0000 0001 01`|`0x005`|5|用于在**页目录表**中查找PDE| -|**二级页表号 (p2)**|`10 1000 0100`|`0x284`|644|用于在**二级页表**中查找PTE| -|**页内偏移 (d)**|`0011 1100 0101`|`0x3C5`|965|最终在物理页帧内的偏移地址| - ---- - -### **第二步:查找页目录项 (PDE)** - -现在,CPU的内存管理单元(MMU)开始工作。 - -1. **定位页目录表**: 从**页目录表基地址寄存器(PDTR)** 中获取页目录表的物理基地址:`0x00100000`。 - -2. **计算PDE地址**: - - - 我们要找的PDE是页目录表中的第 `p1` (`=5`) 项。 - - - 每个PDE大小为4字节。 - - - 所以,目标PDE的物理地址 = 基地址 + 索引 * 表项大小 - - = 0x00100000 + 5 * 4 - - = 0x00100014 - -3. 获取PDE内容: - - MMU从物理地址 0x00100014 处读取到4个字节的PDE内容。我们假设读到的内容是 0x00099067。 - - -让我们用表格来展示这张“页目录表”中我们关心的这一项: - -#### **表格二:页目录表中的第5项 (PDE)** - -|物理地址|索引 (p1)|PDE内容 (十六进制)|PDE内容 (二进制)| -|---|---|---|---| -|`0x00100014`|5|`0x00099067`|`0000 0000 0000 1001 1001 0000 0110 0111`| - -**解读这个PDE (`0x00099067`)**: - -- **页表的物理基地址 (高20位)**: `0x00099`。这意味着我们需要的二级页表,存放在物理页帧号为 `0x00099` 的地方。其物理基地址是 `0x00099000`。 - -- **存在位 P (bit 0)**: `1`,表示这个二级页表在内存中,有效。 - -- 其他控制位... - - ---- - -### **第三步:查找页表项 (PTE)** - -现在我们已经拿到了二级页表的“指针”,继续寻址。 - -1. **定位二级页表**: 根据上一步PDE提供的信息,我们知道二级页表的物理基地址是 `0x00099000`。 - -2. **计算PTE地址**: - - - 我们要找的PTE是这张二级页表中的第 `p2` (`=644`) 项。 - - - 每个PTE大小为4字节。 - - - 所以,目标PTE的物理地址 = 基地址 + 索引 * 表项大小 - - = 0x00099000 + 644 * 4 - - = 0x00099000 + 2576 (十进制) - - = 0x00099000 + 0xA10 (十六进制) - - = 0x00099A10 - -3. 获取PTE内容: - - MMU从物理地址 0x00099A10 处读取到4个字节的PTE内容。我们假设读到的内容是 0x00785067。 - - -#### **表格三:二级页表中的第644项 (PTE)** - -|物理地址|索引 (p2)|PTE内容 (十六进制)|PTE内容 (二进制)| -|---|---|---|---| -|`0x00099A10`|644|`0x00785067`|`0000 0000 0111 1000 0101 0000 0110 0111`| - -**解读这个PTE (`0x00785067`)**: - -- **物理页帧号 (高20位)**: `0x00785`。这**就是我们最终要找的数据所在的物理页帧号**!它的物理基地址是 `0x00785000`。 - -- **存在位 P (bit 0)**: `1`,表示这个数据页在内存中,有效。 - -- 其他控制位... - - ---- - -### **第四步:形成最终物理地址** - -寻宝图的最后一步,拼接最终地址。 - -1. **获取物理页基地址**: 从上一步的PTE中,我们得到了物理页帧号 `0x00785`,其对应的物理基地址是 `0x00785000`。 - -2. **获取页内偏移**: 从第一步分解逻辑地址时,我们得到了页内偏移 `d`,其值为 `0x3C5`。 - -3. **计算最终物理地址**: - - - 最终物理地址 = 物理页基地址 + 页内偏移 - - = 0x00785000 + 0x3C5 - - = 0x007853C5 - - -**结论**:逻辑地址 `0x016843C5` 经过二级页表的转换,最终映射到了物理地址 `0x007853C5`。 - -### **问题一:“页目录项都是20bit吗?”** - -**答:不是。这是一个非常普遍的误解,但非常关键。** - -一个**页目录项 (PDE)** 本身是一个完整的数据结构,在32位系统中,它通常是**32位(即4字节)**。 - -你提到的“20bit”是**页目录项内部一个非常重要的字段**的长度,即**“页表的物理基地址”**字段。 - -让我们把一个32位的页目录项(PDE)想象成一个信息卡片,这张卡片被分成了两部分: - -1. **核心信息区(20位)**: 用来存放它所指向的那个**二级页表的物理基地址**(或者说是物理页帧号)。 - -2. **控制/状态信息区(12位)**: 用来存放各种标志位,如**存在位(P)、读写权限位(R/W)、用户/超级用户位(U/S)**等。 - - -所以,完整的关系是: - -一个32位的页目录项 = 20位的页表基地址 + 12位的控制位 - -这个结构与页表项(PTE)非常相似,PTE也是一个32位的项,由20位的物理页帧号和12位的控制位组成。 - -为什么是20位? - -因为在我们的例子中(32位系统,4KB页面),物理地址也是32位。一个物理页帧的地址由于是4K对齐的,所以其低12位必然全为0。因此,我们只需要存储其高20位(即物理页帧号PFN)就足以定位这个页帧了。20位页帧号 + 12个0 = 32位物理基地址。 - ---- - -### **问题二:“页目录项应该不包括页内偏移量吧?”** - -**答:你说的完全正确!** - -**页目录项 (PDE) 和页表项 (PTE) 的结构中,绝对不包含页内偏移量。** - -原因在于它们在地址转换过程中扮演的角色是完全不同的: - -1. 页目录项(PDE)和页表项(PTE)的使命: - - 它们的唯一使命是将逻辑地址中的“页号”部分翻译成物理地址中的“页帧号”部分。它们是“页到帧”的翻译官。 - -2. 页内偏移(d)的使命: - - 页内偏移量的使命是在已经找到的那个正确的物理页帧内部,定位到具体的字节地址。它是“帧内寻址”的导航员。 - - -**地址转换流程清晰地体现了这一点:** - -1. CPU拿出逻辑地址中的 `p1` 部分去查**页目录表**,找到对应的 **PDE**。 - -2. 从PDE中取出**页表的地址**。 - -3. CPU拿出逻辑地址中的 `p2` 部分去查这个**页表**,找到对应的 **PTE**。 - -4. 从PTE中取出**最终物理页帧的地址**。 - -5. **直到此时**,CPU才拿出逻辑地址中的 `d` (页内偏移) 部分,与上一步得到的物理页帧地址相加,形成最终的物理地址。 - diff --git "a/src/site/notes/notes/408/\345\217\252\344\274\232\350\222\231\345\244\264\347\256\227\346\225\260\347\232\204\345\274\261\346\231\272CPU\346\230\257\346\200\216\344\271\210\350\257\206\345\210\253\345\207\272\345\217\230\351\207\217\347\261\273\345\236\213\347\232\204\345\221\242\360\237\244\224.md" "b/src/site/notes/notes/408/\345\217\252\344\274\232\350\222\231\345\244\264\347\256\227\346\225\260\347\232\204\345\274\261\346\231\272CPU\346\230\257\346\200\216\344\271\210\350\257\206\345\210\253\345\207\272\345\217\230\351\207\217\347\261\273\345\236\213\347\232\204\345\221\242\360\237\244\224.md" deleted file mode 100644 index bb84bbe..0000000 --- "a/src/site/notes/notes/408/\345\217\252\344\274\232\350\222\231\345\244\264\347\256\227\346\225\260\347\232\204\345\274\261\346\231\272CPU\346\230\257\346\200\216\344\271\210\350\257\206\345\210\253\345\207\272\345\217\230\351\207\217\347\261\273\345\236\213\347\232\204\345\221\242\360\237\244\224.md" +++ /dev/null @@ -1,103 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/只会蒙头算数的弱智CPU是怎么识别出变量类型的呢🤔","permalink":"/408/只会蒙头算数的弱智CPU是怎么识别出变量类型的呢🤔/"} ---- - - -在写代码的时候,我们清晰地定义了 `int`、`unsigned int`、`float` 等各种变量类型。但我们又被告知,计算机的CPU是一个只认识0和1的“铁憨憨”,它处理的所有东西本质上都是一串二进制数。 - -这就带来一个悖论:一个连正负号都要用`0`和`1`来表示的“笨蛋”CPU,是怎么在我们执行 `x >> 2` 这类操作时,神奇地知道该对有符号数执行“算术移位”,而对无符号数执行“逻辑移位”的呢? - -难道CPU在运算前还会偷偷检查一下变量的“身份证”? - -**剧透一下:并不会。真正的“魔法”,发生在代码运行之前的编译阶段。** - -为了揭开这个秘密,让我们跟随一个具体的例子,走完它从高级语言到CPU执行的全过程。 - -#### **第一幕:我们的C语言剧本** - -```c -#include - -int main() { - // 我们的两个主角,一个有符号,一个无符号 - // 在8位补码中,-8 是 11111000 - // 在8位无符号数中,248 是 11111000 - signed char signed_num = -8; - unsigned char unsigned_num = 248; - - printf("原始二进制: 11111000\n\n"); - - // 1. 对有符号数进行右移 (我们期望得到 -4) - signed char signed_result = signed_num >> 1; - - // 2. 对无符号数进行右移 (我们期望得到 124) - unsigned char unsigned_result = unsigned_num >> 1; - - // 让我们看看结果 - printf("有符号数: %d >> 1 = %d\n", signed_num, signed_result); - printf(" - 结果二进制: ...11111100\n\n"); - - printf("无符号数: %u >> 1 = %u\n", unsigned_num, unsigned_result); - printf(" - 结果二进制: ...01111100\n\n"); - - return 0; -} -``` - -这段代码的意图很明确:虽然`-8`和`248`的8位二进制表示都是`11111000`,但我们期望得到完全不同的移位结果。 - -#### **第二幕:编译器的“魔法”翻译** - -当编译器读到这份代码时,它会做以下事情: - -1. **记录身份**:它看到 `signed char signed_num`,就在自己的小本本(符号表)上记下:“`signed_num` 这个变量,之后所有操作都要按**有符号数**的规矩来办!”。看到 `unsigned char unsigned_num`,它也记下:“`unsigned_num` 要按**无符号数**的规矩来!” - -2. **选择工具**:当编译器看到 `signed_num >> 1` 时,它会想:“嗯,这是对有符号数的右移,按照C语言标准,这代表除以2,我应该使用**算术右移**指令。” 于是,它生成了一条 `SAR` (Shift Arithmetic Right) 指令。 - -3. 当编译器看到 `unsigned_num >> 1` 时,它会想:“哦,这是对无符号数的右移,这只是一个纯粹的位操作,我应该使用**逻辑右移**指令。” 于是,它生成了一条 `SHR` (Shift Logical Right) 指令。 - - -编译完成后,我们得到一个可执行文件。在这个文件里,`signed` 和 `unsigned` 这些类型信息**已经消失了**。它们的作用已经完成,就像建筑图纸在房子盖好后就可以收起来一样。留下的,是给CPU的具体施工指令。 - -**C代码到汇编指令的翻译(简化示意):** - -代码段 - -``` -; --- 处理有符号数的部分 --- -mov al, -8 ; 将-8的二进制表示(11110000)放入AL寄存器 -sar al, 1 ; 对AL寄存器执行【算术右移】1位 - ; 结果al变为 11111000 -> 11111100 (-4) - -; --- 处理无符号数的部分 --- -mov bl, 248 ; 将248的二进制表示(11110000)放入BL寄存器 -shr bl, 1 ; 对BL寄存器执行【逻辑右移】1位 - ; 结果bl变为 11110000 -> 01111000 (120) - ; (注:这里汇编结果与C语言例子有细微差异,为简化说明) -``` - -_(注:真实编译会更复杂,这里是为了清晰说明核心思想。)_ - -#### **第三幕:CPU的“傻瓜式”执行** - -最后,轮到我们的“笨蛋”CPU登场了。CPU不关心变量的过去,也不关心它的未来,它只活在当下,忠实地执行收到的每一条指令。 - -1. CPU取到指令 `sar al, 1`。它的控制单元解码后,激活了内部的“算术右移”电路。这个电路的物理设计就是:把所有位向右移动,并在最高位**复制原来的符号位**。CPU不假思索地完成了这个操作。 - -2. 过了一会儿,CPU又取到指令 `shr bl, 1`。控制单元解码后,激活了另一组“逻辑右移”电路。这个电路的物理设计就是:把所有位向右移动,并在最高位**固定填充0**。CPU同样不假思索地完成了这个操作。 - - -看到了吗?CPU自始至终没有做任何“判断”。它只是一个高效的执行者,根据收到的**不同指令**(`SAR` vs `SHR`),调用了内部**不同功能**的电路,从而对完全相同的二进制串 `11110000` 产生了截然不同的结果。 - -### **结论:到底谁是“笨蛋”?** - -回到我们最初的问题:只会闷头算数的“笨蛋”CPU,如何“认识”变量类型? - -答案是:**它根本不认识,也不需要认识。** - -- **类型**,是高级语言提供给**程序员**和**编译器**沟通的“契约”。 - -- **编译器**,是遵守这份契约,将程序员的意图(比如“这是一个有符号数”)翻译成具体机器指令(比如“请使用算术移位”)的“智能翻译官”。 - -- **CPU**,则是这个体系中最底层的“高效工人”,它只负责执行收到的明确指令,不多问一句为什么。 - diff --git "a/src/site/notes/notes/408/\345\234\250cache\345\260\217\345\247\220\345\215\217\345\212\251\344\270\213\347\232\204CPU\350\256\277\345\255\230\345\205\250\346\265\201\347\250\213\360\237\244\224.md" "b/src/site/notes/notes/408/\345\234\250cache\345\260\217\345\247\220\345\215\217\345\212\251\344\270\213\347\232\204CPU\350\256\277\345\255\230\345\205\250\346\265\201\347\250\213\360\237\244\224.md" deleted file mode 100644 index 5eb7acf..0000000 --- "a/src/site/notes/notes/408/\345\234\250cache\345\260\217\345\247\220\345\215\217\345\212\251\344\270\213\347\232\204CPU\350\256\277\345\255\230\345\205\250\346\265\201\347\250\213\360\237\244\224.md" +++ /dev/null @@ -1,101 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/在cache小姐协助下的CPU访存全流程🤔","permalink":"/408/在cache小姐协助下的CPU访存全流程🤔/"} ---- - - -### 一、 带 Cache 的 CPU 访存完整流程 - -CPU执行一条访存指令(读或写)时,并不会直接与主存打交道,而是优先与Cache交互。整个流程如下: - -1. **CPU发出请求:** CPU产生一个内存地址(逻辑地址,经过MMU转换为物理地址),并连同读/写信号一起发送给Cache控制器。 - -2. **Cache命中判断 (Cache Hit / Miss):** 这是流程的关键分水岭。 - - - Cache控制器根据地址的**索引字段 (Index)** 定位到Cache中的某一个或某一组Cache行。 - - - 然后,比较地址的**标记字段 (Tag)** 与该Cache行中存储的标记是否一致。 - - - 同时,检查该Cache行的**有效位 (Valid Bit)** 是否为1。 - - - **如果** `标记匹配` 且 `有效位为1`,则判断为 **“Cache命中 (Cache Hit)”**。 - - - **否则**,即为 **“Cache缺失 (Cache Miss)”**。 - -3. **后续操作:** - - - **若命中 (Hit):** - - - **读命中:** Cache控制器直接从命中的Cache行中,根据地址的**块内偏移字段 (Offset)**,取出对应的字节或字,通过内部总线高速地传送给CPU。访存操作快速完成。 - - - **写命中:** 根据写策略(Write Policy)进行操作。 - - - **写直通 (Write-Through):** 同时写入Cache和主存。 - - - **写回 (Write-Back):** 只写入Cache,并将该Cache行标记为“脏”(Dirty)。该行的数据只有在被替换时,才会被写回主存。 - - - **若缺失 (Miss):** 系统将启动一套相对复杂的“缺失处理机制 (Miss Handling)”。 - - ---- - -### 二、 缓存缺失 (Cache Miss) 的处理机制 - -**如果Cache缺失,访问主存和更新Cache是同时完成的吗?** - -**不是同时完成的,这是一个有明确先后逻辑顺序的过程,但现代CPU通过优化技术使其部分操作可以并发执行,从而缩短延迟。** - -下面是标准的、逻辑上的处理步骤: - -#### 步骤 1:暂停CPU (Stall the CPU) - -当发生Cache Miss时,CPU无法立即获得它想要的数据,因此其执行流水线会**暂停 (Stall)**,等待数据从主存中取回。 - -#### 步骤 2:访问主存 (Access Main Memory) - -Cache控制器会接管后续工作,向主存控制器发出一个**读请求**。 - -- **关键点:** 这个读请求请求的**不是CPU最初需要的那个字**,而是包含了那个字在内的**一整个主存块 (Block/Line)**。这是利用了程序的**空间局部性原理**,即CPU访问了某个地址后,很可能在不久的将来访问其附近的地址。将整个块调入Cache可以提高后续访问的命中率。 - - -#### 步骤 3:数据从主存调往Cache (Block Transfer) - -主存响应请求,找到对应的数据块,通过系统总线将其传输给Cache控制器。 - -#### 步骤 4:数据交付与Cache更新 (重点!) - -这是最关键的一步,它直接回答了你的问题。当数据块从主存传输过来时,发生了两件事: - -- A. 将CPU所需的字直接送往CPU (CPU Forwarding / Critical Word First) - - 为了尽快地让CPU从暂停状态中恢复,大多数Cache控制器采用了**“读穿 (Read-Through)”或称“关键宇优先”技术。即在整个数据块还在从主存流向Cache的途中,一旦CPU最先需要的那个字到达了,Cache控制器就会立即将这个字直接“转发”给CPU**。这样CPU就可以继续执行,不必等待整个块完全写入Cache。 - -- B. 将整个数据块写入Cache (Cache Line Fill) - - 在将关键字发送给CPU的同时,或者之后,Cache控制器会执行以下操作来更新Cache: - - 1. **选择替换行:** 从Cache的对应组中,根据**替换算法 (Replacement Algorithm)**(如LRU, FIFO等)选择一个Cache行用于存放新调入的数据块。 - - 2. **处理脏块 (Dirty Block):** 如果被选中的替换行是“脏”的(即在写回策略下,该行数据被修改过),则必须**先将这一整行旧的“脏”数据写回主存**,这个过程称为“写回 (Write-Back)”。这会增加本次Cache Miss的处理时间(Miss Penalty)。 - - 3. **写入新块:** 将从主存调入的新数据块写入被选中的Cache行。 - - 4. **更新元数据:** 更新该Cache行的**标记 (Tag)** 和**有效位 (Valid Bit)**。 - - -#### 流程总结与回答 - -- **从逻辑上讲,是“先访问主存,再更新Cache”**。因为没有从主存取回数据,就无法进行更新。 - -- **从执行效率上讲,为了缩短CPU的等待时间,将数据送往CPU和将数据块写入Cache这两个动作是高度并发的**。CPU不必等到整个Cache行更新完毕,通过“关键宇优先”技术,它可以提前拿到需要的数据。 - ---- - -### 三、 考点分析与陷阱 - -1. **Cache缺失代价 (Miss Penalty):** 这是衡量Cache性能的重要指标,指的是从发生缺失到CPU获得数据所需的时间。其主要构成是**访问主存的时间**。 - -2. **写回策略的额外开销:** 考题中经常会涉及计算Cache缺失的代价。一个常见的陷阱是,如果替换策略选中了一个“脏块”,考生必须记得加上**将脏块写回主存的时间**。 - -3. **“关键宇优先”技术:** 这个概念是理解Cache效率的关键。它解释了为什么在Cache缺失后,CPU的停顿时间可以小于“主存访问时间 + Cache更新时间”的总和。 - -4. **数据块传输:** 一定要牢记,Cache和主存之间的数据交换单位是**块 (Block)**,而不是字 (Word)。这是Cache工作的基础,也是利用空间局部性原理的体现。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/\345\234\250\351\232\220\345\220\253\345\257\273\345\235\200\344\270\255\347\254\254\344\272\214\346\223\215\344\275\234\346\225\260\346\230\257\346\200\216\344\271\210\345\234\250ACC\345\275\223\344\270\255\351\207\221\345\261\213\350\227\217\345\250\207\345\221\242\360\237\244\224.md" "b/src/site/notes/notes/408/\345\234\250\351\232\220\345\220\253\345\257\273\345\235\200\344\270\255\347\254\254\344\272\214\346\223\215\344\275\234\346\225\260\346\230\257\346\200\216\344\271\210\345\234\250ACC\345\275\223\344\270\255\351\207\221\345\261\213\350\227\217\345\250\207\345\221\242\360\237\244\224.md" deleted file mode 100644 index 78b1e40..0000000 --- "a/src/site/notes/notes/408/\345\234\250\351\232\220\345\220\253\345\257\273\345\235\200\344\270\255\347\254\254\344\272\214\346\223\215\344\275\234\346\225\260\346\230\257\346\200\216\344\271\210\345\234\250ACC\345\275\223\344\270\255\351\207\221\345\261\213\350\227\217\345\250\207\345\221\242\360\237\244\224.md" +++ /dev/null @@ -1,90 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/在隐含寻址中第二操作数是怎么在ACC当中金屋藏娇呢🤔","permalink":"/408/在隐含寻址中第二操作数是怎么在ACC当中金屋藏娇呢🤔/"} ---- - - -### 一、 答案 - -累加器ACC中的数据,是在执行这条隐含寻址指令**之前**,由另一条加载指令(如 `LDA`)从内存或其它地方送入的。 - -### 二、 相关原理或操作过程的详细讲解 - -要理解ACC中的值“何时进去”,我们必须明白,一条独立的指令通常只完成一个原子性的操作。将数据从内存加载到ACC,和利用ACC中的数据进行运算,是两条不同的指令完成的。 - -这个过程通常分为两步,我们以一个典型的单累加器结构的CPU为例,计算表达式 `C = A + B` (假设A, B, C是内存地址)。 - -**第1步:加载操作数(让数据“进去”)** - -CPU首先需要执行一条**加载指令 (Load)**,把第一个操作数从内存地址 `A` 拿出来,送到累加器ACC中。 - -- **指令:** `LDA A` - - - `LDA` 是 "LoaD Accumulator" 的缩写,是操作码。 - - - `A` 是地址码,采用直接寻址方式,指明了操作数在主存中的位置。 - -- **执行过程:** - - 1. CPU读取并译码 `LDA A` 指令。 - - 2. CPU通过地址总线发送地址 `A`。 - - 3. 主存根据地址 `A` 找到对应的数据 `M(A)`。 - - 4. 主存将数据 `M(A)` 通过数据总线返回给CPU。 - - 5. CPU将接收到的数据 `M(A)` 打入(写入)到累加器ACC中。 - - -**至此,第一个操作数已经进入了ACC。这是对您“何时进去”这个问题的直接回答。** - -**第2步:执行隐含寻址指令** - -现在ACC中已经有了来自 `A` 的值,接下来CPU执行加法指令。 - -- **指令:** `ADD B` - - - `ADD` 是加法操作码。 - - - `B` 是地址码,采用直接寻址,指明了第二个操作数的位置。 - -- **执行过程:** - - 1. CPU读取并译码 `ADD B` 指令。操作码 `ADD` 在这种体系结构中**隐含约定**了: - - - 一个操作数已经存放在ACC中。 - - - 另一个操作数需要从指令的地址码部分获取。 - - - 运算结果将存回ACC中。 - - 2. CPU根据地址码 `B` 从主存中取出第二个操作数 `M(B)`。 - - 3. CPU内部的算术逻辑单元 (ALU) 将ACC的现存内容和从主存取出的 `M(B)` 相加。 - - 4. ALU将加法结果写回到ACC中,覆盖掉原来的值。 - - -**整个流程的伪代码与ACC状态变化:** - -| 汇编指令 | 操作解释 | ACC 内部值的变化 | -| ------- | ------------------------------------ | ----------------------------------------- | -| `LDA A` | Load Accumulator: 将内存地址A处的值加载到ACC。 | `ACC` <- `M(A)` | -| `ADD B` | Add: 将ACC的值与内存地址B处的值相加,结果存回ACC。 | `ACC` <- `(ACC)` + `M(B)` (即 `M(A)+M(B)`) | -| `STA C` | Store Accumulator: 将ACC的当前值存入内存地址C处。 | `ACC` 内容不变,`M(C)` <- `(ACC)` | - -上图中,`ACC = M[A]` 这个状态就是ACC“进去”数据的时间点。 - - -### 三、 边界情况、易错点、反例 - -- **纯粹的隐含寻址:** 有些指令甚至不包含任何地址码字段,它们的操作数完全是隐含的。 - - - **例:** `CMA` (Complement Accumulator,累加器取反)。这条指令只有一个操作码,它默认对ACC的内容进行按位取反,结果仍存回ACC。这里,源操作数和目的操作数都是ACC,且都被隐含了。 - - - **例:** 堆栈操作中的 `PUSH` 和 `POP` 指令。它们隐含使用了栈顶指针 `SP` 来作为内存操作的地址。例如 `PUSH AX`,指令只说了要把 `AX` 的内容入栈,但入到哪里去呢?这个地址是由 `SP` 寄存器隐含提供的。 - -- **易错点:** 将单地址指令与隐含寻址划等号。 - - - 单地址指令 `OP Addr`(如 `ADD B`)确实使用了隐含寻址(隐含了ACC),但它的另一个操作数 `M(B)` 却采用了**直接寻址**。一条指令可以组合多种寻址方式。考题若问“ADD B指令使用了哪种寻址方式?”,严谨的答案是“隐含寻址和直接寻址”。如果选项中只有其一,通常是考察其最核心的特征,即作为单地址指令对ACC的隐含使用。 - \ No newline at end of file diff --git "a/src/site/notes/notes/408/\346\200\216\344\271\210\345\214\272\345\210\206\345\217\214\350\203\236\350\203\216ISA\345\222\214\345\276\256\344\275\223\347\263\273\347\273\223\346\236\204\345\221\242\360\237\244\224.md" "b/src/site/notes/notes/408/\346\200\216\344\271\210\345\214\272\345\210\206\345\217\214\350\203\236\350\203\216ISA\345\222\214\345\276\256\344\275\223\347\263\273\347\273\223\346\236\204\345\221\242\360\237\244\224.md" deleted file mode 100644 index dd72495..0000000 --- "a/src/site/notes/notes/408/\346\200\216\344\271\210\345\214\272\345\210\206\345\217\214\350\203\236\350\203\216ISA\345\222\214\345\276\256\344\275\223\347\263\273\347\273\223\346\236\204\345\221\242\360\237\244\224.md" +++ /dev/null @@ -1,143 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/怎么区分双胞胎ISA和微体系结构呢🤔","permalink":"/408/怎么区分双胞胎ISA和微体系结构呢🤔/"} ---- - - -### 一、 答案 - -指令集体系结构(ISA)是软件能看到的“计算机能做什么”的规范契约,微体系结构是实现这套规范的硬件内部结构,而微程序结构则是实现微体系结构的一种具体技术手段。重点是:**对谁可见** - ---- - -### 二、 概念的官方定义与区分 - -#### 1. 指令集体系结构 (Instruction Set Architecture, ISA) - -- **定义:** ISA,又称指令集或机器体系结构,是计算机体系结构中与程序员(特别是汇编程序员和编译器开发者)相关的部分。它定义了软件如何与硬件进行交互的接口,规定了计算机**能做什么**。 - -- **核心内容(考点):** - - - **指令系统:** 操作码(Opcode)、指令格式、指令类型(数据传送、算术逻辑、控制、I/O等)。 - - - **寻址方式:** 如何找到操作数(立即、直接、间接、寄存器、隐含等)。 - - - **数据类型:** 机器能直接处理的数据类型(如整型、浮点型)及其格式。 - - - **寄存器定义:** 程序员可见的寄存器集合(如通用寄存器、程序计数器PC、状态寄存器PSW等)的数量、名称和功能。 - - - **存储体系:** 寻址空间大小、字节序(大端/小端)。 - - - **中断和异常处理机制**。 - -- **一句话理解:** ISA是计算机的“功能菜单”和“使用说明书”,它对程序员可见。比如,Intel的x86、ARM公司的ARM都是著名的ISA。 - - -#### 2. 微体系结构 (Microarchitecture) - -- **定义:** 微体系结构,又称计算机组织,是ISA的具体物理实现。它描述了为了实现ISA所规定的功能,计算机内部的硬件组件**如何组织、如何连接、如何运作**。它关心的是**如何把事做成、做得多快**。 - -- **核心内容(考点):** - - - **数据通路(Data Path):** 执行部件的构成,如ALU、寄存器文件(Register File)、内部总线、多路选择器(MUX)等。 - - - **控制器(Control Unit):** 如何产生控制信号来指挥数据通路中的各个部件按指令要求进行操作。 - - - **流水线(Pipeline):** 是否采用流水线技术,流水线的级数(取指、译码、执行等)、如何解决流水线冲突(结构、数据、控制冲突)。 - - - **存储系统层次结构:** Cache的设计(大小、相联度、替换策略)、虚存机制的硬件支持(TLB)。 - - - **其他优化技术:** 如分支预测、乱序执行、超标量等 - -- **一句话理解:** 微体系结构是计算机的“内部工程设计图”和“生产流水线”,它对程序员**不可见**。同一个ISA可以有完全不同的微体系结构实现。 - - -#### 3. 微程序结构 (Microprogrammed Architecture) - -- **这个概念是用来回答“控制器是如何实现的?”这个问题的。** 它本身不是一个与ISA、微体系结构并列的抽象层次,而是实现**微体系结构中控制器**的一种技术。 - -- **定义:** 微程序设计思想是将每条机器指令(如`ADD`, `LDA`)编写成一个**微程序**,每个微程序由若干条**微指令**组成。这些微程序存放在一个专门的存储器——**控制存储器(Control Store, CS)** 中。当CPU需要执行一条机器指令时,就去执行对应的微程序,通过逐条执行微指令来发出各种微命令控制信号。 - -- **核心内容(考点):** - - - **基本概念:** 微命令、微指令、微程序、微地址。 - - - **微指令格式:** 水平型、垂直型、混合型。 - - - **微地址的形成方式:** 计数器方式、下地址字段方式。 - - - **与硬布线控制器的对比。** - -- **一句话理解:** 微程序结构是一种用“软件化”(编写微程序)的方法来设计“硬件”(控制器)的技术。 - - ---- - -### 三、 三者关系与图示 - -这三者是一个从抽象到具体,从“是什么”到“怎么做”的层次关系。 - -**层次关系图:** - -``` -+------------------------------------------------------+ -| 应用软件 / 操作系统 | <-- 用户/系统软件层面 -+------------------------------------------------------+ - ^ - | (接口 Interface) - v -+------------------------------------------------------+ -| 指令集体系结构 (ISA) - (x86, ARM) | <-- 软件与硬件的契约 -| (程序员可见:指令、寄存器、寻址等) | -+------------------------------------------------------+ - ^ - | (实现 Implementation) - v -+------------------------------------------------------+ -| 微体系结构 (Microarchitecture) | <-- 硬件的逻辑组织 -| (程序员不可见:数据通路、控制器、流水线、Cache) | -| | -| +------------------------------------+ | -| | 控制器 (Control Unit) | | -| | | | -| | 实现方式1: 硬布线 (Hardwired) | | -| | 实现方式2: 微程序结构 (Microprogrammed)| | -| +------------------------------------+ | -+------------------------------------------------------+ - -``` - -**一个绝佳的比喻:** - -- **ISA** 就像是**汽车的驾驶界面**:方向盘、油门、刹车、档位。所有司机(程序员)都通过这个标准接口来驾驶汽车。无论你开的是家用车还是跑车,这些基本操作是相通的。x86和ARM就是两种不同的驾驶标准(比如手动挡和自动挡)。 - -- **微体系结构** 就像是**引擎和传动系统的具体设计**:是V6引擎还是直列四缸引擎?是涡轮增压还是自然吸气?是前驱还是后驱?这些都决定了汽车的性能(即指令执行的效率),但司机并不需要知道这些内部细节。Intel的酷睿(Core)和AMD的锐龙(Ryzen)都实现了x86这个ISA,但它们的微体系结构(引擎设计)完全不同。 - -- **微程序结构** 就像是**引擎控制单元(ECU)的一种实现技术**。ECU负责根据司机的操作(踩油门)来精确控制喷油量、点火时机。这个ECU可以用“硬电路”(**硬布线**)来实现,也可以用一个内置的、可编程的芯片(**微程序**)来实现。微程序方式就像ECU里有一张表,根据当前转速和油门开度查表得到控制参数,更灵活,易于修改和扩展。 - - ---- - -1. **ISA与微体系结构的关系:** - - - **考点:** 同一种ISA可以有不同的微体系结构实现;不同的ISA必然对应不同的微体系结构。 - - - **例题形式:** “下列说法正确的是?” - - - A. Intel的Pentium处理器和AMD的Athlon处理器有相同的ISA。(可能正确,因为它们都兼容x86) - - - B. Intel的Pentium处理器和AMD的Athlon处理器有相同的微体系结构。(**错误**,内部设计天差地别) - - - C. 改变CPU的流水线级数会改变其ISA。(**错误**,改变的是微体系结构) - -2. **微程序与硬布线的对比:** 这是控制器章节的绝对核心,常以选择题或简答题形式出现。 - - - **考点:** - - - **规整性/设计难度:** 微程序规整,类似编程,设计简单,易于修改和扩展;硬布线逻辑复杂,设计困难。 - - - **执行速度:** 硬布线是纯硬件电路,速度快;微程序需要从控存中取微指令,有访存过程,速度慢。 - - - **灵活性/可修改性:** 微程序灵活,可通过修改微代码来增加新指令或修复bug;硬布线一旦成型则无法修改。 - - - **命题趋势:** RISC(精简指令集)倾向于使用**硬布线控制器**(因为指令简单规整,适合硬连线),CISC(复杂指令集)倾向于使用**微程序控制器**(因为指令复杂,用微程序实现更方便)。这个对应关系是高频考点。 - diff --git "a/src/site/notes/notes/408/\346\214\207\344\273\244\351\233\206\351\207\214\347\232\204\342\200\234\346\226\255\350\210\215\347\246\273\342\200\235\345\244\247\345\270\210\342\200\224\342\200\224Load Store.md" "b/src/site/notes/notes/408/\346\214\207\344\273\244\351\233\206\351\207\214\347\232\204\342\200\234\346\226\255\350\210\215\347\246\273\342\200\235\345\244\247\345\270\210\342\200\224\342\200\224Load Store.md" deleted file mode 100644 index 6d97cc1..0000000 --- "a/src/site/notes/notes/408/\346\214\207\344\273\244\351\233\206\351\207\214\347\232\204\342\200\234\346\226\255\350\210\215\347\246\273\342\200\235\345\244\247\345\270\210\342\200\224\342\200\224Load Store.md" +++ /dev/null @@ -1,52 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/指令","permalink":"/408/指令/"} ---- - - -相较于允许运算指令直接访问内存的指令系统,Load/Store型指令系统的核心优点在于:**指令格式更规整、控制器设计更简单,从而极大地有利于实现指令流水线,提升处理器吞吐率**。 - -### 一、Load/Store架构的优点详解 - -为了完成一个“内存变量A + 内存变量B -> 内存变量C”的操作,两种架构的指令序列对比如下: - -|寄存器-存储器型 (R-M, CISC)|Load/Store型 (R-R, RISC)| -|---|---| -|`MOV R1, A` ; 将内存A加载到R1|`LOAD R1, A` ; 将内存A加载到R1| -|`ADD R1, B` ; R1 <- R1 + [B]|`LOAD R2, B` ; 将内存B加载到R2| -|`MOV C, R1` ; 将R1结果存回内存C|`ADD R3, R1, R2` ; R3 <- R1 + R2| -||`STORE C, R3` ; 将R3结果存回内存C| - -表面上看,Load/Store型指令数量更多,似乎更繁琐,但其优势体现在以下几个方面: - -#### 1. 指令格式规整,多为定长指令 - -- **R-M架构**:`ADD R1, B` 这类指令,既要指定寄存器`R1`,又要指定内存地址`B`。内存地址的寻址方式多种多样(直接寻址、间接寻址等),导致指令长度可变,格式复杂。 - -- **L/S架构**:算术指令如 `ADD R3, R1, R2`,操作数都在寄存器中,只需用固定的位数(如5位)指明寄存器号即可。这使得大部分指令可以设计成**长度固定的格式**。 - - -#### 2. 简化指令解码与控制器设计 - -- **定长指令**意味着CPU的指令解码器(ID)无需判断当前指令到底有多长,可以直接按固定长度取指、解码,大大简化了控制逻辑。 - -- 指令类型单一(运算类、访存类、转移类分明),功能正交,使得控制器可以用更简单、更高速的**硬布线逻辑**来实现,而非CISC中常用的、速度较慢的微程序控制器。 - - -#### 3. 极大地有利于指令流水线执行 - -这是Load/Store架构**最核心、最重要**的优势。一个典型的五段流水线(IF取指, ID解码, EX执行, MEM访存, WB写回)可以清晰地展示这一点。 - -- R-M架构的流水线困境: - - ADD R1, B这条指令,在EX(执行)阶段需要计算地址和执行加法,紧接着又需要在MEM(访存)阶段从内存读取操作数B。这导致单个指令需要跨越多个流水段的核心功能区,或者说访存操作被嵌入到了运算指令中。这会造成严重的结构冒险(EX和MEM阶段争用内存地址总线)或数据冒险,并使得流水线各段执行时间极不均衡,大大增加了流水线设计的复杂性,充满了“气泡”(stall)。 - -- L/S架构的流水线优势: - - 其设计将运算和访存彻底分离,完美契合流水线的分段思想: - - - `ADD R3, R1, R2`:这类指令只在EX阶段对寄存器内容进行运算,速度极快。它根本不涉及MEM阶段,可以直接“飘过”。 - - - `LOAD R1, A` / `STORE C, R3`:这类指令的核心工作在MEM阶段完成,而EX阶段可能只做简单的地址计算。 - - - **结果**:每条指令各司其职,在自己对应的流水段完成核心工作。流水线可以顺畅地流动,极少发生停顿,大大提高了**指令的吞吐率**(单位时间完成的指令数),实现了CPI≈1的目标。 - \ No newline at end of file diff --git "a/src/site/notes/notes/408/\346\226\207\344\273\266\347\232\204\351\200\273\350\276\221\347\273\223\346\236\204\344\273\245\345\217\212\347\264\242\345\274\225\346\226\207\344\273\266.md" "b/src/site/notes/notes/408/\346\226\207\344\273\266\347\232\204\351\200\273\350\276\221\347\273\223\346\236\204\344\273\245\345\217\212\347\264\242\345\274\225\346\226\207\344\273\266.md" deleted file mode 100644 index 0373627..0000000 --- "a/src/site/notes/notes/408/\346\226\207\344\273\266\347\232\204\351\200\273\350\276\221\347\273\223\346\236\204\344\273\245\345\217\212\347\264\242\345\274\225\346\226\207\344\273\266.md" +++ /dev/null @@ -1,97 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/文件的逻辑结构以及索引文件","permalink":"/408/文件的逻辑结构以及索引文件/"} ---- - - -### **文件的逻辑结构 (Logical File Structure)** - -文件的**逻辑结构**是指在用户看来,文件是由什么信息成分构成的,以及成分之间的关系。它关注的是文件的**组织方式**,而不是存储方式。从用户的角度,文件主要可以分为以下两种逻辑结构: - -#### **1. 无结构文件 (流式文件 - Stream of Bytes File)** - -这是**最简单、也是当今最常用**的文件逻辑结构。 - -- **核心思想**: 文件就是一个连续的、无差别的**字节序列 (Stream of Bytes)**。操作系统不关心文件中存储的是什么,无论是文本、图像还是可执行代码,在操作系统看来,它都只是一个长长的字节流。 - -- **结构**: 文件内部没有结构。任何结构和意义都由**访问该文件的应用程序**来解释和管理。例如,文本编辑器知道换行符`\n`的含义,而编译器知道`.c`文件中`main()`函数的语法结构,但操作系统本身对此一无所知。 - -- **访问方式**: 基本的访问单位是字节。可以通过一个读写指针来顺序地访问文件,也可以通过`lseek`等操作将指针移动到任意字节位置,实现随机访问。 - -- **典型例子**: 所有在UNIX/Linux和Windows中的文件,本质上都是流式文件。例如 `.txt`, `.c`, `.jpg`, `.mp3`, `.exe` 等。 - - -#### **2. 有结构文件 (记录式文件 - Record-based File)** - -在这种结构中,文件不再是无差别的字节流,而是由一组具有相似结构的**记录 (Record)** 构成的集合。一条记录通常用于描述一个实体对象的信息。 - -- **核心思想**: 文件是记录的序列,操作系统需要理解“记录”是文件的基本组成单位。 - -- **结构**: - - - **定长记录**: 文件中所有记录的长度都是相同的。这种结构简单,易于管理。要访问第 `i` 条记录,可以直接计算其地址:`地址 = i * 单条记录长度`。这使得随机访问非常高效。 - - - **变长记录**: 文件中各条记录的长度可以不同。这种结构存储效率高,不浪费空间,但管理复杂,实现随机访问的难度较大,通常需要在每条记录的开头存储其长度信息。 - -- **访问方式**: 基本的访问单位是记录。 - -- **典型例子**: 这种结构在早期的操作系统(如大型机的文件管理系统)和某些特定的商业应用(如COBOL语言处理的文件、数据库文件)中比较常见。 - - ---- - -### **索引文件 (Indexed File) 是什么?** - -现在,我们来重点讲解“索引文件”。**索引文件是“有结构文件”的一种高级、复杂且高效的组织形式**,其设计的核心目的就是为了解决对文件中记录的**快速随机访问**问题。 - -#### **1. 核心思想与类比** - -想象一本非常厚的书,如果你想找一个特定的知识点,从第一页翻到最后一页会非常慢。你会怎么做?你会去翻书最后的**索引**。索引告诉你“某个关键词在第几页”,然后你直接翻到那一页即可。 - -**索引文件就是采用了完全相同的思想。** - -#### **2. 结构组成** - -一个索引文件通常由两部分构成: - -1. **数据文件 (Data File)**: 存放着所有的实际**记录**。这些记录可以顺序存放,也可以无序存放。 - -2. **索引表 (Index Table)**: 这是索引文件的精华所在。它是一张独立的、通常比数据文件小得多的表。表中的每一项都是一个**索引项**。 - - - **索引项的结构**: `(关键字, 指针)` - - - **关键字 (Key)**: 是记录中的某个数据项,用于**唯一标识**这条记录。例如,在一个学生信息文件中,关键字可以是“学号”。 - - - **指针 (Pointer)**: 指向该关键字对应的记录在**数据文件**中的**存储地址**(可以是逻辑记录号或物理地址)。 - - -#### **3. 工作流程(如何访问)** - -假设我们要查找学号为`“20250702”`的学生记录: - -1. **不读数据文件**: 程序**不会**去遍历庞大的学生数据文件。 - -2. **查找索引表**: 程序会直接在**索引表**中查找关键字`“20250702”`。由于索引表通常是按关键字排好序的,所以可以使用**二分查找**等高效算法,查找速度非常快。 - -3. **获取指针**: 在索引表中找到该关键字后,从中取出与之对应的**指针**(例如,指针告诉我们该记录是数据文件中的第158条记录)。 - -4. **直接访问数据**: 程序利用这个指针,直接计算出第158条记录在数据文件中的确切位置,并将其读入内存。 - - -#### **4. 图示理解** - -``` - 索引表 (Index Table) 数据文件 (Data File) -+----------------+---------+ +--------------------------------+ -| 关键字(Key) | 指针 | | 记录1 (Record 1) | -+----------------+---------+ +--------------------------------+ -| “20250001” | 1 | -- 指向 --> | 记录2 (Record 2) | -+----------------+---------+ +--------------------------------+ -| “20250002” | 2 | | ... | -+----------------+---------+ +--------------------------------+ -| ... | ... | | 记录158 (学号为“20250702”) | <--+ -+----------------+---------+ +--------------------------------+ | -| “20250702” | 158 | -----------------------------------------------------+ -+----------------+---------+ | ... | -| ... | ... | +--------------------------------+ -+----------------+---------+ -``` diff --git "a/src/site/notes/notes/408/\346\235\241\344\273\266\350\275\254\347\247\273\346\214\207\344\273\244\345\257\271\345\272\224\347\232\204\350\275\254\347\247\273\346\235\241\344\273\266\345\210\206\346\236\220\360\237\244\224.md" "b/src/site/notes/notes/408/\346\235\241\344\273\266\350\275\254\347\247\273\346\214\207\344\273\244\345\257\271\345\272\224\347\232\204\350\275\254\347\247\273\346\235\241\344\273\266\345\210\206\346\236\220\360\237\244\224.md" deleted file mode 100644 index b41ca16..0000000 --- "a/src/site/notes/notes/408/\346\235\241\344\273\266\350\275\254\347\247\273\346\214\207\344\273\244\345\257\271\345\272\224\347\232\204\350\275\254\347\247\273\346\235\241\344\273\266\345\210\206\346\236\220\360\237\244\224.md" +++ /dev/null @@ -1,103 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/条件转移指令对应的转移条件分析🤔","permalink":"/408/条件转移指令对应的转移条件分析🤔/"} ---- - - - -要准确判断两个带符号整数的大小关系,CPU必须同时参考符号标志位(SF)和溢出标志位(OF),而不能仅仅依赖符号标志位(SF)。 - -### 一、带符号整数比较原理详解 - -计算机中的比较操作通常由一条减法指令来实现,例如`CMP A, B`指令,其在硬件层面的实际操作是计算 A−B ,但结果不回存,只根据计算结果设置相应的标志位。后续的条件转移指令(如`jg`, `jl`等)则根据这些标志位来判断是否跳转。 - -#### 1. 为什么不能只看SF标志位? - -一个常见的误区是:既然SF(Sign Flag)标志位表示结果的符号(SF=1为负,SF=0为正),那么比较 A 和 B 的大小,不就是看 A−B 的结果是正是负吗? - -这种想法在**不发生溢出**的情况下是正确的。但一旦发生**算术溢出**,结果的符号位就会变得不可靠,甚至与真实结果的符号完全相反。 - -**溢出(Overflow)**:当两个同号的数相加(或两个异号的数相减)后,结果超出了机器数所能表示的范围。在带符号数运算中,溢出会导致结果的符号位不正确。 - -- **正溢出**:两个正数相加,结果大于了能表示的最大正数,变成了一个负数(例如8位系统中 127+1=−128)。 - -- **负溢出**:两个负数相加,结果小于了能表示的最小负数,变成了一个正数(例如8位系统中 −127+(−2)=127)。 - - -**溢出标志位(OF)** 就是用来检测这种情况的。当运算结果发生溢出时,`OF=1`,否则`OF=0`。 - -#### 2. SF与OF的协同工作 - -为了正确判断带符号数的大小,必须将SF和OF结合起来。我们来分析 `jg` (Jump if Greater) 和 `jl` (Jump if Less) 的转移条件。 - -**前提**:`CMP A, B` 执行 A−B 操作。我们期望判断 A 是否大于/小于 B。 - -**A > B (对应指令 jg, jnle)** - -- **转移条件**: SF=OF AND ZF=0 - -- **逻辑分析**: 我们希望 A−B 的真实结果是正数。 - - 1. 情况一:不溢出 (OF=0) - - 此时,计算结果的符号是可信的。要想 A−B>0,其结果必须为正数,即 SF=0。此时条件 SF=OF (0=0) 成立。 - - 2. 情况二:发生溢出 (OF=1) - - 此时,计算结果的符号是不可信的,与真实结果的符号相反。什么情况下 A−B 会溢出?只有“正数 - 负数”才可能导致正溢出(结果太大,看似为负)。例如,A 是一个较大的正数,B 是一个绝对值较大的负数,A−B 变成 A+(∣B∣),结果超出了最大正数范围,导致最终结果的符号位为1,即 SF=1。但我们知道,此时真实的 A 肯定是大于 B 的。此时条件 SF=OF (1=1) 也成立。 - -- **结论**:无论是否溢出,只要 A>B,就必然满足 SF=OF。`ZF=0` 条件是为了排除 A=B 的情况。 - - -**A < B (对应指令 jl, jnge)** - -- **转移条件**: SF=OF AND ZF=0 - -- **逻辑分析**: 我们希望 A−B 的真实结果是负数。 - - 1. 情况一:不溢出 (OF=0) - - 此时,计算结果的符号是可信的。要想 A−B<0,其结果必须为负数,即 SF=1。此时条件 SF=OF (1=0) 成立。 - - 2. 情况二:发生溢出 (OF=1) - - 此时,计算结果的符号是不可信的。什么情况下 A−B 会溢出?只有“负数 - 正数”才可能导致负溢出(结果太小,看似为正)。例如,A 是一个绝对值较大的负数,B 是一个较大的正数,A−B 的结果小于了最小负数范围,导致最终结果的符号位为0,即 SF=0。但我们知道,此时真实的 A 肯定是小于 B 的。此时条件 SF=OF (0=1) 也成立。 - -- **结论**:无论是否溢出,只要 A B**|**不溢出**,结果为正|0|0|**SF=OF**|✅| -|**A > B**|**正溢出**,“正-负”,结果看似为负|1|1|**SF=OF**|✅| -|**A < B**|**不溢出**,结果为负|0|1|**SF=OF**|✅| -|**A < B**|**负溢出**,“负-正”,结果看似为正|1|0|**SF=OF**|✅| - -而对于 `jge` (大于等于) 和 `jle` (小于等于),逻辑是类似的,只是放宽了对 ZF 的要求: - -- `jge` (A ≥ B): SF=OF。 (允许 A=B 时结果为0,此时 ZF=1 但 SF=0,OF=0 依然满足 SF=OF) - -- `jle` (A ≤ B): SF=OF OR ZF=1。 (当 A=B 时,ZF=1,直接满足跳转条件) - - -### 三、常考点 - -1. **混淆无符号数与有符号数跳转**:这是最核心的考点。考生必须清晰地辨别指令助记符。 - - - **无符号数**:`ja` (above), `jb` (below)。依据:`CF` 和 `ZF`。 - - - **有符号数**:`jg` (greater), `jl` (less)。依据:`SF`, `OF` 和 `ZF`。 - - - `je` (equal), `jne` (not equal) 对两者通用,只看 `ZF`。 - -2. **忽略CMP指令**:条件转移指令本身不进行比较,它只检查标志位。题目中一定会有一个先行指令(如`CMP`, `SUB`, `ADD`等)来设置标志位,分析时必须以该指令的执行结果为依据。 - -3. **对溢出判断不熟练**:考题常常会精心设计操作数,使其运算结果恰好在溢出的边界上。考生必须熟练掌握溢出的判断方法: - - - **方法一(双符号位法)**:使用补码的变形,用两个符号位表示。如果运算结果的两个符号位不同(01或10),则表示发生溢出。 - - - **方法二(单符号位法)**:OF=Cs⊕Cs−1,即符号位的进位和最高数值位的进位是否不同。对于减法 A−B,等效于计算 A+[B]补,此时 OF 是判断两个正数相加是否得到负数,或两个负数相加是否得到正数。 diff --git "a/src/site/notes/notes/408/\346\255\273\351\224\201\347\232\204\345\233\233\344\270\252\345\277\205\350\246\201\346\235\241\344\273\266.md" "b/src/site/notes/notes/408/\346\255\273\351\224\201\347\232\204\345\233\233\344\270\252\345\277\205\350\246\201\346\235\241\344\273\266.md" deleted file mode 100644 index 72cd400..0000000 --- "a/src/site/notes/notes/408/\346\255\273\351\224\201\347\232\204\345\233\233\344\270\252\345\277\205\350\246\201\346\235\241\344\273\266.md" +++ /dev/null @@ -1,80 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/死锁的四个必要条件","permalink":"/408/死锁的四个必要条件/"} ---- - - -### 1. 死锁产生的四个必要条件 - -这四个条件是考研的必考点,要求能准确默写、理解,并能结合实例进行分析。 - -#### 1.1 条件定义与阐述 - -1. **互斥条件 (Mutual Exclusion)** - - - **含义**: 进程所申请的资源是独占的、排他性的,即一个资源在任意时刻只能被一个进程所使用。 - - - **解释**: 这个条件是很多资源的固有属性,比如打印机、物理内存区域等。如果资源本身就是可共享的(如只读文件),那么该资源本身就不会引发死锁。这个条件通常是无法破坏的。 - -2. **请求与保持条件 (Hold and Wait)** - - - **含义**: 进程在已经保持了**至少一个**资源的情况下,又提出了新的资源请求,但该资源已被其他进程占用,此时请求进程被阻塞,但它在等待新资源时**并不会释放**自己已经保持的资源。 - - - **解释**: 这是资源分配策略上的问题。“拿着碗里的,还看着锅里的”。 - -3. **不可剥夺条件 (No Preemption)** - - - **含义**: 进程已获得的资源,在未使用完毕之前,不能被其他进程强行剥夺,只能由获得该资源的进程自己主动释放。 - - - **解释**: 这保证了进程对已分配资源的控制权,但同时也增加了死锁的风险。 - -4. **循环等待条件 (Circular Wait)** - - - **含义**: 存在一个进程-资源的循环等待链,即存在一个进程集合 `{P_0, P_1, ..., P_n}`,其中 P_0 正在等待 P_1 所占用的资源,P_1 正在等待 P_2 所占用的资源,...,P_n 正在等待 P_0 所占用的资源。 - - - **解释**: 这是死锁状态的直接体现,形成了一个“你等我、我等你”的闭环。可以用“资源分配图”清晰地展示出来,如果图中出现了环路,则**可能**存在死锁(如果每类资源只有一个实例,则必为死锁)。 - - -#### 1.2 实例说明 - -我们用一个非常经典的例子来说明这四个条件是如何同时出现的。 - -**场景**: 系统中有两个进程 `P1`、`P2`,以及两个互斥资源 `R1`(打印机)、`R2`(扫描仪)。 - -**执行时序**: - -1. **时刻 t1**: `P1` 成功申请到资源 `R1`(打印机)。 - -2. **时刻 t2**: `P2` 成功申请到资源 `R2`(扫描仪)。 - -3. **时刻 t3**: `P1` 在保持 `R1` 的同时,又去申请 `R2`。但 `R2` 已被 `P2` 占用,因此 `P1` 进入阻塞等待状态。 - -4. **时刻 t4**: `P2` 在保持 `R2` 的同时,又去申请 `R1`。但 `R1` 已被 `P1` 占用,因此 `P2` 也进入阻塞等待状态。 - - -**此刻,系统进入死锁状态。我们来分析四个条件是否满足**: - -- **满足互斥条件**: 打印机 `R1` 和扫描仪 `R2` 都是独占设备,一次只能给一个进程使用。 - -- **满足请求与保持条件**: `P1` 保持着 `R1` 去请求 `R2`;`P2` 保持着 `R2` 去请求 `R1`。 - -- **满足不可剥夺条件**: 系统不能强行将 `R1` 从 `P1` 手中夺走分配给 `P2`,也不能将 `R2` 从 `P2` 手中夺走分配给 `P1`。 - -- **满足循环等待条件**: `P1` 等待 `P2` 释放 `R2`,而 `P2` 同时在等待 `P1` 释放 `R1`,形成了 `P1 -> R2 -> P2 -> R1 -> P1` 的循环等待链。 - - -由于四个条件同时满足,死锁发生。两个进程都将永远等待下去,无法推进。 - -### 2. 信号量与死锁的关系 - -这是一个非常经典的考点,也是一个极易产生误解的地方。 - -**明确结论**: **使用信号量机制并不能保证不出现死锁。恰恰相反,对信号量的不当使用是导致死锁的常见原因之一。** - - -1. 生产者接着执行 `P(empty)`。由于 `empty.value` 为0,生产者进程**阻塞**。**关键点:此时它因为阻塞,无法执行后面的 `V(mutex)`,所以它仍然“保持”着 `mutex` 信号量。** - -2. 此时轮到消费者执行,它想要消费一个产品。 - -3. 消费者执行 `P(full)`,成功(因为缓冲区是满的),`full.value` 减1。 - -4. 消费者接着执行 `P(mutex)`,试图进入临界区取出产品。但由于 `mutex.value` 已被生产者置为0,所以消费者进程**阻塞**。 diff --git "a/src/site/notes/notes/408/\346\255\273\351\224\201\351\201\277\345\205\215,\351\242\204\351\230\262,\346\243\200\346\265\213\347\256\227\346\263\225.md" "b/src/site/notes/notes/408/\346\255\273\351\224\201\351\201\277\345\205\215,\351\242\204\351\230\262,\346\243\200\346\265\213\347\256\227\346\263\225.md" deleted file mode 100644 index 8cd9a59..0000000 --- "a/src/site/notes/notes/408/\346\255\273\351\224\201\351\201\277\345\205\215,\351\242\204\351\230\262,\346\243\200\346\265\213\347\256\227\346\263\225.md" +++ /dev/null @@ -1,125 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/死锁避免,预防,检测算法","permalink":"/408/死锁避免,预防,检测算法/"} ---- - - -### 1. 死锁预防 (Deadlock Prevention) - -死锁预防是最严格的策略。其核心思想是通过在系统设计时施加特定的限制,**从结构上破坏死锁产生的四个必要条件中的至少一个**,从而使得死锁永远不会发生。这更像是一系列**静态策略**或**协议**,而不是动态运行的算法。 - -- **破坏“互斥”条件 (Break Mutual Exclusion)** - - - **方法**: 允许资源被同时访问。但这只适用于那些本质上可共享的资源(如只读文件)。对于打印机、处理器等临界资源,互斥是无法被破坏的。 - - - **“伪共享”技术**: 对于打印机这类独占设备,可以使用 **SPOOLing 技术 (假脱机技术)**。进程并不直接访问打印机,而是将打印数据输出到一个共享的磁盘缓冲区(Spool),由系统的一个服务进程统一、依次地从缓冲区读取并打印。对进程而言,它感觉自己可以随时“打印”,但实际上对物理打印机的访问依然是互斥的。这种方法绕过了互斥的限制,但并未真正破坏它。 - - -- **破坏“请求与保持”条件 (Break Hold and Wait)** - - - **方法1:一次性申请 (Request All at Once)** - - - **协议**: 规定进程在开始运行前,必须一次性地申请其执行过程中所需的**全部**资源。若系统能满足其所有请求,则分配给它;否则,一个资源也不给它,让其等待。 - - - **缺点**: 资源浪费严重(很多资源在进程后期才用到,但一开始就被占用),且可能导致“饥饿”(一个需要很多资源的进程可能永远无法满足所有请求)。 - - - **方法2:先释放再申请 (Release then Request)** - - - **协议**: 允许进程动态申请资源,但在申请新资源前,必须先释放它**所有**已占有的资源。 - - - **缺点**: 实现复杂,代价高昂。进程多次执行的数据可能需要保存和恢复,严重影响效率。 - -- **破坏“不可剥夺”条件 (Break No Preemption)** - - - **方法**: 允许操作系统强行“剥夺”已被占有的资源。 - - - **协议**: 当一个已持有某些资源的进程申请新资源被阻塞时,它必须释放其当前持有的所有资源。另一种更复杂的协议是,当进程P申请的资源被进程Q占用时,若Q的优先级低于P,则系统可以剥夺Q的资源给P。 - - - **缺点**: 实现非常复杂,且只适用于状态可以轻易保存和恢复的资源(如CPU、内存),不适用于打印机等设备。 - -- **破坏“循环等待”条件 (Break Circular Wait)** - - - **方法**: **资源有序分配法 (Resource Ordering/Hierarchy)** - - - **协议**: 将系统中所有资源类型进行线性排序,并赋予它们唯一的序号(如 `R1, R2, ..., Rn`)。规定任何进程在申请资源时,必须**严格按照序号递增的顺序**进行申请。 - - - **举例**: 若一个进程已持有 `Ri`,则它接下来只能申请序号大于 `i` 的资源 `Rj (j > i)`。这样绝对不会形成 `P1等P2、P2等P1` 的环路。 - - - **优点**: 相较于前几种破坏方式,这是**实现相对简单、代价较小且应用最广**的预防策略。 - - - **缺点**: 限制了进程申请资源的灵活性,可能导致资源使用不便;且需要为所有资源编号,给系统设计带来负担。 - - ---- - -### 2. 死锁避免 (Deadlock Avoidance) - -死锁避免是一种比预防更宽松的策略。它不要求破坏必要条件,而是在资源分配的**过程中**,通过一个**动态运行的算法**来判断本次分配是否会将系统带入“不安全状态”。如果会,则拒绝分配,让进程等待;否则,执行分配。 - -**核心思想**: 确保系统始终处于**安全状态**。所谓安全状态,是指系统能找到一个**安全序列** ``,即按照这个序列的顺序为每个进程分配其所需资源,可以使所有进程都顺利完成。不安全状态**不一定**是死锁状态,但可能导致死锁。 - -- **算法:银行家算法 (Banker's Algorithm)** - - - 这是死锁避免策略的**最著名、也是唯一的考纲核心算法**。它适用于每种资源有多个实例的系统。 - - - **所需数据结构**: - - - `Available`: 一个向量,表示每种资源当前可用的实例数。 - - - `Max`: 一个矩阵,表示每个进程完成其任务所需各类资源的最大数量。 - - - `Allocation`: 一个矩阵,表示当前已分配给每个进程的各类资源实例数。 - - - `Need`: 一个矩阵,表示每个进程**还**需要多少资源才能完成任务。`Need[i, j] = Max[i, j] - Allocation[i, j]`。 - ---- - -### 3. 死锁检测与解除 (Deadlock Detection and Recovery) - -这是最宽松的策略。它允许系统进入死锁状态,但要求系统能**检测**出死锁的发生,并采取措施**解除**死锁。 - -- **死锁检测算法**: - - - **基于资源分配图 (适用于每类资源只有一个实例)**: - - - **方法**: 系统维护一个**等待图 (Wait-for Graph)**,它是资源分配图的简化版,只包含进程节点和它们之间的等待关系。如果进程 `Pi` 等待进程 `Pj` 拥有的资源,则画一条从 `Pi` 到 `Pj` 的有向边。 - - - **算法**: 定期地在等待图中运行一个**环路检测算法** (如深度优先搜索)。如果**检测到环路**,则证明系统发生了死锁。 - - - **类似于银行家算法的检测算法 (适用于每类资源有多个实例)**: - - - 该算法与安全性算法非常相似,但不使用`Max`或`Need`矩阵,而是使用`Request`矩阵表示进程当前申请的资源。 - - - **数据结构**: `Available`, `Allocation`, `Request`。 - - - **步骤**: - - 1. 初始化 `Work = Available`, `Finish` (所有为 `false`)。 - - 2. 寻找一个进程 `Pi`,满足 `Finish[i] == false` 且 `Request[i] <= Work`。注意,这里判断的是`Request`而非`Need`。 - - 3. 若找不到,跳转到第5步。 - - 4. 若找到,说明 `Pi` 的请求可以被满足,它不会被永久阻塞。我们假定它能运行完并释放**已分配**的资源。更新 `Work = Work + Allocation[i]`, `Finish[i] = true`,返回第2步。 - - 5. 算法结束时,如果存在某个进程 `Pi` 的 `Finish[i]` 仍为 `false`,则该进程 `Pi` 就是**死锁进程**。 - -- **死锁解除方法 (Deadlock Recovery)**: - - - **1. 进程终止 (Process Termination)** - - - **方法一:终止所有死锁进程**。简单粗暴,代价巨大。 - - - **方法二:逐个终止死锁进程**。每终止一个,就重新运行死锁检测算法,直到环路被打破。选择终止哪个进程,通常会考虑进程优先级、已运行时间、已占用资源等因素,以求代价最小。 - - - **2. 资源剥夺 (Resource Preemption)** - - - **方法**: 强行从一个或多个死锁进程中剥夺资源,分配给其他死锁进程,以打破环路。 - - - **需要考虑的问题**: - - - **选择牺牲者 (Victim Selection)**: 剥夺哪个进程的哪个资源,代价最小? - - - **回滚 (Rollback)**: 进程的资源被剥夺后,其状态必须回退到某个安全的时间点(检查点),并重新开始。这可能导致该进程“饥饿”。 - - - **饥饿 (Starvation)**: 如果总是选择同一个进程作为牺牲者,它可能永远无法完成。需要有机制确保公平性。 - diff --git "a/src/site/notes/notes/408/\347\224\250\346\210\267\347\272\247\347\272\277\347\250\213\344\270\272\344\273\200\344\271\210\351\230\273\345\241\236\344\270\200\344\270\252\345\260\261\345\205\250\351\203\250\351\230\273\345\241\236\344\272\206.md" "b/src/site/notes/notes/408/\347\224\250\346\210\267\347\272\247\347\272\277\347\250\213\344\270\272\344\273\200\344\271\210\351\230\273\345\241\236\344\270\200\344\270\252\345\260\261\345\205\250\351\203\250\351\230\273\345\241\236\344\272\206.md" deleted file mode 100644 index ecb1e67..0000000 --- "a/src/site/notes/notes/408/\347\224\250\346\210\267\347\272\247\347\272\277\347\250\213\344\270\272\344\273\200\344\271\210\351\230\273\345\241\236\344\270\200\344\270\252\345\260\261\345\205\250\351\203\250\351\230\273\345\241\236\344\272\206.md" +++ /dev/null @@ -1,42 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/用户级线程为什么阻塞一个就全部阻塞了","permalink":"/408/用户级线程为什么阻塞一个就全部阻塞了/"} ---- - - -## 用户级线程:一个线程阻塞,整个进程阻塞的原因 -你描述的现象是用户级线程的一个显著特点和主要缺点。根本原因在于: - -**用户级线程的调度和管理完全由用户空间的线程库(Thread Library)完成,内核对这些用户级线程的存在是“无感知”的。对于内核来说,它只知道有一个“进程”在运行,而不知道这个进程内部有多少个用户级线程。** - -下面我们来详细解释这一点: - -1. **内核的视角**: - - - 当一个进程被调度执行时,**内核调度器**(操作系统的核心部分)会将CPU时间片分配给这个**进程**。 - - 在内核看来,无论是这个进程内部有一个线程还是上百个用户级线程,它都只把它当做一个单一的执行实体。 - - 内核只会管理**进程**的调度、内存、I/O等资源。 -2. **线程库的作用**: - - - 用户级线程的创建、销毁、调度、同步等操作,都是由用户空间的**线程库**(比如POSIX Threads库,即Pthreads)来实现的。 - - 线程库在进程内部维护着所有用户级线程的上下文信息(如程序计数器、寄存器值、栈指针等)。 - - 当一个用户级线程执行时,它实际上是运行在分配给**整个进程**的CPU时间片内。线程库会根据自己的调度算法,在这些用户级线程之间进行**用户态的上下文切换**。 -3. **阻塞式系统调用(Blocking System Calls)**: - - - 问题的关键在于**阻塞式系统调用**。当用户级线程执行一个阻塞式系统调用时(例如,`read()`从磁盘读取数据,`write()`向网络发送数据,`sleep()`暂停执行,或者等待某个I/O完成),它必须通过**陷入内核**的方式来请求操作系统服务。 - - 一旦用户级线程发出阻塞式系统调用,内核就会认为**整个进程**进入了阻塞状态(因为内核不知道这个进程内部还有其他用户级线程)。 - - 内核会将这个进程从运行队列中移除,放入等待队列,直到它所请求的I/O操作完成或者等待的事件发生。 -4. **结果:整个进程被挂起**: - - - 由于内核已经将整个进程置于阻塞状态,并停止为其分配CPU时间片,因此,即使这个进程内部还有其他**用户级线程是就绪状态**,它们也无法获得CPU执行,因为整个进程已经被内核“冻结”了。 - - 这意味着,当一个用户级线程因I/O或同步操作而阻塞时,整个进程都会被阻塞,导致进程内的其他用户级线程也无法执行,就好像整个进程都“停”下来了一样。 - ---- - - - -## 与内核级线程的对比 -为了更好地理解这一点,我们可以简单对比一下**内核级线程**: - -- **内核级线程(KLTs)**:每个线程都是内核可感知的独立调度实体。当一个内核级线程执行阻塞系统调用时,只有这一个线程会被阻塞,而同一进程内的其他内核级线程仍然可以被内核调度执行,从而实现真正的并发。 - ---- diff --git "a/src/site/notes/notes/408/\347\273\206\345\210\206\344\270\244\347\247\215\345\217\230\345\235\200\345\257\273\345\235\200\360\237\244\224.md" "b/src/site/notes/notes/408/\347\273\206\345\210\206\344\270\244\347\247\215\345\217\230\345\235\200\345\257\273\345\235\200\360\237\244\224.md" deleted file mode 100644 index 02090a4..0000000 --- "a/src/site/notes/notes/408/\347\273\206\345\210\206\344\270\244\347\247\215\345\217\230\345\235\200\345\257\273\345\235\200\360\237\244\224.md" +++ /dev/null @@ -1,131 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/关于寻址方式","permalink":"/408/关于寻址方式/"} ---- - - -### 1. 变址寻址 (Indexed Addressing) - -- **一句话说明:** 有效地址由“基准地址(在指令中)”加上“偏移量(在变址寄存器中)”得到。 - -- **生活比喻:** 我告诉你我们的基地在“人民路”(基准地址),你要找的人住在从基地出发沿着门牌号走“100号”的位置(偏移量)。你的最终目的地 = 人民路 + 100号。 - -- **指令示例:** `MOV AX, 100[SI]` - - - `SI` 是变址寄存器 (Source Index)。 - - - **含义:** 有效地址 `EA = 100 + (SI)`,其中 `(SI)` 表示 `SI` 寄存器中的内容。然后取出主存地址 `EA` 处的数据放入 `AX`。 - -- **执行分析:** - - 1. CPU取指、译码。 - - 2. CPU将指令中的形式地址 `100` 和变址寄存器 `SI` 的内容在ALU中相加,得到有效地址 `EA`。 - - 3. CPU使用 `EA` 访问主存,取出操作数。 - - 4. 将操作数送入 `AX`。 - -- **主要用途:** 处理数组。指令中的 `100` 是数组的首地址,变址寄存器 `SI` 中存放元素的下标(乘以元素大小后的偏移)。通过循环改变 `SI` 的值,就可以方便地遍历数组。 - -- **考点陷阱:** 变址寄存器的内容是可变的(通常是循环变量),而指令中的地址是固定的(通常是数组首地址)。 - - -### 总结对比表 - -|寻址方式|有效地址 EA 的计算公式|操作数位置|访存次数|主要用途| -|---|---|---|---|---| -|**立即**|(无,操作数在指令中)|指令内部|0|赋常量| -|**直接**|`EA = D`|主存|1|访问静态变量| -|**间接**|`EA = (D)`|主存|2|指针、动态内存| -|**寄存器**|(无,操作数在寄存器中)|寄存器|0|高频数据操作| -|**寄存器间接**|`EA = (R)`|主存|1|按地址访问数据结构| -|**变址**|`EA = D + (IX)`|主存|1|数组遍历| -|**基址**|`EA = (BR) + D`|主存|1|程序重定位| -|**相对**|`EA = (PC) + D` (这是转移地址)|指令流(下一条指令)|1 (取指)|程序分支、循环| - -**注:** `D`=指令中的形式地址, `R`=通用寄存器, `IX`=变址寄存器, `BR`=基址寄存器, `PC`=程序计数器, `(X)`=X的内容。访存次数指为获取一个操作数而访问主存的次数。 - -### 一、 答案 - -在现代计算机中,变址寄存器通常**存放的是原始的数组下标**,由CPU硬件在计算有效地址时**自动完成“下标乘以元素大小”**这个操作;但在简化的教学模型或一些早期设计中,也可能是要求程序员(或编译器)**事先算好偏移量**(即下标 * 元素大小)再放入变址寄存器。 - -### 二、两种变址寻址 - -变址寻址的两种实现模式:**基础变址寻址**和**比例变址寻址 (Scaled Indexed Addressing)**。 - -#### 模式一:基础模型(编译器/程序员负责计算偏移量) - -在这种简化的、经典的教学模型中,CPU的地址生成单元只包含一个加法器。 - -- **变址寄存器的角色:** 存放的是**字节偏移量** (Byte Offset)。 - -- **有效地址计算公式:** `EA = Base Address + (IX)` - - - 这里的 `(IX)` 就是最终的字节偏移。 - -- **操作过程:** - - 1. **程序员/编译器层面:** 程序员使用逻辑下标 `i` (例如 `i = 3`)。如果要访问一个4字节的整型数组 `int arr[]`,编译器需要生成额外的指令,计算出字节偏移量 `Offset = i * 4 = 12`。 - - 2. **指令执行前:** 必须有一条指令将计算出的 `Offset` (12) 载入变址寄存器 `IX`。 - - 3. **变址寻址指令执行:** CPU执行 `MOV AX, arr_base[IX]` 时,直接将 `arr_base` 和 `IX` 中的 `12` 相加得到有效地址。 - - -**伪代码示例 (访问 `arr[i]`, int类型):** - -代码段 - -``` -; 假设 i 存放在 CX 寄存器中 -MOV AX, CX ; 将 i 复制到 AX -SHL AX, 2 ; 左移两位,等效于 AX = AX * 4,计算字节偏移量 -MOV SI, AX ; 将计算好的字节偏移量放入变址寄存器 SI -MOV BX, arr[SI] ; 执行变址寻址,EA = arr基地址 + (SI) -``` - -- **结论:** 在这种模式下,变址寄存器里存放的是**已经乘好的**字节偏移量。乘法操作是在执行变址寻址**之前**由独立的指令完成的。 - - -#### 模式二:比例变址模型(CPU硬件负责计算偏移量) - -在现代CPU(如x86系列)中,为了提高处理数组的效率,地址生成单元硬件本身就包含了移位器或乘法器,可以直接处理比例因子。 - -- **变址寄存器的角色:** 存放的是**逻辑下标** (Logical Index)。 - -- **有效地址计算公式:** `EA = Base Address + (IX) * Scale` - - - `Scale` 是比例因子,大小等于数组元素的大小(1, 2, 4, 8)。这个比例因子通常由操作码或指令的其他部分隐式或显式地指定。 - -- **操作过程:** - - 1. **程序员/编译器层面:** 程序员使用逻辑下标 `i`。编译器直接将 `i` 的值生成指令载入变址寄存器 `IX`。 - - 2. **指令执行前:** `IX` 中存放的就是 `i` 本身(例如 `3`)。 - - 3. **变址寻址指令执行:** CPU在**一条指令的地址计算周期内**,自动完成 `(IX) * Scale` 和加法操作。硬件电路并行地或流水地完成这些计算。 - - -**x86汇编示例 (访问 `arr[i]`, int类型, 即dword):** - -代码段 - -``` -; 假设 i 存放在 ECX 寄存器中, arr 基地址在 EBX 中 -MOV EAX, [EBX + ECX * 4] ; 一条指令完成所有操作 -``` - -- **解释:** 这条指令明确告诉CPU: - - - 基地址在 `EBX` 中。 - - - 逻辑下标在 `ECX` 中。 - - - 比例因子是 `4` (因为是 `dword` 数组)。 - - - CPU硬件会计算 `(EBX) + (ECX) * 4` 来得到最终的有效地址。 - -- **结论:** 在这种模式下,变址寄存器里存放的是**原始的数组下标**。乘法操作是作为地址计算的一部分,由CPU硬件在执行该指令**期间自动完成的**。 - - ---- diff --git "a/src/site/notes/notes/408/\347\273\252\350\256\272\350\277\233\347\250\213.md" "b/src/site/notes/notes/408/\347\273\252\350\256\272\350\277\233\347\250\213.md" deleted file mode 100644 index 7118505..0000000 --- "a/src/site/notes/notes/408/\347\273\252\350\256\272\350\277\233\347\250\213.md" +++ /dev/null @@ -1,4 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/绪论进程","permalink":"/408/绪论进程/"} ---- - diff --git "a/src/site/notes/notes/408/\350\231\232\346\213\237\345\234\260\345\235\200\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\245\260.md" "b/src/site/notes/notes/408/\350\231\232\346\213\237\345\234\260\345\235\200\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\245\260.md" deleted file mode 100644 index a74484b..0000000 --- "a/src/site/notes/notes/408/\350\231\232\346\213\237\345\234\260\345\235\200\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\245\260.md" +++ /dev/null @@ -1,85 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/虚拟地址的生命周期🥰","permalink":"/408/虚拟地址的生命周期🥰/"} ---- - - -### **虚拟地址的生命周期** - -虚拟地址的生命周期与它所属的**进程 (Process)** 的生命周期是紧密绑定的。我们可以将其划分为四个主要阶段: - -#### **第一阶段:孕育期 (Gestation) — 在编译链接时** - -- **产生**: 严格来说,虚拟地址的“蓝图”在程序被编译和链接成可执行文件(如Windows的`.exe`或Linux的ELF文件)时就已经被规划好了。编译器将源代码转换为包含相对地址的目标代码,链接器则将多个目标文件和库组合起来,为整个程序创建一个**统一的、线性的逻辑地址空间映像**。例如,链接器会决定`.text`段(代码段)从虚拟地址`0x08048000`开始,`.data`段(数据段)从另一个地址开始,等等。 - -- **状态**: 在这个阶段,虚拟地址仅仅是文件中的**元数据或偏移量**。它还没有与任何实际的物理内存产生关联,只是一个存在于磁盘文件中的“规划图”。 - - -#### **第二阶段:诞生期 (Birth) — 在进程创建与加载时** - -- **产生**: 当用户执行一个程序时,操作系统会创建一个新的进程。在这个过程中,虚拟地址空间才真正“活”了过来。操作系统的加载器(Loader)会: - - 1. 为新进程创建一个**进程控制块(PCB)**。 - - 2. 为该进程创建一套**地址映射表**(即页目录和页表)。 - - 3. 解析可执行文件,根据“规划图”在进程的虚拟地址空间中划定区域(如代码区、数据区、BSS区、堆、栈等)。 - - 4. **将页目录表的基地址装入特定寄存器**(如x86的CR3寄存器)。 - -- **状态**: 从此刻起,一个独立的、私有的虚拟地址空间正式诞生。CPU在执行这个进程时,其发出的所有地址都将被视为这个空间内的虚拟地址。 - - -#### **第三阶段:活跃期 (Active Life) — 在进程执行时** - -- **使用**: 这是虚拟地址最活跃的时期。当CPU执行该进程的指令时,它会不断地产生虚拟地址(用于取指令、读写数据)。这些虚拟地址会被CPU中的**内存管理单元(MMU)** 截获,并通过查询页表将其**翻译**成物理地址,最终访问物理内存。 - -- **变化**: 这个时期的虚拟地址空间不是一成不变的: - - - **堆的增长**: 当程序调用`malloc()`或`new`时,操作系统会扩展堆区域,**凭空创造**出新的可用虚拟地址供程序使用。 - - - **栈的增长**: 当函数调用发生时,栈会向低地址方向增长,使用新的虚拟地址。 - - - **动态库加载**: 程序可能会在运行时加载新的动态链接库(DLL/.so),这也会将新的库映射到虚拟地址空间的某个区域。 - - - **内存释放**: 当调用`free()`或`delete`时,对应的虚拟地址被标记为可用,但其映射关系通常不会立即撤销,可能会被后续的`malloc`重用。 - - -#### **第四阶段:消亡期 (Disappearance) — 在进程终止时** - -- **消失**: 当一个进程终止时(无论是正常退出还是异常崩溃),操作系统会回收其所有资源。在内存方面,操作系统会: - - 1. 遍历该进程的地址映射表。 - - 2. 释放该进程占用的所有**物理内存页框**。 - - 3. **销毁该进程的页目录和所有页表**。 - -- **状态**: 一旦一个进程的页表被销毁,其对应的整个虚拟地址空间就彻底不复存在了。原来那些虚拟地址对于CPU和系统来说,重新变回了毫无意义的数字。 - - ---- - -### **虚拟地址的使用范围** - -理解了生命周期,虚拟地址的使用范围就非常清晰了。 - -#### **1. 范围的起点:CPU** - -虚拟地址的“管辖范围”始于**中央处理器(CPU)**。当一个进程在运行时,CPU内部的程序计数器(PC)存储的是下一条指令的**虚拟地址**,CPU执行的访存指令(如`mov`)操作的也都是**虚拟地址**。 - -#### **2. 范围的终点:内存管理单元(MMU)** - -虚拟地址的“生命”终结于**内存管理单元(MMU)**。MMU是CPU内部的一个硬件模块,它的职责就是接收来自CPU核心的虚拟地址,然后通过查询页表(和高速缓存TLB)将其翻译成物理地址。一旦地址离开了MMU,进入到**物理地址总线**上,它就变成了**物理地址**。主存(内存条)、内存控制器等硬件只认识物理地址。 - -#### **3. 范围的边界:进程** - -虚拟地址最核心的范围限定就是**进程 (Process)**。 - -- **私有性**: 每个进程都拥有自己的一套独立的、从0开始的虚拟地址空间。 - -- **隔离性**: 进程A的虚拟地址`0x1000`和进程B的虚拟地址`0x1000`是**完全不同**的两个地址,它们通过各自独立的页表,被映射到不同的物理内存位置。 - -- **上下文**: 一个虚拟地址,如果脱离了它的进程上下文(即不知道它属于哪个进程,不知道该用哪张页表去翻译它),它就是**没有意义的**。操作系统在进行进程切换时,最关键的一步就是**切换CR3寄存器**,即更换当前生效的页目录表,从而切换到新进程的虚拟地址空间“宇宙”中。 - - -**总结一下范围**:虚拟地址是操作系统为**单个进程**提供的、在**CPU内部使用**的一种地址抽象,其有效性由该进程的**地址映射表**所定义,并在**MMU**处被转换为物理世界的地址。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/\350\256\276\345\244\207\347\233\270\345\205\263\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204\344\273\245\345\217\212\346\240\271\346\215\256\351\200\273\350\276\221\350\256\276\345\244\207\345\220\215\345\210\206\351\205\215\350\256\276\345\244\207\347\232\204\345\205\267\344\275\223\346\265\201\347\250\213.md" "b/src/site/notes/notes/408/\350\256\276\345\244\207\347\233\270\345\205\263\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204\344\273\245\345\217\212\346\240\271\346\215\256\351\200\273\350\276\221\350\256\276\345\244\207\345\220\215\345\210\206\351\205\215\350\256\276\345\244\207\347\232\204\345\205\267\344\275\223\346\265\201\347\250\213.md" deleted file mode 100644 index f5d29ce..0000000 --- "a/src/site/notes/notes/408/\350\256\276\345\244\207\347\233\270\345\205\263\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204\344\273\245\345\217\212\346\240\271\346\215\256\351\200\273\350\276\221\350\256\276\345\244\207\345\220\215\345\210\206\351\205\215\350\256\276\345\244\207\347\232\204\345\205\267\344\275\223\346\265\201\347\250\213.md" +++ /dev/null @@ -1,233 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/设备相关的数据结构以及根据逻辑设备名分配设备的具体流程","permalink":"/408/设备相关的数据结构以及根据逻辑设备名分配设备的具体流程/"} ---- - - -## 1. 设备控制表 (DCT) -| **字段名称** | **说明** | **典型内容/示例** | -| -------------------------- | -------------------------------- | ------------------------------------------------------- | -| **设备标识符 (Device ID/Name)** | 唯一标识该设备的名称或编号。 | `打印机P1`, `磁盘D0`, `鼠标` | -| **设备类型 (Device Type)** | 描述设备的类别。 | `字符设备`, `块设备`, `专用设备`, `共享设备` | -| **设备状态 (Device State)** | 反映设备当前的可用性。 | `空闲 (Idle)`, `忙碌 (Busy)`, `脱机 (Offline)`, `故障 (Faulty)` | -| **设备特性表指针** | 指向一个包含设备详细技术参数的区域。 | 指向内存中存储设备速率、容量、寻道时间等信息的地址 | -| **设备等待队列头指针** | 指向等待使用该设备的进程请求队列的队首。 | 指向一个**设备请求块 (DRB)** 链表的头结点 | -| **占有设备进程ID** | 如果设备当前忙碌,记录当前占用该设备的进程的PID。 | `PID = 1234` | -| **缓冲区地址** | 对于某些设备,可能需要专属的I/O缓冲区地址。 | `0xABCDEF00` (缓冲区起始地址) | -| **COCT指针** | 指向其所属的设备控制器对应的**控制器控制表 (COCT)**。 | 指向内存中该控制器COCT的地址 | - ---- - - - -## 2. 控制器控制表 (COCT) ---- - -|**字段名称**|**说明**|**典型内容/示例**| -|---|---|---| -|**控制器标识符 (Controller ID)**|唯一标识该控制器的编号。|`磁盘控制器C1`, `打印机控制器C2`| -|**控制器类型 (Controller Type)**|描述控制器的功能或所连接的设备类型。|`磁盘控制器`, `网络控制器`, `通用I/O控制器`| -|**控制器状态 (Controller State)**|描述控制器自身的当前状态。|`空闲 (Idle)`, `忙碌 (Busy)`, `故障 (Faulty)`| -|**连接设备列表/DCT指针集合**|包含一个列表或一组指针,指向所有连接到该控制器上的**设备控制表 (DCT)**。|包含 `DCT_P1`, `DCT_D0`, `DCT_D1` 等指针| -|**当前处理I/O请求指针**|指向控制器当前正在处理的I/O请求或对应的DCT。|指向当前正在进行I/O的设备的DCT地址| -|**控制器中断向量**|控制器完成I/O或发生错误时,向CPU发出的中断的地址。|`中断向量号 32`| -|**CHCT指针**|指向其所属的通道对应的**通道控制表 (CHCT)**。|指向内存中该通道CHCT的地址| - ---- - - - -## 3. 通道控制表 (CHCT) ---- - -|**字段名称**|**说明**|**典型内容/示例**| -|---|---|---| -|**通道标识符 (Channel ID)**|唯一标识该I/O通道的编号。|`通道0`, `通道1`| -|**通道类型 (Channel Type)**|区分通道的工作模式。|`选择通道 (Selector Channel)`, `多路复用通道 (Multiplexer Channel)`| -|**通道状态 (Channel State)**|描述通道自身的当前状态。|`空闲 (Idle)`, `忙碌 (Busy)`, `故障 (Faulty)`| -|**通道程序地址**|指向通道当前正在执行的**通道程序**(由一系列通道指令字CCW组成)的内存起始地址。|`0x10002000` (通道程序起始地址)| -|**连接控制器列表/COCT指针集合**|包含一个列表或一组指针,指向所有连接到该通道上的**控制器控制表 (COCT)**。|包含 `COCT_C1`, `COCT_C2` 等指针| -|**中断处理信息**|包含通道完成或异常时生成中断的相关信息。|`中断处理例程地址`, `通道状态字`| -|**数据计数器**|记录当前I/O操作已传输的数据量。|`1024` (已传输字节数)| -|**状态字**|通道执行状态和错误信息的集合。|`0x0000` (正常完成), `0x8001` (设备错误)| - ---- - - - -## 4. 系统设备表 (SDT) ---- - -|**字段名称**|**说明**|**典型内容/示例**| -|---|---|---| -|**设备列表/数组**|SDT通常以数组或链表的形式组织,存储系统中所有设备的条目。|一个由多个条目构成的数组或链表| -|**指向DCT的指针集合**|列表中每个条目都包含一个指向对应**设备控制表 (DCT)** 的指针。|包含 `DCT_P1` 的地址, `DCT_D0` 的地址, `DCT_MOUSE` 的地址等| -|**设备ID到DCT指针的映射**|支持通过设备ID作为索引或键来快速查找对应的DCT指针。|内部查找表或哈希表实现,例如 `map["打印机P1"] = DCT_P1的地址`| -|**系统设备总数**|记录当前系统中物理设备的数量。|`5` (表示系统中有5个物理设备)| - ---- - - - -## 依据逻辑设备名分配设备的具体例子 -在这个例子中,我们假设一个用户进程 **P1** 需要使用一台**打印机**来打印文档,但它**不关心具体是哪一台物理打印机**。它只请求一个名为“`我的打印机`”的逻辑设备。 - -**场景设定:** - -- **进程 P1**:需要打印一份文档,发出I/O请求,指定逻辑设备名为“`我的打印机`”。 - -- **系统配置**: - - - 系统中有两台物理打印机:**打印机P1** 和 **打印机P2**。 - - - **打印机P1** 连接到 **打印机控制器C2**,而 **控制器C2** 连接到 **通道CH0**。 - - - **打印机P2** 连接到 **打印机控制器C3**,而 **控制器C3** 也连接到 **通道CH0**。 - - - 系统中维护着一个**逻辑设备表 (LDT)**,用于将逻辑设备名映射到物理设备名。 - -- **逻辑设备表 (LDT) 示例**: - - -|**逻辑设备名 (Logical Device Name)**|**物理设备名 (Physical Device Name)**|**状态/备注**| -|---|---|---| -|`我的打印机`|`打印机P1`|优先分配,可轮换| -|`我的打印机`|`打印机P2`|备用,可轮换| -|`文档扫描仪`|`扫描仪S1`|唯一映射| - -- **初始系统状态(假设)**: - - - **打印机P1** 及其控制器C2、通道CH0都处于**空闲**状态。 - - - **打印机P2** 及其控制器C3、通道CH0也处于**空闲**状态。 - - ---- - -### **设备分配步骤详解及示例:** - -#### **1. 解析逻辑设备名,查找逻辑设备表 (LDT) 确定物理设备名** -- **进程P1的请求**:进程P1执行 `print("文档内容", "我的打印机")` 这样的系统调用,将“`我的打印机`”作为参数传递给操作系统。 - -- **操作系统操作**: - - - 操作系统接收到P1的请求,识别到逻辑设备名“`我的打印机`”。 - - - 它会立即查找内存中的**逻辑设备表 (LDT)**。 - - - 在LDT中,“`我的打印机`”映射到了**`打印机P1`** 和 **`打印机P2`**。 - - - 操作系统会根据某种策略(例如,轮询、负载均衡、或简单的查找顺序),选择一个可用的物理设备。 - - - **示例**:假设操作系统首先尝试分配**`打印机P1`**。此时,操作系统已将逻辑设备名“`我的打印机`”映射到了物理设备名“**`打印机P1`**”。 - - -#### **2. 查找系统设备表 (SDT) 定位设备控制表 (DCT)** -- **操作系统操作**: - - - 获得物理设备名“`打印机P1`”后,操作系统会去查找**系统设备表 (SDT)**。 - - - 在SDT中,根据“`打印机P1`”这个物理设备名,找到对应的**打印机P1的设备控制表 (DCT_P1)** 的内存地址。 - - -#### **3. 判断设备状态并尝试分配设备(DCT操作)** -- **操作系统操作**: - - - 操作系统访问 **DCT_P1**,检查其**设备状态**字段。 - - - **示例情况A:物理设备 `打印机P1` 空闲** - - - 假设 `DCT_P1.设备状态` 当前为 **`空闲`**。 - - - 操作系统将 `DCT_P1.设备状态` 更新为 **`忙碌`**。 - - - 将 `DCT_P1.占有设备进程ID` 设置为 **`P1`**。 - - - **成功分配物理设备 `打印机P1` 给进程P1。** - - - **示例情况B:物理设备 `打印机P1` 忙碌 (回溯或尝试其他设备)** - - - 如果 `DCT_P1.设备状态` 当前为 **`忙碌`**。 - - - 操作系统会回到步骤1,**重新查找LDT**,尝试分配映射到“`我的打印机`”的**下一个物理设备**,即 **`打印机P2`**。 - - - 它会继续查找 `打印机P2` 对应的 **DCT_P2**,并重复步骤3的检查和分配流程。 - - - **如果所有映射的物理设备都忙碌**:此时,进程P1的PCB(或DRB)会被添加到**逻辑设备“`我的打印机`”对应的逻辑等待队列中**(一个抽象的队列,可能由操作系统统一管理,或最终分散到实际物理设备的等待队列中,但此处逻辑上先等待逻辑设备空闲)。P1进入**等待状态**。 - -- **接续示例(假设 `打印机P1` 空闲,分配成功)**: - - - 操作系统已成功将物理设备 `打印机P1` 分配给进程P1。 - - - 接下来,操作系统会通过 `DCT_P1` 中存储的 **`COCT指针`**,找到并访问与打印机P1相连的**打印机控制器C2的控制器控制表 (COCT_C2)**。 - - -#### **4. 判断控制器状态并尝试分配控制器(COCT操作)** -- **操作系统操作**: - - - 操作系统访问 **COCT_C2**,检查其**控制器状态**字段。 - - - **示例情况A:控制器C2空闲** - - - 假设 `COCT_C2.控制器状态` 当前为 **`空闲`**。 - - - 操作系统将 `COCT_C2.控制器状态` 更新为 **`忙碌`**。 - - - **成功分配控制器C2给进程P1。** - - - **示例情况B:控制器C2忙碌 (回溯)** - - - 如果 `COCT_C2.控制器状态` 当前为 **`忙碌`**。 - - - 由于控制器忙碌意味着当前尝试的物理设备(打印机P1)及其路径都不可用,操作系统会**回溯**到步骤1,再次通过LDT尝试分配“`我的打印机`”对应的**下一个物理设备(`打印机P2`)**。 - - - 如果所有物理设备及其控制器都忙碌,进程P1将进入等待状态。 - -- **接续示例(假设控制器C2空闲,分配成功)**: - - - 操作系统已成功将控制器C2分配给进程P1。 - - - 接下来,操作系统会通过 `COCT_C2` 中存储的 **`CHCT指针`**,找到并访问与控制器C2相连的**通道CH0的通道控制表 (CHCT_CH0)**。 - - -#### **5. 判断通道状态并尝试分配通道(CHCT操作)** -- **操作系统操作**: - - - 操作系统访问 **CHCT_CH0**,检查其**通道状态**字段。 - - - **示例情况A:通道CH0空闲** - - - 假设 `CHCT_CH0.通道状态` 当前为 **`空闲`**。 - - - 操作系统将 `CHCT_CH0.通道状态` 更新为 **`忙碌`**。 - - - **成功分配通道CH0给进程P1。** - - - **示例情况B:通道CH0忙碌 (回溯)** - - - 如果 `CHCT_CH0.通道状态` 当前为 **`忙碌`**。 - - - 由于通道是整个I/O路径的“瓶颈”(所有通过此通道的设备都会受其影响),通道忙碌意味着当前尝试的物理设备(打印机P1)及其路径都不可用。操作系统会**回溯**到步骤1,再次通过LDT尝试分配“`我的打印机`”对应的**下一个物理设备(`打印机P2`)**。 - - - **注意**:在这个例子中,`打印机P1` 和 `打印机P2` 都连接到 `通道CH0`。如果 `CH0` 忙碌,那么无论尝试 `P1` 还是 `P2`,都会遇到通道忙碌的问题。在这种情况下,进程P1最终将进入等待状态,直到 `CH0` 空闲。 - - ---- - - -### **分配成功与后续操作** -在上述例子中,如果**打印机P1** (或P2)、**控制器C2** (或C3)、**通道CH0** 三者都成功分配给了进程P1,那么这次设备分配才算真正**成功**。 - -1. **启动I/O操作**:操作系统为进程P1的打印请求构建通道程序,并将通道程序的起始地址写入 **`CHCT_CH0.通道程序地址`** 字段,然后启动通道CH0。 - -2. **数据传送**:通道CH0独立执行,控制对应的控制器(C2或C3)驱动物理打印机(P1或P2)进行打印。 - -3. **I/O完成与释放**:打印任务完成后,通过中断通知操作系统。操作系统会依次将 **`CHCT_CH0.通道状态`**、**`COCT_C2/C3.控制器状态`** 和 **`DCT_P1/P2.设备状态`** 重新设置为 **`空闲`**。同时,检查各自的等待队列,唤醒任何在等待使用这些资源的进程。 - - -这个例子强调了: - -- **逻辑设备独立性**:用户进程无需知道具体的物理设备名,提高了程序的通用性和可移植性。 - -- **灵活性**:操作系统可以动态选择可用的物理设备,提高了设备利用率和系统吞吐量。 - -- **回溯机制**:当尝试分配某个物理设备路径失败时(设备、控制器或通道忙碌),操作系统可以智能地尝试其他备用物理设备。 diff --git "a/src/site/notes/notes/408/\350\256\276\345\244\207\351\251\261\345\212\250\347\250\213\345\272\217\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\230\256\342\200\215\360\237\222\250.md" "b/src/site/notes/notes/408/\350\256\276\345\244\207\351\251\261\345\212\250\347\250\213\345\272\217\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\230\256\342\200\215\360\237\222\250.md" deleted file mode 100644 index 837caa6..0000000 --- "a/src/site/notes/notes/408/\350\256\276\345\244\207\351\251\261\345\212\250\347\250\213\345\272\217\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237\360\237\230\256\342\200\215\360\237\222\250.md" +++ /dev/null @@ -1,106 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/设备驱动程序的生命周期😮💨","permalink":"/408/设备驱动程序的生命周期😮💨/"} ---- - - - -### **设备驱动程序的完整工作流程** - -#### **第零步:驱动的初始化与注册 (在系统启动或模块加载时)** - -在任何I/O操作发生前,驱动程序必须先让内核“认识”它。 - -1. **加载与初始化**: 系统启动时或管理员加载模块时,内核会运行驱动程序的`init()`初始化函数。 - -2. **探测硬件**: 驱动程序会探测总线,检查它所负责的硬件设备是否存在且工作正常。 - -3. **注册接口**: 这是最关键的一步。驱动程序向内核注册一个包含一系列**函数指针**的结构体(在Linux中称为`file_operations`)。 - - - **作用**: 这相当于驱动程序向内核立下字据:“尊敬的内核,我是硬盘驱动。以后只要有针对硬盘的`open`, `read`, `write`, `ioctl`等操作请求,请调用我这里的`my_disk_open`, `my_disk_read`, `my_disk_write`等函数地址。” - - - 从此,内核就知道该如何将上层的通用I/O请求转发给这个具体的驱动程序了。 - - ---- - -## _以下流程是一个具体的`read`请求到达后的生命周期_ - -#### **第一步:接收上层请求(“Top Half”的开始)** - -1. 一个进程发起`read`系统调用,经过**设备无关的I/O层**处理后,内核确定需要从磁盘读取某个物理块。 - -2. 设备无关层会调用在第零步中注册的函数指针,即调用磁盘驱动程序的`my_disk_read()`函数,并将请求的块号、目标内存缓冲区地址等信息作为参数传入。 - -3. **驱动程序代码开始执行。** 这部分由系统调用直接触发,在进程的上下文中运行,我们称之为驱动的**“上半部 (Top Half)”**。 - - -#### **第二步:参数校验与设备状态检查** - -1. 驱动程序首先会检查上层传来的参数是否合法。例如,请求的块号是否超出了磁盘的容量范围? - -2. 接着,检查硬件设备当前是否正忙于处理上一个请求。如果设备忙,新的请求可能会被放入一个**请求队列**中等待。如果设备空闲,则继续。 - - -#### **第三步:对硬件编程(核心翻译工作)** - -这是驱动程序的核心价值所在。它需要将上层“读逻辑块X”这样抽象的命令,翻译成磁盘控制器能听懂的“电子语言”。 - -1. **准备DMA传输**: 驱动程序向内核申请一块用于DMA(直接内存访问)的内存缓冲区,并获取其物理地址。 - -2. **设置控制器寄存器**: 驱动程序通过特定的I/O端口地址,向磁盘控制器的硬件寄存器中写入一系列值,例如: - - - **目标内存地址寄存器**: 写入DMA缓冲区的物理地址。 - - - **扇区计数寄存器**: 写入要读取的扇区数量(例如一个块包含8个扇区,就写入8)。 - - - **LBA寄存器**: 写入要读取的逻辑块地址(LBA)。 - - - **命令寄存器**: 最后,向命令寄存器写入**“开始读”**的命令码。 - - -#### **第四步:阻塞当前进程** - -- 向命令寄存器写入命令后,硬件就开始了漫长的物理操作(寻道、旋转、读取)。这个过程可能耗时数毫秒,CPU不能在此空等。 - -- 驱动程序会调用内核的调度器函数,将发起此次I/O请求的进程状态设置为**“阻塞(Blocked)”或“等待(Waiting)”**,并将其放入等待队列。 - -- 然后,驱动程序的`my_disk_read()`函数**返回**,将CPU控制权交还给调度器。调度器会选择另一个处于“就绪”状态的进程投入运行。 - - -_(至此,上半部工作完成,CPU已转去处理其他任务,静待硬件佳音)_ - ---- - -#### **第五步:处理硬件中断(“Bottom Half”的开始)** - -1. 磁盘硬件完成了数据读取,并通过DMA控制器将数据成功写入到了第三步中指定的内存DMA缓冲区。 - -2. 为了通知任务完成,磁盘控制器会向CPU发送一个**中断信号**。 - -3. CPU无论正在做什么,都会立即暂停,保存当前现场,然后根据中断向量表,跳转到该中断对应的**中断服务例程(ISR)**——这正是磁盘驱动程序预先注册好的中断处理函数。 - -4. 这部分在中断上下文中运行的代码,我们称之为驱动的**“下半部 (Bottom Half)”**。 - - -#### **第六步:中断善后与唤醒进程** - -中断处理程序(下半部)必须**执行得非常快**。它通常会做以下几件事: - -1. **读取状态**: 从磁盘控制器的状态寄存器中读取I/O操作的结果,检查是否发生了错误。 - -2. **数据处理**: 如果有错误,则记录错误信息。如果成功,则通知上层数据已准备好。 - -3. **唤醒进程**: 调用内核函数,将在第四步中被阻塞的那个进程从等待队列中移出,将其状态改为**“就绪(Ready)”**,并放入就绪队列,等待调度器再次垂青。 - -4. **返回**: 从中断处理程序返回。CPU恢复之前被中断的现场,继续执行。 - - -#### **第七步:请求完成与返回用户** - -- 当调度器再次选择运行我们那个刚刚被唤醒的进程时,它的执行会从当初`my_disk_read()`函数中被阻塞的地方继续。 - -- 此时,驱动程序代码知道I/O已经完成,它会进行一些清理工作。 - -- 数据此时已经位于内核的DMA缓冲区中,设备无关层会负责将其从内核缓冲区**复制**到用户最初指定的缓冲区`buf`中。 - -- 最后,整个系统调用完成,返回到用户态,用户程序从`read()`函数调用处继续执行,并在`buf`中得到了它想要的数据。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/\350\265\204\346\272\220\345\210\206\351\205\215\345\233\276.md" "b/src/site/notes/notes/408/\350\265\204\346\272\220\345\210\206\351\205\215\345\233\276.md" deleted file mode 100644 index 01e2fc2..0000000 --- "a/src/site/notes/notes/408/\350\265\204\346\272\220\345\210\206\351\205\215\345\233\276.md" +++ /dev/null @@ -1,78 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/资源分配图","permalink":"/408/资源分配图/"} ---- - - -### 1. 资源分配图 (RAG) 的构成 - -资源分配图是一个有向图,由两类节点和两种有向边构成: - -- **两类节点**: - - 1. **进程节点 (Process Node)**: 通常用**圆形**表示,内部写有进程的标识符(如 `P1`, `P2`)。 - - 2. **资源节点 (Resource Node)**: 通常用**矩形**表示,内部写有资源的标识符(如 `R1`, `R2`)。矩形框中的**小圆点或方点**代表该类资源的**实例数量**。例如,一个矩形内有2个点,表示该类资源有2个实例(如系统有2台打印机)。 - -- **两种有向边**: - - 1. **请求边 (Request Edge)**: 从**进程指向资源**的有向边 (`P_i \rightarrow R_j`)。它表示进程 `Pi` 正在**请求**一个 `Rj` 类的资源实例,但尚未获得,正处于等待状态。 - - 2. **分配边 (Assignment Edge)**: 从**资源实例指向进程**的有向边 (`R_j \rightarrow P_i`)。它表示 `Rj` 类中的一个资源实例已经被**分配**给了进程 `Pi`,`Pi` 正在持有并使用它。 - - -### 3. 如何用资源分配图检测死锁 - -使用资源分配图检测死锁的规则,根据资源实例的数量分为两种情况, - -#### 情况一:每类资源只有一个实例 - -当系统中每种类型的资源都只有一个实例时,检测规则非常简单: - -**规则**: **图中存在环路 (Cycle) 是死锁发生的充分必要条件。** - -- **有环路 ⇒ 必有死锁** - -- **有死锁 ⇒ 必有环路** - - -#### 情况二:某些资源类型有多个实例 - -当系统中存在拥有多个实例的资源类型时,情况变得复杂: - -**规则**: **图中存在环路是死锁发生的必要不充分条件。** - -- **有环路 ⇒ 不一定有死锁** (这是你问题的关键) - -- **有死锁 ⇒ 必有环路** - - ---- - -### 4. 为什么“有环路”不一定是死锁?(考点与难点) - -这种情况的本质在于:**虽然一个进程 `P_i` 在环路中等待的资源 `R_j` 被另一个进程 `P_j` 占用,但 `R_j` 可能还有其他空闲的实例,或者环路外的某个进程可能释放一个 `R_j` 的实例来满足 `P_i` 的请求。** - -另一种无死锁的环路情况: - -即使 R1 没有空闲实例,但如果有一个不在环路中的进程 P4 持有 R1 的一个实例,并且 P4 无需再申请其他资源即可运行完毕。那么当 P4 运行完并释放 R1 后,P1 的请求也能被满足,环路同样被打破。 - -如何判断这种复杂情况?——图简化法 (Graph Reduction) - -为了在多实例场景下准确判断死锁,我们需要尝试简化资源分配图: - -1. 首先找到一个**不被阻塞**的进程 `Pi`(即它的所有请求边指向的资源 `Rj` 都有空闲的实例)。 - -2. 如果找不到这样的进程,则图无法简化,图中所有剩余进程都是死锁进程。 - -3. 如果找到了,就**去掉**该进程 `Pi` 的所有请求边和分配边,假装它已运行完毕并释放了所有资源。 - -4. 重复步骤1,继续寻找新的不被阻塞的进程。 - -5. 如果最终能**消除图中所有的边**,则说明系统没有发生死锁;否则,发生了死锁。 - - -**总结**: - -- **单实例资源**: **环路 ⇔ 死锁**,检测非常简单。 - -- **多实例资源**: **死锁 ⇒ 环路**,但**环路 ⇒ 不一定死锁**。必须通过图简化法或类似银行家算法的死锁检测算法来最终确认。这是因为环路中的进程所等待的资源,可能由**空闲实例**或**环路外即将结束的进程**来满足。 \ No newline at end of file diff --git "a/src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257.md" "b/src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257.md" deleted file mode 100644 index 5238152..0000000 --- "a/src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257.md" +++ /dev/null @@ -1,65 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/通道技术","permalink":"/408/通道技术/"} ---- - - -操作系统中的**通道技术**是一种用于高效管理输入/输出(I/O)操作的硬件机制,其核心目标是**将I/O操作从CPU的直接控制中分离出来**,从而提升系统整体性能。以下是其详细解析: - ---- - - - -### 1. **通道的定义与本质** -通道(I/O Channel)是一个**独立于CPU的专用处理机**,专门负责管理外部设备与内存之间的数据交换。它能够接收中央处理机(CPU)的命令,独立执行通道程序,协助CPU控制和管理外部设备 。 -例如,在早期计算机中,CPU需要全程参与数据从磁盘读取到内存的过程,而通道技术引入后,CPU只需发出启动I/O的指令,后续数据传输由通道自动完成,无需CPU干预 。 - ---- - - - -### 2. **通道的核心功能** -- **数据传输控制**: - 通道负责在外设与内存之间直接传输数据,例如从磁盘读取文件或向打印机发送输出数据。这种方式避免了CPU频繁介入,显著减少CPU负载 。 -- **设备状态监控**: - 通道会实时监控外设的状态(如是否空闲、是否发生错误),并在操作完成后向CPU发送中断信号,报告任务完成或异常情况 。 -- **错误处理**: - 在数据传输过程中,通道能检测并处理部分硬件错误(如校验错误),确保数据完整性 。 - ---- - - - -### 3. **通道的引入目的** -- **解放CPU资源**: - 通过将I/O操作从CPU转移到通道,CPU得以专注于计算任务,而非等待外设完成数据传输。例如,若没有通道,CPU需等待磁盘读取数据完成才能继续执行,而通道可并行处理这一过程 。 -- **提升系统并行性**: - 通道与CPU可同时工作,实现指令执行与数据传输的并行化。例如,CPU计算数据的同时,通道可将结果写入磁盘 。 -- **简化I/O管理**: - 通道通过执行预定义的通道程序(Channel Program)自动完成复杂的I/O操作,减少了操作系统内核的管理复杂度 。 - ---- - - - -### 4. **通道的工作机制** -- **通道程序**: - 通道执行一段由操作系统预先编写的通道指令序列(Channel Command Word, CCW),描述数据传输的源地址、目标地址、数据长度等信息。例如,读取磁盘扇区的操作会被分解为多个通道指令 。 -- **中断通知**: - 当通道完成数据传输或发生异常时,会通过中断机制通知CPU,由操作系统处理后续逻辑(如唤醒等待I/O的进程) 。 -- **直接内存访问(DMA)协作**: - 部分通道结合DMA技术,进一步实现外设与内存之间的高速数据交换,完全绕过CPU 。 - ---- - - - -### 5. **通道技术的现实意义** -- **历史演进**: - 通道技术最早出现在大型机系统中(如IBM System/360),是计算机体系结构的重要创新。它标志着I/O操作从“CPU全程控制”向“独立硬件管理”的转变 。 -- **现代应用**: - 尽管现代计算机更多依赖DMA和总线控制器(如PCIe)来管理高速设备,但通道技术的核心思想(如独立I/O处理单元)仍体现在许多硬件设计中,例如网络接口卡(NIC)的卸载功能 。 - ---- - - -[[408(demo)/通道技术和DMA的区别解释\|408(demo)/通道技术和DMA的区别解释]] diff --git "a/src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257\345\222\214DMA\347\232\204\345\214\272\345\210\253\350\247\243\351\207\212.md" "b/src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257\345\222\214DMA\347\232\204\345\214\272\345\210\253\350\247\243\351\207\212.md" deleted file mode 100644 index d76fbb8..0000000 --- "a/src/site/notes/notes/408/\351\200\232\351\201\223\346\212\200\346\234\257\345\222\214DMA\347\232\204\345\214\272\345\210\253\350\247\243\351\207\212.md" +++ /dev/null @@ -1,83 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/通道技术和DMA的区别解释","permalink":"/408/通道技术和DMA的区别解释/"} ---- - - -### 一、核心差异:**干预减少的程度与方式** - -#### 1. **通道技术**(Channel Technology) -- **减少干预的方式**: - - 通过**独立的硬件处理器**(通道)执行完整的I/O操作,CPU仅需启动通道程序,后续操作由通道全权负责。 - - 通道程序(Channel Program)包含多个I/O指令(如读写、跳转),可实现**多步骤、多设备协同的复杂数据流**。例如,从磁带读取数据→内存缓冲→打印机输出的链式流程 。 -- **干预减少的程度**: - - CPU仅在启动I/O时介入一次,后续操作完全由通道接管,甚至支持**通道链式传输**(Chaining),进一步减少CPU干预 。 -- **适用场景**: - - 大型机、专用系统的复杂I/O控制(如IBM System/360通道)。 - - -#### 2. **DMA技术**(Direct Memory Access) -- **减少干预的方式**: - - 通过**硬件控制器**(DMA控制器)直接管理设备与内存之间的数据传输,CPU仅需配置DMA参数(源地址、目标地址、数据长度)。 - - 数据传输完全由DMA控制器完成,无需CPU参与搬运过程 。 -- **干预减少的程度**: - - CPU仅在传输开始前配置参数,传输完成后通过中断通知CPU,中间过程完全由硬件处理。例如,网卡通过DMA将数据包直接写入内存环形缓冲区 。 -- **适用场景**: - - 通用系统的高速数据传输(如硬盘、网卡、GPU显存交换)。 - ---- - - - -### 二、关键区别总结 -| 维度 | DMA技术 | 通道技术 | -| ----------- | ------------------ | ------------------ | -| **核心机制** | 寄存器配置(硬件自动传输) | 程序控制(执行通道程序) | -| **CPU干预程度** | 配置参数后零干预 | 启动通道程序后零干预 | -| **数据流复杂度** | 点对点传输(设备↔内存) | 支持多步骤、多设备协同(如链式传输) | -| **硬件复杂度** | 中(仅需DMA控制器) | 高(需专用处理器与指令集) | -| **历史背景** | 普及于1980s后(PC与通用系统) | 大型机时代(1950s-1960s) | -| | | | - ---- - - - -### 三、为何需要两种技术? - -#### 1. **通道技术的不可替代性** -- **复杂流程控制需求**: - 在需要**多步骤I/O协同**的场景(如大型机磁带机多段传输),通道技术通过程序控制实现精细的数据流管理,而DMA仅能完成单一数据块传输 。 -- **虚拟化与隔离需求**: - 现代虚拟通道技术(如Brocade的Virtual Channel)通过动态隔离数据流,确保关键业务流量的带宽和低延迟,这是DMA无法提供的功能 。 - - -#### 2. **DMA技术的普适性优势** -- **简化硬件与软件模型**: - DMA仅需配置寄存器即可启动传输,无需编写复杂通道程序,降低了驱动开发难度。例如,PCIe设备通过BAR空间配置DMA参数,无需操作系统理解设备内部逻辑 。 -- **适应高速设备需求**: - 现代存储设备(如NVMe SSD)和网络设备(如千兆网卡)依赖DMA的突发传输(Burst Mode)模式,充分利用总线带宽,避免CPU成为瓶颈 。 - ---- - - - -### 四、边界条件与误用风险 -1. **通道技术的局限性**: - - **硬件绑定性强**:通道程序与硬件指令集紧密耦合,移植性差(如IBM通道程序无法在x86系统运行)。 - - **编程复杂度高**:错误的通道指令可能导致硬件死锁(如CCW链表循环)。 - -2. **DMA技术的潜在问题**: - - **缓存一致性风险**:DMA写入内存可能绕过CPU缓存,需显式刷新(如`dma_wmb()`)保证一致性 。 - - **安全漏洞**:恶意设备可通过DMA访问任意内存区域(如Thunderbolt攻击),需IOMMU防护 。 - ---- - - - -### 五、结论:两种技术的互补性 -- **通道技术**: - 以**流程控制为核心**,适用于需要精细管理I/O流程的专用系统(如大型机),注重数据流的逻辑路径规划与多设备协同。 -- **DMA技术**: - 以**传输效率为核心**,适用于通用系统的高速设备数据传输,注重数据流的物理搬运速度与CPU卸载。 - -两者均旨在解除CPU对I/O的直接控制,但**通道技术偏向“流程驱动”**,而**DMA技术偏向“性能驱动”**。开发者需根据应用场景选择合适技术,并严格遵循边界条件(如DMA缓存一致性、通道程序安全性)以避免系统崩溃 。 diff --git "a/src/site/notes/notes/408/\351\241\265\345\274\217\347\256\241\347\220\206\344\270\216\346\256\265\345\274\217\347\256\241\347\220\206\345\206\205\345\255\230.md" "b/src/site/notes/notes/408/\351\241\265\345\274\217\347\256\241\347\220\206\344\270\216\346\256\265\345\274\217\347\256\241\347\220\206\345\206\205\345\255\230.md" deleted file mode 100644 index a06ba6c..0000000 --- "a/src/site/notes/notes/408/\351\241\265\345\274\217\347\256\241\347\220\206\344\270\216\346\256\265\345\274\217\347\256\241\347\220\206\345\206\205\345\255\230.md" +++ /dev/null @@ -1,48 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/408/页式管理与段式管理内存","permalink":"/408/页式管理与段式管理内存/"} ---- - - -### 正确的理解: -**页式管理下的逻辑地址空间是“一维的”,指的是对于用户程序(程序员)而言,他们所使用的地址(即虚拟地址或逻辑地址)是一个单一的、连续的、从0开始的线性序列。** - -让我们来详细解释: - -#### 1. 页式管理:一维的逻辑地址空间 -- **用户程序的视角**:当程序员编写代码时,他们定义变量、函数等,并使用指针等来访问内存。他们所使用的地址(无论是变量名、数组下标还是指针的值)都是**一个单一的数字**,这个数字表示从程序起始位置开始的偏移量。例如,一个变量 `int x` 的地址可能是 `0x1000`,程序员直接使用 `0x1000` 这个单一的数字来引用它。他们**不需要**说“我访问的是第N页的第M个字节”。 - -- **地址的内部拆分**:当CPU生成这个**一维的逻辑地址**(例如 `0x1000`)后,是**硬件(MMU)**在底层**自动且透明地**将这个单一的数字拆分成“页号”和“页内偏移量”。这个拆分过程对程序员是完全**隐藏**的。程序员无需关心页的大小、页的边界、如何计算页号和偏移量。 - -- **无逻辑意义的划分**:页的划分是**物理内存管理层面**的,是**固定大小**的。它不考虑程序本身的逻辑结构(比如,一个函数可能跨越两个页的边界)。对用户来说,页号和页内偏移量这两个概念**没有内在的逻辑意义**,它们只是为了方便硬件进行地址转换而人为进行的物理地址空间的划分。 - - -**简而言之,用户给出一个线性地址(一个数字),MMU将其解释为页号和偏移量。用户本身不关心这个解释过程,也不需要提供两个独立的数字。** - - -#### 2. 段式管理:二维的逻辑地址空间 -- **用户程序的视角**:在段式管理中,逻辑地址被显式地分为“段号”和“段内偏移量”两个部分。这里的**段号是具有逻辑意义的**。 - - - 例如,程序员可能知道他们的代码在“代码段”中,数据在“数据段”中,栈在“栈段”中。 - - - 在一些支持段式寻址的体系结构(如早期的Intel x86处理器在实模式或保护模式的某些寻址方式下)中,程序员或编译器在生成地址时,**确实需要明确指定**是哪个段,以及在该段内的偏移量。例如,一个完整逻辑地址可能表示为 `CS:IP` (代码段寄存器:指令指针) 或 `DS:Offset` (数据段寄存器:偏移量)。这里的 `CS` 或 `DS` 就是段号(或段选择子)。 - - - 用户程序在访问内存时,**会感知到**要访问的是哪个逻辑段,以及该段内的哪个位置。这对应了程序的**逻辑结构**。 - -- **有逻辑意义的划分**:段的划分是**基于程序的逻辑结构**进行的,例如代码段、数据段、栈段、子程序段等。每个段都有其特定的功能和属性。因此,**段号本身携带着程序的逻辑信息**。 - - -**简而言之,用户需要提供两个有逻辑意义的数字:段号(表示哪个逻辑部分)和偏移量(表示该逻辑部分中的位置)。** - - - -### 总结页和段的维度差异: -|特性|页式管理|段式管理| -|---|---|---| -|**用户感知/给出地址**|**一维的线性地址**(一个单一的数字,例如 `0x12345`)|**二维的逻辑地址**(例如 `(代码段, 0x100)` 或 `(数据段, 0x50)`)| -|**地址解析者**|**硬件(MMU)** 自动将一维地址拆分为页号和偏移量|**程序员/编译器/硬件** 共同管理,地址本身就带有段号和偏移量| -|**划分依据**|**物理内存的固定大小划分**,与程序逻辑无关|**程序的逻辑结构**(代码、数据、栈等)划分,与物理内存无关| -|**透明性**|**对用户透明**,用户无需关心页的存在|**对用户不透明**,用户感知并使用段的概念| - -**因此,当说页式管理下的地址空间是“一维的”时,强调的是用户程序所操作的逻辑地址是一个连续的、不区分内部结构的线性地址空间。而段式管理下是“二维的”,强调的是用户程序所使用的逻辑地址本身就包含两个独立的、具有逻辑意义的组成部分(段号和段内偏移)。** - -希望这次的解释能彻底理清您关于地址维度的问题! diff --git "a/src/site/notes/notes/Welcome\360\237\216\211.md" "b/src/site/notes/notes/Welcome\360\237\216\211.md" deleted file mode 100644 index 5c720ce..0000000 --- "a/src/site/notes/notes/Welcome\360\237\216\211.md" +++ /dev/null @@ -1,108 +0,0 @@ ---- -{"dg-publish":true,"dg-permalink":"/Welcome🎉","permalink":"/Welcome🎉/","tags":["gardenEntry"]} ---- - - - - - - 408考研计算机启发性问题集(Demo版) - - - - - - 408考研计算机启发性问题集(Demo版) - 专为考研学子打造的408启发性问题笔记库 - - - - - - - 📢 网站说明 - - 本网站主要收集和整理408考研科目中的启发性问题,旨在帮助同学们开阔思路、发现知识盲区、提升理解深度。 - 注意:本网站不是一份全面的复习指南,也不包含所有基础知识点。请结合权威教材和系统课程进行复习,本网站内容仅供查漏补缺和思维训练参考。 - - - 📢 紧急集结!408老兵连队,虚位以待! - 请即刻加入我们,一同征服考研408的战场! - 点击链接加入群聊【408老兵连队】 - - 📚 目录导航 - - 👉 网页导航 - ⭐️ GitHub 仓库 - 你们千万别来当contributors 我不想看到你们复试简历上面有百星项目的贡献经历 - 💬 QQ群 - 📦 百度网盘群 - - - 数学:张宇、李林、武忠祥等全程课,真题分类与模拟题汇总 - 408:王道、beok全程课,真题分类,专题整理,模拟题与1000题 - 其他:徐涛、田静全程课等 - - 你们千万不要加我的WeChat(yuuri777)和我一起讨论408 千万不要! - - - - - \ No newline at end of file
专为考研学子打造的408启发性问题笔记库
- 本网站主要收集和整理408考研科目中的启发性问题,旨在帮助同学们开阔思路、发现知识盲区、提升理解深度。 - 注意:本网站不是一份全面的复习指南,也不包含所有基础知识点。请结合权威教材和系统课程进行复习,本网站内容仅供查漏补缺和思维训练参考。 -
你们千万不要加我的WeChat(yuuri777)和我一起讨论408 千万不要!