<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Go中的map在底层是用哈希表实现的,你可以在 $GOROOT/src/pkg/runtime/hashmap.goc 找到它的实现。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>数据结构</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">哈希表的数据结构中一些关键的域如下所示:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">struct Hmap{ uint8 B; // 可以容纳2^B个项 uint16 bucketsize; // 每个桶的大小 byte *buckets; // 2^B个Buckets的数组 byte *oldbuckets; // 前一个buckets,只有当正在扩容时才不为空};</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上面给出的结构体只是Hmap的部分的域。需要注意到的是,这里直接使用的是Bucket的数组,而不是Bucket*指针的数组。这意味着,第一个Bucket和后面溢出链的Bucket分配有些不同。第一个Bucket是用的一段连续的内存空间,而后面溢出链的Bucket的空间是使用mallocgc分配的。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这个hash结构使用的是一个可扩展哈希的算法,由hash值mod当前hash表大小决定某一个值属于哪个桶,而hash表大小是2的指数,即上面结构体中的2^B。每次扩容,会增大到上次大小的两倍。结构体中有一个buckets和一个oldbuckets是用来实现增量扩容的。正常情况下直接使用buckets,而oldbuckets为空。如果当前哈希表正在扩容中,则oldbuckets不为空,并且buckets大小是oldbuckets大小的两倍。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">具体的Bucket结构如下所示:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">struct Bucket{ uint8 tophash[BUCKETSIZE]; // hash值的高8位....低位从bucket的array定位到bucket Bucket *overflow; // 溢出桶链表,如果有 byte data[1]; // BUCKETSIZE keys followed by BUCKETSIZE values};</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">其中BUCKETSIZE是用宏定义的8,每个bucket中存放最多8个key/value对, 如果多于8个,那么会申请一个新的bucket,并将它与之前的bucket链起来。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">按key的类型采用相应的hash算法得到key的hash值。将hash值的低位当作Hmap结构体中buckets数组的index,找到key所在的bucket。将hash的高8位存储在了bucket的tophash中。<strong style="box-sizing: border-box;">注意,这里高8位不是用来当作key/value在bucket内部的offset的,而是作为一个主键,在查找时对tophash数组的每一项进行顺序匹配的</strong>。先比较hash值高位与bucket的tophash[i]是否相等,如果相等则再比较bucket的第i个的key与所给的key是否相等。如果相等,则返回其对应的value,反之,在overflow buckets中按照上述方法继续寻找。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">整个hash的存储如下图所示(临时先采用了XX同学画的图,这个图有点问题):</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><img src="https://www.maxiaoke.com/uploads/images/20231217/d3c5ab4f6f0459e5402c019694a9831d.png" alt=""/></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">图2.2 HMap的存储结构</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">注意一个细节是Bucket中key/value的放置顺序,是将keys放在一起,values放在一起,为什么不将key和对应的value放在一起呢?如果那么做,存储结构将变成key1/value1/key2/value2… 设想如果是这样的一个map[int64]int8,考虑到字节对齐,会浪费很多存储空间。不得不说通过上述的一个小细节,可以看出Go在设计上的深思熟虑。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>增量扩容</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">大家都知道哈希表表就是以空间换时间,访问速度是直接跟填充因子相关的,所以当哈希表太满之后就需要进行扩容。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">如果扩容前的哈希表大小为2^B,扩容之后的大小为2^(B+1),每次扩容都变为原来大小的两倍,哈希表大小始终为2的指数倍,则有(hash mod 2^B)等价于(hash & (2^B-1))。这样可以简化运算,避免了取余操作。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">假设扩容之前容量为X,扩容之后容量为Y,对于某个哈希值hash,一般情况下(hash mod X)不等于(hash mod Y),所以扩容之后要重新计算每一项在哈希表中的新位置。当hash表扩容之后,需要将那些旧的pair重新哈希到新的table上(源代码中称之为evacuate), 这个工作并没有在扩容之后一次性完成,而是逐步的完成(在insert和remove时每次搬移1-2个pair),Go语言使用的是增量扩容。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">为什么会增量扩容呢?主要是缩短map容器的响应时间。假如我们直接将map用作某个响应实时性要求非常高的web应用存储,如果不采用增量扩容,当map里面存储的元素很多之后,扩容时系统就会卡往,导致较长一段时间内无法响应请求。不过增量扩容本质上还是将总的扩容时间分摊到了每一次哈希操作上面。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">扩容会建立一个大小是原来2倍的新的表,将旧的bucket搬到新的表中之后,并不会将旧的bucket从oldbucket中删除,而是加上一个已删除的标记。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">正是由于这个工作是逐渐完成的,这样就会导致一部分数据在old table中,一部分在new table中, 所以对于hash table的insert, remove, lookup操作的处理逻辑产生影响。只有当所有的bucket都从旧表移到新表之后,才会将oldbucket释放掉。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">扩容的填充因子是多少呢?如果grow的太频繁,会造成空间的利用率很低, 如果很久才grow,会形成很多的overflow buckets,查找的效率也会下降。 这个平衡点如何选取呢(在go中,这个平衡点是有一个宏控制的(#define LOAD 6.5), 它的意思是这样的,如果table中元素的个数大于table中能容纳的元素的个数, 那么就触发一次grow动作。那么这个6.5是怎么得到的呢?原来这个值来源于作者的一个测试程序,遗憾的是没能找到相关的源码,不过作者给出了测试的结果:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"> LOAD %overflow bytes/entry hitprobe missprobe 4.00 2.13 20.77 3.00 4.00 4.50 4.05 17.30 3.25 4.50 5.00 6.85 14.77 3.50 5.00 5.50 10.55 12.94 3.75 5.50 6.00 15.27 11.67 4.00 6.00 6.50 20.90 10.79 4.25 6.50 7.00 27.14 10.15 4.50 7.00 7.50 34.03 9.73 4.75 7.50 8.00 41.10 9.40 5.00 8.00 %overflow = percentage of buckets which have an overflow bucket bytes/entry = overhead bytes used per key/value pair hitprobe = # of entries to check when looking up a present key missprobe = # of entries to check when looking up an absent key</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">可以看出作者取了一个相对适中的值。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>查找过程</h2><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>根据key计算出hash值。</p></li><li><p>如果存在old table, 首先在old table中查找,如果找到的bucket已经evacuated,转到步骤3。 反之,返回其对应的value。</p></li><li><p>在new table中查找对应的value。</p></li></ol><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这里一个细节需要注意一下。不认真看可能会以为低位用于定位bucket在数组的index,那么高位就是用于key/valule在bucket内部的offset。事实上高8位不是用作offset的,而是用于加快key的比较的。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">do { //对每个桶b //依次比较桶内的每一项存放的tophash与所求的hash值高位是否相等 for(i = 0, k = b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) { if(b->tophash[i] == top) { k2 = IK(h, k); t->key->alg->equal(&eq, t->key->size, key, k2); if(eq) { //相等的情况下再去做key比较... *keyp = k2; return IV(h, v); } } } b = b->overflow; //b设置为它的下一下溢出链} while(b != nil);</pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>插入过程分析</h2><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>根据key算出hash值,进而得出对应的bucket。</p></li><li><p>如果bucket在old table中,将其重新散列到new table中。</p></li><li><p>在bucket中,查找空闲的位置,如果已经存在需要插入的key,更新其对应的value。</p></li><li><p>根据table中元素的个数,判断是否grow table。</p></li><li><p>如果对应的bucket已经full,重新申请新的bucket作为overbucket。</p></li><li><p>将key/value pair插入到bucket中。</p></li></ol><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这里也有几个细节需要注意一下。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在扩容过程中,oldbucket是被冻结的,查找时会在oldbucket中查找,但不会在oldbucket中插入数据。如果在oldbucket是找到了相应的key,做法是将它迁移到新bucket后加入evalucated标记。并且还会额外的迁移另一个pair。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">然后就是只要在某个bucket中找到第一个空位,就会将key/value插入到这个位置。也就是位置位于bucket前面的会覆盖后面的(类似于存储系统设计中做删除时的常用的技巧之一,直接用新数据追加方式写,新版本数据覆盖老版本数据)。找到了相同的key或者找到第一个空位就可以结束遍历了。不过这也意味着做删除时必须完全的遍历bucket所有溢出链,将所有的相同key数据都删除。所以目前map的设计是为插入而优化的,删除效率会比插入低一些。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>map设计中的性能优化</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">读完map源代码发现作者还是做了很多设计上的选择的。本人水平有限,谈不上优劣的点评,这里只是拿出来与读者分享。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">HMap中是Bucket的数组,而不是Bucket指针的数组。好的方面是可以一次分配较大内存,减少了分配次数,避免多次调用mallocgc。但相应的缺点,其一是可扩展哈希的算法并没有发生作用,扩容时会造成对整个数组的值拷贝(如果实现上用Bucket指针的数组就是指针拷贝了,代价小很多)。其二是首个bucket与后面产生了不一致性。这个会使删除逻辑变得复杂一点。比如删除后面的溢出链可以直接删除,而对于首个bucket,要等到evalucated完毕后,整个oldbucket删除时进行。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">没有重用设freelist重用删除的结点。作者把这个加了一个TODO的注释,不过想了一下觉得这个做的意义不大。因为一方面,bucket大小并不一致,重用比较麻烦。另一方面,下层存储已经做过内存池的实现了,所以这里不做重用也会在内存分配那一层被重用的,</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">bucket直接key/value和间接key/value优化。这个优化做得蛮好的。注意看代码会发现,如果key或value小于128字节,则它们的值是直接使用的bucket作为存储的。否则bucket中存储的是指向实际key/value数据的指针,</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">bucket存8个key/value对。查找时进行顺序比较。第一次发现高位居然不是用作offset,而是用于加快比较的。定位到bucket之后,居然是一个顺序比较的查找过程。后面仔细想了想,觉得还行。由于bucket只有8个,顺序比较下来也不算过分。仍然是O(1)只不过前面系数大一点点罢了。相当于hash到一个小范围之后,在这个小范围内顺序查找。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">插入删除的优化。前面已经提过了,插入只要找到相同的key或者第一个空位,bucket中如果存在一个以上的相同key,前面覆盖后面的(只是如果,实际上不会发生)。而删除就需要遍历完所有bucket溢出链了。这样map的设计就是为插入优化的。考虑到一般的应用场景,这个应该算是很合理的。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">作者还列了另个2个TODO:将多个几乎要empty的bucket合并;如果table中元素很少,考虑shrink table。(毕竟现在的实现只是单纯的grow)。</p><p><br/></p>
文章列表
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">为了方便读者在追踪代码的过程中找到各种opcode对应的处理函数实现,下面介绍几种方法。</p><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>从PHP5.1开始,PHP对opcode的分发方式可以用户自定义,分为CALL,SWITCH和GOTO三种类型。<br/>默认使用的CALL的方式,本文也应用于这种方式。有关Zend虚拟机的介绍请阅读后面相关内容。</p></blockquote><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>Debug法</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在学习研究PHP内核的过程中,经常通过opcode来查看代码的执行顺序,opcode的执行由在文件Zend/zend_vm_execute.h中的execute函数执行。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]ZEND_API void execute(zend_op_array *op_array TSRMLS_DC){...zend_vm_enter:....if ((ret = EX(opline)->handler(execute_data TSRMLS_CC)) > 0) { switch (ret) { case 1: EG(in_execution) = original_in_execution; return; case 2: op_array = EG(active_op_array); goto zend_vm_enter; case 3: execute_data = EG(current_execute_data); default: break; }}...}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在执行的过程中,EX(opline)->handler(展开后为 *execute_data->opline->handler)存储了处理当前操作的函数指针。<br/>使用gdb调试,在execute函数处增加断点,使用p命令可以打印出类似这样的结果:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c](gdb) p *execute_data->opline->handler$1 = {int (zend_execute_data *)} 0x10041f394 <ZEND_NOP_SPEC_HANDLER></pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这样就可以方便的知道当前要执行的处理函数了,这种debug的方法。这种方法比较麻烦,需要使用gdb来调试。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>计算法</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在PHP内部有一个函数用来快速的返回特定opcode对应的opcode处理函数指针:zend_vm_get_opcode_handler()函数:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]static opcode_handler_tzend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op){ static const int zend_vm_decode[] = { _UNUSED_CODE, /* 0 */ _CONST_CODE, /* 1 = IS_CONST */ _TMP_CODE, /* 2 = IS_TMP_VAR */ _UNUSED_CODE, /* 3 */ _VAR_CODE, /* 4 = IS_VAR */ _UNUSED_CODE, /* 5 */ _UNUSED_CODE, /* 6 */ _UNUSED_CODE, /* 7 */ _UNUSED_CODE, /* 8 = IS_UNUSED */ _UNUSED_CODE, /* 9 */ _UNUSED_CODE, /* 10 */ _UNUSED_CODE, /* 11 */ _UNUSED_CODE, /* 12 */ _UNUSED_CODE, /* 13 */ _UNUSED_CODE, /* 14 */ _UNUSED_CODE, /* 15 */ _CV_CODE /* 16 = IS_CV */ }; return zend_opcode_handlers[ opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">由上面的代码可以看到,opcode到php内部函数指针的查找是由下面的公式来进行的:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">然后将其计算的数值作为索引到zend_init_opcodes_handlers数组中进行查找。<br/>不过这个数组实在是太大了,有3851个元素,手动查找和计算都比较麻烦。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>命名查找法</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上面的两种方法其实都是比较麻烦的,在定位某一opcode的实现执行代码的过程中,<br/>都不得不对程序进行执行或者计算中间值。而在追踪的过程中,笔者发现处理函数名称是有一定规则的。<br/>这里以函数调用的opcode为例,调用某函数的opcode及其对应在php内核中实现的处理函数如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]//函数调用:DO_FCALL ==> ZEND_DO_FCALL_SPEC_CONST_HANDLER//变量赋值:ASSIGN => ZEND_ASSIGN_SPEC_VAR_CONST_HANDLER ZEND_ASSIGN_SPEC_VAR_TMP_HANDLER ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER ZEND_ASSIGN_SPEC_VAR_CV_HANDLER //变量加法:ASSIGN_SUB => ZEND_ASSIGN_SUB_SPEC_VAR_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_CV_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_CV_HANDLER,</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在上面的命名就会发现,其实处理函数的命名是有以下规律的:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]ZEND_[opcode]_SPEC_(变量类型1)_(变量类型2)_HANDLER</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这里的变量类型1和变量类型2是可选的,如果同时存在,那就是左值和右值,归纳有下几类:<br/>VAR TMP CV UNUSED CONST<br/>这样可以根据相关的执行场景来判定。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>日志记录法</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这种方法是上面<strong style="box-sizing: border-box;">计算法</strong>的升级,同时也是比较精准的方式。在<strong style="box-sizing: border-box;">zend_vm_get_opcode_handler</strong> 方法中添加以下代码:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]static opcode_handler_tzend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op){ static const int zend_vm_decode[] = { _UNUSED_CODE, /* 0 */ _CONST_CODE, /* 1 = IS_CONST */ _TMP_CODE, /* 2 = IS_TMP_VAR */ _UNUSED_CODE, /* 3 */ _VAR_CODE, /* 4 = IS_VAR */ _UNUSED_CODE, /* 5 */ _UNUSED_CODE, /* 6 */ _UNUSED_CODE, /* 7 */ _UNUSED_CODE, /* 8 = IS_UNUSED */ _UNUSED_CODE, /* 9 */ _UNUSED_CODE, /* 10 */ _UNUSED_CODE, /* 11 */ _UNUSED_CODE, /* 12 */ _UNUSED_CODE, /* 13 */ _UNUSED_CODE, /* 14 */ _UNUSED_CODE, /* 15 */ _CV_CODE /* 16 = IS_CV */ }; //很显然,我们把opcode和相对应的写到了/tmp/php.log文件中 int op_index; op_index = opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]; FILE *stream; if((stream = fopen("/tmp/php.log", "a+")) != NULL){ fprintf(stream, "opcode: %d , zend_opcode_handlers_index:%d\n", opcode, op_index); } fclose(stream);</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><br/></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"> return zend_opcode_handlers[ opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">然后,就可以在<strong style="box-sizing: border-box;">/tmp/php.log</strong>文件中生成类似如下结果:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]opcode: 38 , zend_opcode_handlers_index:970</pre><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">前面的数字是opcode的,我们可以这里查到: <a href="http://php.net/manual/en/internals2.opcodes.list.php" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">http://php.net/manual/en/internals2.opcodes.list.php</a><br/>后面的数字是static const opcode_handler_t labels[] 索引,里面对应了处理函数的名称,<br/>对应源码文件是:Zend/zend_vm_execute.h (第30077行左右)。 这是一个超大的数组,php5.3.4中有3851个元素,<br/>在上面的例子里,看样子我们要数到第970个了,当然,有很多种方法来避免人工去计算,这里就不多介绍了。</p><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">指令的格式和规范由处理器的指令规范指定。 除了指令本身以外通常还有指令所需要的操作数,可能有的指令不需要显式的操作数。<br/>这些操作数可能是寄存器中的值,堆栈中的值,某块内存的值或者IO端口中的值等等。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">通常opcode还有另一种称谓: 字节码(byte codes)。 例如Java虚拟机(JVM),.NET的通用中间语言(CIL: Common Intermeditate Language)等等。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>PHP的opcode</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">PHP中的opcode则属于前面介绍中的后着,PHP是构建在Zend虚拟机(Zend VM)之上的。PHP的opcode就是Zend虚拟机中的指令。</p><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>有关Zend虚拟机的介绍请阅读后面相关内容</p></blockquote><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在PHP实现内部,opcode由如下的结构体表示:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]struct _zend_op { opcode_handler_t handler; // 执行该opcode时调用的处理函数 znode result; znode op1; znode op2; ulong extended_value; uint lineno; zend_uchar opcode; // opcode代码};</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">和CPU的指令类似,有一个标示指令的opcode字段,以及这个opcode所操作的操作数,PHP不像汇编那么底层,<br/>在脚本实际执行的时候可能还需要其他更多的信息,extended_value字段就保存了这类信息,<br/>其中的result域则是保存该指令执行完成后的结果。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">例如如下代码是在编译器遇到print语句的时候进行编译的函数:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]void zend_do_print(znode *result,const znode *arg TSRMLS_DC){ zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->result.op_type = IS_TMP_VAR; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->opcode = ZEND_PRINT; opline->op1 = *arg; SET_UNUSED(opline->op2); *result = opline->result;}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这个函数新创建一条zend_op,将返回值的类型设置为临时变量(IS_TMP_VAR),并为临时变量申请空间,<br/>随后指定opcode为ZEND_PRINT,并将传递进来的参数赋值给这条opcode的第一个操作数。这样在最终执行这条opcode的时候,<br/>Zend引擎能获取到足够的信息以便输出内容。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">下面这个函数是在编译器遇到echo语句的时候进行编译的函数:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]void zend_do_echo(const znode *arg TSRMLS_DC){ zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_ECHO; opline->op1 = *arg; SET_UNUSED(opline->op2);}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">可以看到echo处理除了指定opcode以外,还将echo的参数传递给op1,这里并没有设置opcode的result结果字段。<br/>从这里我们也能看出print和echo的区别来,print有返回值,而echo没有,这里的没有和返回null是不同的,<br/>如果尝试将echo的值赋值给某个变量或者传递给函数都会出现语法错误。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">PHP脚本编译为opcode保存在op_array中,其内部存储的结构如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]struct _zend_op_array { /* Common elements */ zend_uchar type; char *function_name; // 如果是用户定义的函数则,这里将保存函数的名字 zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; /* END of common elements */ zend_bool done_pass_two; zend_uint *refcount; zend_op *opcodes; // opcode数组 zend_uint last,size; zend_compiled_variable *vars; int last_var,size_var; // ...}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">如上面的注释,opcodes保存在这里,在执行的时候由下面的execute函数执行:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c]ZEND_API void execute(zend_op_array *op_array TSRMLS_DC){ // ... 循环执行op_array中的opcode或者执行其他op_array中的opcode}</pre><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">前面提到每条opcode都有一个opcode_handler_t的函数指针字段,用于执行该opcode,<br/>这里并没有给没有指定处理函数,那在执行的时候该由哪个函数来执行呢?<br/>更多信息请参考Zend虚拟机相关章节的详细介绍。虚拟机相关章节的详细介绍。</p><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">广义而言,语言是一套采用共同符号、表达方式与处理规则。就编程语言而言,<br/>编程语言也是特定规则的符号,用来传达特定的信息,自然语言是人与人之间沟通的渠道,<br/>而编程语言则是机器之间,人与机器之间的沟通渠道。人有非常复杂的语言能力,<br/>语言本身也在不断的进化,人之间能够理解复杂的语言规则,而计算机并没有这么复杂的系统,<br/>它们只能接受指令执行操作,编程语言则是机器和人(准确说是程序员)之间的桥梁,<br/>编程语言的作用就是将语言的特定符号和处理规则进行翻译,由编程语言来处理这些规则,</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">目前有非常多的编程语言,不管是静态语言还是动态语言都有固定的工作需要做:<br/>将代码编译为目标指令,而编译过程就是根据语言的语法规则来进行翻译,<br/>我们可以选择手动对代码进行解析,但这是一个非常枯燥而容易出错的工作,<br/>尤其是对于一个完备的编程语言而言,由此就出现了像lex/yacc这类的编译器生成器。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">编程语言的编译器(compiler)或解释器(interpreter)一般包括两大部分:</p><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>读取源程序,并处理语言结构。</p></li><li><p>处理语言结构并生成目标程序。</p></li></ol><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Lex和Yacc可以解决第一个问题。<br/>第一个部分也可以分为两个部分:</p><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>将代码切分为一个个的标记(token)。</p></li><li><p>处理程序的层级结构(hierarchical structure)。</p></li></ol><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">很多编程语言都使用lex/yacc或他们的变体(flex/bison)来作为语言的词法语法分析生成器,<br/>比如PHP、Ruby、Python以及MySQL的SQL语言实现。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Lex和Yacc是Unix下的两个文本处理工具, 主要用于编写编译器, 也可以做其他用途。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>Lex(词法分析生成器:A Lexical Analyzer Generator)。</p></li><li><p>Yacc(Yet Another Compiler-Compiler)</p></li></ul><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>Lex/Flex</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Lex读取词法规则文件,生成词法分析器。目前通常使用Flex以及Bison来完成同样的工作,<br/>Flex和lex之间并不兼容,Bison则是兼容Yacc的实现。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">词法规则文件一般以.l作为扩展名,flex文件由三个部分组成,三部分之间用%%分割:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[flex]定义段%%规则段%%用户代码段</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">例如以下一个用于统计文件字符、词以及行数的例子:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[flex]%option noyywrap%{int chars = 0;int words = 0;int lines = 0;%}%%[a-zA-Z]+ { words++; chars += strlen(yytext); }\n { chars++; lines++; }. { chars++; }%%main(int argc, char **argv) { if(argc > 1) { if(!(yyin = fopen(argv[1], "r"))) { perror(argv[1]); return (1); } yylex(); printf("%8d%8d%8d\n", lines, words, chars); }}</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">该解释器读取文件内容, 根据规则段定义的规则进行处理, 规则后面大括号中包含的是动作, 也就是匹配到该规则程序执行的动作,<br/>这个例子中的匹配动作时记录下文件的字符,词以及行数信息并打印出来。其中的规则使用正则表达式描述。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">回到PHP的实现,PHP以前使用的是flex,<a href="http://blog.somabo.de/2008/02/php-on-re2c.html" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">后来</a>PHP的词法解析改为使用<a href="http://re2c.org/" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">re2c</a>,<br/>$PHP_SRC/Zend/zend_language_scanner.l 文件是re2c的规则文件, 所以如果修改该规则文件需要安装re2c才能重新编译。</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>Yacc/Bison</h3><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>PHP在后续的版本中<a href="http://wiki.php.net/rfc/lemon" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">可能会使用Lemon作为语法分析器</a>,<br/><a href="http://www.sqlite.org/src/doc/trunk/doc/lemon.html" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">Lemon</a>是SQLite作者为SQLite中SQL所编写的词法分析器。<br/>Lemon具有线程安全以及可重入等特点,也能提供更直观的错误提示信息。</p></blockquote><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Bison和Flex类似,也是使用%%作为分界不过Bison接受的是标记(token)序列,根据定义的语法规则,来执行一些动作,<br/>Bison使用巴科斯范式(<a href="http://baike.baidu.com/view/1137652.htm" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">BNF</a>)来描述语法。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">下面以php中echo语句的编译为例:echo可以接受多个参数,<br/>这几个参数之间可以使用逗号分隔,在PHP的语法规则如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[yacc]echo_expr_list: echo_expr_list ',' expr { zend_do_echo(&$3 TSRMLS_CC); } | expr { zend_do_echo(&$1 TSRMLS_CC); };</pre><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">其中echo_expr_list规则为一个递归规则,这样就允许接受多个表达式作为参数。<br/>在上例中当匹配到echo时会执行zend_do_echo函数,<br/>函数中的参数可能看起来比较奇怪, 其中的$3 表示前面规则的第三个定义,也就是expr这个表达式的值,<br/>zend_do_echo函数则根据表达式的信息编译opcode,其他的语法规则也类似。<br/>这和C语言或者Java的编译器类似,不过GCC等编译器时将代码编译为机器码,Java编译器将代码编译为字节码。</p><p><br/></p>
<h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;">一、用户和组的相关概念</h2><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>账号的概念和分类</h3><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">账号</strong>:是一种用来记录单个用户或是多个用户的数据。Linux中每一个合法的用户都必须要拥有账号,才能使用 。它不仅可以用来验证用户身份,还决定了一个用户在系统中可以从事什么工作</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在Linux 上的账号可以分成两类:</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">用户账号</strong> 用来储存单一用户的数据,你可以使用一个用户账号,来储存某一个用户的数据。</p></li><li><p><strong style="box-sizing: border-box;">群组账号</strong> 用来储存多个用户的信息,每一个群组账号可以用来记录一组用户的数据。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">我们可以把 Linux 的所有账号依照下面两种方法进行分类:</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>依照账号的位置。</p></li><li><p>依照账号的功能。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">依照账号存储的位置</strong></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">账号数据存放在不同的地方,其可使用的范围就会不太一样,账号的使用范围我们称为视野。依照账号储存的位置与视野不同,我们可以区分本机账号与网域账号两种:</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">本机账号:</strong> 账号密码储存于本机硬盘中,我们称为本机账号。<br/>本机账号使用范围只能在账号建立的 Linux 系统上,如果超出范围时,本机账号将无法使用。比如:你在自己电脑上登录用的账号和密码,正常是无法在其它的电脑上使用的。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>本机账号的优点:是简单易用,你无须作额外的设置,就可以直接建立本机账号;</p></li><li><p>本机账号的缺点:则是无法具备延展性(<em style="box-sizing: border-box;">Scalability</em>)。比如:你在一个拥有很多主机的环境中,想拥有一个在每台主机都可以登录的账号时,你就需要在所有的主机上都建立相关的用户</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">网域账号:</strong> 你也可以把大量的计算机组织成为一个网域,然后在网域中的某一台 Linux 上建立账号数据,并且通过某些通信协议,将账号数据分享出来。当其他计算机需要取得账号数据时,再通过网络调用这些分享的账号即可。这种账号我们称为网域账号 (<em style="box-sizing: border-box;">Domain Account</em>)。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>网域账号的优点:在于具备延展性。在大规模的环境中,使用网域账号往往能节省管理账号的时间;</p></li><li><p>但网域账号也有缺点,其最大的缺点就是要配置网域账号前,你必须先建立“域”的环境才行。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">依照账号的功能</strong></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">不管是本机账号或是网域账号,我们还可以把所有账号依照功能分成下面几类</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><img src="https://www.maxiaoke.com/uploads/images/20231231/a68f1deb3226d9adde38c90654879a14.png" alt=""/></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">用户账号部分:</strong> 包含了超级用户、普通用户;而普通用户中还可细分为系统用户、真实用户两种。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;">超级用户:在Linux 系统上拥有完整的控制能力,常被称为系统管理员,在 系统上拥有完整的控制能力,你可以利用超级用户读取或写入 上任何文件、安装或删除软硬件、启动或停止服务,甚至关机与停止系统的执行。 通常只有在管理系统时才会使用超级用户账号登录,所以超级用户常被称为系统管理员 (<em style="box-sizing: border-box;">System Administrator</em>)。由于超级用户的权限不受任何限制,你可以使用该账号来管理 系统;但是,也可能因为操作错误,或者打错命令而造成无法挽救的伤害。<br/>在此,强烈建议你“除非有必要,否则请不要轻易以超级用户身份使用 Linux”!</p><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;"><strong style="box-sizing: border-box;">在 Linux 系统中,默认超级用户的用户为 root,其 UID(用户ID号)一定为 0。</strong></p></li><li><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;">普通用户账号:行为能力会受到限制,只能调用具备权限的文件,如果没有足够的权限,普通用户是完全无法调用的;所以,普通用户账号不太容易危害 Linux 系统。普通用户账号中,我们又可分为两大类:</p></li><li><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;">系统账号<br/>这种类型的账号仅提供给Linux 系统本身使用。在某些软件执行的时候,需要你提供一个普通用户类型的账号。为了满足这些软件而建立的账号,我们称为系统账号 (<em style="box-sizing: border-box;">System Account</em>)。</p></li><li><p style="box-sizing: border-box; margin-top: 16px; margin-bottom: 16px;">真实用户<br/>系统用户账号是给软件或程序使用的,那么,什么账号是让我们登录Linux 时使用的呢?答案就是真实用户(<em style="box-sizing: border-box;">Real User</em>)。真实用户账号是为了让其他人登录系统使用的</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">群组账号部分:</strong> 包含了超级用户群组、系统群组以及用户自定义组三大类。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>超级用户群组:Linux 有一个叫做 root 的群组,因为这个群组的名称与 root 这个超级用户的名称相同,所以,我们习惯把 root 群组叫做超级用户群组。超级用户群组的 GID 为 0。</p></li><li><p>系统群组: 与系统账号一样,系统群组是给 Linux 系统本身,或是某个软件所使用</p></li><li><p>用户自定义组:除了上述的群组外其余的所有群组,皆是由管理者自行定义,因此我们把这些群组称之为用户自定义组</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">另外,Linux 系统的“用户自定义组“类型中,还有一种名为<strong style="box-sizing: border-box;">用户私有群组</strong> (<em style="box-sizing: border-box;">UPG, User Private Group</em>)的群组。什么是 <strong style="box-sizing: border-box;">用户私有群组</strong>呢?用户私有群组(<em style="box-sizing: border-box;">UPG, User Private Group</em>)是指 “与用户账号名称相同,且为用户的主要群组”的群组。当你建立新的用户账号时,Linux 会自动建立该用户的私有群组。如,当你建立 test 这个用户账号时,Linux 会自动建立了一个名为 test 的群组,并且让 test群组成为 test 用户账号的主要群组,test 群组即是 test 的私有群组。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">了解完账号的基本概念后,我们一起来看下账号到底记录了哪些信息</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>二、用户账号管理</h2><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">useradd命令</strong><br/>用户创建命令,创建一个linux用户。</p></li></ul><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">命令介绍 useradd - 创建一个新用户或更新默认新用户信息命令语法 useradd [选项] 登录 useradd -D useradd -D [选项]命令选项 -D 默认 -u UID 用来指定账号的 UID,如果省略这个参数,useradd会自动以最后一个可用的 UID 作为新账号的 UID。 -o 告诉 useradd 允许重复的 UID。 -g gid 定义用户的主要群组。使用 -g 参数前,GROUP 必须已经存在。 -d HOME 指定用户的主目录。默认的主目录是建立在 /home/ 目录下,而且目录名称与用户名称相同。 -s SHELL 指定用户登录执行的程序。 -c COMMENT 指定用户的批注说明。如果批注文字包含空白,请记得使用双引号 (") 包起来。 -r 这个参数用来指出建立一个系统用户的账号。 -M 不创建家目录 -N 不创建同名组 #相关文件 /etc/passwd 用户账户信息。 /etc/shadow 安全用户账户信息。 /etc/group 组账户信息。 /etc/gshadow 安全组账户信息。 /etc/default/useradd 账户创建的默认值。 /etc/skel/ 包含默认文件的目录。 /etc/subgid Per user subordinate group IDs. /etc/subuid Per user subordinate user IDs. /etc/login.defs Shadow 密码套件配置。</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这些参数大多数可以同时使用,可以根据自己的需求指定,例如<br/><strong style="box-sizing: border-box;">创建一个linux用户hello</strong><br/>– uid为 1500<br/>– 附加组为 hello<br/>– 家目录为 /home/hello<br/>– 登陆shell为 /bin/bash<br/>– 描述为 “this is a test user”</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">useradd -u 1500 -G hello -d /home/hello -s /bin/bash -c "this is a test user" hello</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">关于linux本机用户</strong><br/>本机的用户账号数据储存于/etc/passwd文件中。与其他的配置文件一样,passwd 也是一个文本文件,因此,你可以直接使用文字处理程序,例如 cat 或 less 浏览其中的内容。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologindaemon:x:2:2:daemon:/sbin:/sbin/nologin</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">/etc/passwd 权限必须是0644,每一行代表一个用户的账号数据,每一行又使用冒号(:)分隔为几个部分</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">USERNAME:PASSWORD:UID:GID:COMMENT:HOMEDIR:SHELL - USERNAME:用户识别名称,也是登录的名称- PASSWORD:密码位,用于存储用户的密码,为了安全起见,密码放在另一个文件中,这里统一用x- UID:用户识别号,0表示为管理员,非0为普通用户- GID:组识别号,用来识别用户组的身份,同样0为管理员组,非0为系统群组或者自定义组- COMMENT:描述信息- HOMEDIR:家目录位置- SHELL:shell类型</pre><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">usermod命令</strong><br/>是用来修改用户相关信息的,和useradd使用的参数很多是相同的,用法也是一样的,除此之外usermod命令还有一些额外的参数</p></li></ul><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">命令介绍 usermod - 修改一个用户账户命令语法 usermod [选项] 登录命令选项-l NEWNAME 修改账号的用户名称,NEWNAME 即是新的账号名称。-L 锁定账号,一经锁定的账号将无法用来登录系统。-U 解除锁定。</pre><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">userdel命令</strong><br/>删除linux用户</p></li></ul><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">命令介绍 userdel - 删除用户账户和相关文件命令语法 userdel [选项] 登录命令选项-r 用户主目录中的文件将随用户主目录和用户邮箱一起删除。在其它文件系统中的文件必须手动搜索并删除。-f 强制</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">删除tom用户以及其家目录文件</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# userdel -r tom</pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>三、用户查询命令</h2><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">id命令:查询用户uid、gid信息</strong></p></li></ul><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# id hellouid=1500(hello) gid=1500(hello) 组=1500(hello)[root@zutuanxue ~]# id -u hello1500</pre><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">whoami命令:查询当前登录用户</strong></p></li></ul><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# whoamiroot</pre><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">w命令:查询linux系统中登陆的所有用户</strong></p></li></ul><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap; margin-bottom: 0px !important;">[root@zutuanxue ~]# w 18:21:43 up 2:01, 1 user, load average: 0.00, 0.00, 0.00USER TTY FROM LOGIN@ IDLE JCPU PCPU WHATroot pts/0 192.168.1.17 16:16 3.00s 0.08s 0.00s w</pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><br/></h2>
<h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;">一、linux的文件存储</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">假如有一个用户在linux系统中编辑了一个文件,编辑完内容后,关闭编辑器时会问用户改如何命名这个文件,设置完名称之后会选择一个目录将该文件保存到指定目录下,在这个例子中包含了linux系统中与文件相关的三个组成部分</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>数据:就是文件的内容,保存在一个叫data(数据块)的结构中</p></li><li><p>元数据:保存一个文件的特征的系统数据,用来保存除了文件内容和文件名以外的与文件相关的信息, 诸如文件的创建者,日期,大小等等,保存在一个叫inode(i节点)的结构中。</p></li><li><p>文件名:用来保存文件名称,文件名保存在一个叫dentry(目录项)的结构中。</p></li></ul><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>二、i节点介绍</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">通过前面的课程我们知道,inode是用来保存文件的元数据的,除此之外还保存文件相关属性信息,如链接数等。我们通过类似stat hello.txt这条命令查看指定文件的inode信息。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>三、链接文件介绍</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Linux中如果用户想把同一个文件保存在两个地方,或用两个不同的文件名保存,除了复制之外还有另外一种选择,就是链接,在linux系统中链接分为硬链接,软链接,空链接,递归链接等</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">链接命令: ln</strong></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">语法:<br/>ln 源文件路径 链接位置路径</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">常用命令选项:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">-s 创建软连接-f 强制执行-i 交互模式,文件存在则提示用户是否覆盖-n 把符号链接视为一般目录,显示为一般文件-v 显示详细的处理过程</pre><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>硬链接:允许一个文件拥有多个有效路径名,这样用户就可以建立硬链接到重要的文件,以防止“误删”源数据,不过硬链接只能在同一文件系统中的文件之间进行连接</p></li><li><p>软链接 : 也叫符号链接,类似于windows系统中的快捷方式,与硬链接不同,软链接就是一个普通文件,软链接可对文件或目录创建。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这是概念上的描述,我们用一句话来将这两种链接进行总结:</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">a、硬链接不能链目录,不能跨文件系统,软链接可以;</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">b、源文件删除后软链接失效,硬链接依然可用;</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">我们通过下面的例子可以看到硬链接和软链接的创建方式以及基本对比</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">软连接</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"># 在root家目录下创建一个文件ztx[root@zutuanxue ~]# touch ztx# 为/root/ztx文件创建一个软连接文件在/tmp目录下[root@zutuanxue ~]# ln -s /root/ztx /tmp/# 验证快捷方式是否创建成功[root@zutuanxue ~]# ls -l /tmp/ztx lrwxrwxrwx 1 root root 9 11月 26 15:06 /tmp/ztx -> /root/ztx# 查看源文件和链接文件的i节点信息[root@zutuanxue ~]# ls -i /root/ztx /tmp/ztx 674632 /root/ztx 929916 /tmp/ztx # 发现软连接的文件i节点不同# 删除源文件[root@zutuanxue ~]# rm -f /root/ztx# 验证链接文件是否可用[root@zutuanxue ~]# cat /tmp/ztx cat: /tmp/ztx: 没有那个文件或目录 #删除源文件发现链接文件失效</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">硬链接</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"># 创建一个源文件 名字为组团学[root@zutuanxue ~]# touch zutuanxue # 为/root/zutuanxue文件创建一个硬链接文件 到/tmp/zutuanxue[root@zutuanxue ~]# ln /root/zutuanxue /tmp/zutuanxue # 查看源文件和链接文件的i节点[root@zutuanxue ~]# ls -i /root/zutuanxue /tmp/zutuanxue 674632 /root/zutuanxue 674632 /tmp/zutuanxue # 硬链接的i节点和源文件的i节点一致# 删除源文件,看看硬链接文件是否可用[root@zutuanxue ~]# echo test > /root/zutuanxue [root@zutuanxue ~]# rm /root/zutuanxue rm:是否删除普通文件 "/root/zutuanxue"?y[root@zutuanxue ~]# cat /tmp/zutuanxue test #删除硬链接源文件后,发现链接文件依然可以使用</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">除了上面我们说到的硬链接和软链接之外还有</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>空链接,所谓的空链接就是软链接指向的源文件不存在了,包括源文件被删除,改名。</p></li><li><p>递归链接:递归链接不如空链接常见,如果想看的话几乎需要专门寻找,如果用户创建两个软链接,linka与linkb关联,而linkb又与linka关联,这时候就会出现递归链接,比如</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><img src="https://www.maxiaoke.com/uploads/images/20231231/4ded8d364377922830c8efe362d35871.png" alt=""/></p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>绝对软链接和相对软链接:主要指的是用户在创建软链接的时候使用的是绝对路径还是相对路径来指定链接目标,如果链接创建完成之后不会移动,那么这两种方法可以随意使用,但是如果链接文件需要被移动,那么建议还是使用绝对路径,比如</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><img src="https://www.maxiaoke.com/uploads/images/20231231/0280981b4da83c6475fd08099836b080.png" alt=""/></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">好了链接我们就说到这里,下面我们看一下时间戳</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>四、时间戳</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。通俗的讲, 时间戳是一份能够表示一份数据在一个特定时间点已经存在的完整的可验证的数据。 它的提出主要是为用户提供一份电子证据, 以证明用户的某些数据的产生时间。 当我们使用stat命令查看文件的时候除了创建时间你会发现有三个跟时间戳相关的信息</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><img src="https://www.maxiaoke.com/uploads/images/20231231/8c185bf1775f27cbd0dc208dedb23e10.png" alt=""/></p><table width="1344"><thead style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);" class="firstRow"><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">缩写</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">命令</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">目的</th></tr></thead><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">Atime</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">访问时间</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">文件数据每次被阅读后的更新</td></tr><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">Ctime</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">改变时间</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">文件的i-节点信息每次被改变后都更新</td></tr><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">Mtime</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">修改时间</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">文件数据每次被改变后的更新</td></tr></tbody></table><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>五、常用目录管理命令总结</h2><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">目录:</strong></p></li></ul><table width="1344"><thead style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);" class="firstRow"><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">命令</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">用法</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">功能</th></tr></thead><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">mkdir</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">mkdir 目录名称</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">创建一个目录</td></tr><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">rmdir</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">rmdir 目录名称</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">删除一个空目录</td></tr><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">cd</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">cd 目录名称</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">进入一个目录</td></tr><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">ls</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">ls 目录名称</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">列出一个目录内容</td></tr></tbody></table><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">文件:</strong></p></li></ul><table width="1344"><thead style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);" class="firstRow"><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">命令</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">用法</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">功能</th></tr></thead><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">touch</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">touch 文件名</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">新建一个文件</td></tr><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">rm</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">rm 文件名</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">删除一个文件</td></tr><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">cat</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">cat 文件名</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">打印一个文件内容</td></tr></tbody></table><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p><strong style="box-sizing: border-box;">其他:</strong></p></li></ul><table width="1344"><thead style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);" class="firstRow"><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">命令</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">用法</th><th style="box-sizing: border-box; text-align: inherit; padding: 6px 13px; border-color: rgb(238, 238, 238);">功能</th></tr></thead><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">cp</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">mkdir file folder</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">将文件file拷贝到目录folder</td></tr><tr style="box-sizing: border-box; background-color: rgb(248, 248, 248); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">mv</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">mv A B</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">移动A到B目录或者从命名 A为B</td></tr><tr style="box-sizing: border-box; background-color: rgb(255, 255, 255); border-top: 1px solid rgb(238, 238, 238);"><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">ln</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">ln -s A B</td><td style="box-sizing: border-box; padding: 6px 13px; border-color: rgb(238, 238, 238);">给A做一个快捷方式,放到B位置</td></tr></tbody></table><p><br/></p>
<p>介绍</p><p>区块链是一项突破性的技术,它已经超越了最初在加密货币中的使用,并已成为安全、去中心化系统的重要组成部分。在本文中,我们将探讨区块链的基础知识,并使用 Go 编程语言动手演示其内部工作原理。</p><p>为什么区块链对开发人员很重要</p><p>作为这个时代的开发人员,了解区块链不再是可有可无的,而是一种战略优势。除了与数字货币的关联之外,区块链还是一种强大的工具,用于在各种应用程序中建立信任、透明度和安全性。无论您是经验丰富的开发人员还是好奇的新手,掌握区块链的基础知识都会为创新解决方案和新机会打开大门。</p><p>区块链技术的核心</p><p>从本质上讲,区块链是一种分布式账本,用于记录计算机网络上的交易。它的分散性质使它与众不同,消除了对中央权威的需要。想象一下一连串的区块,每个区块都包含一个交易列表,通过加密哈希链接在一起。这确保了数据的完整性,并使其几乎防篡改。</p><p>区块链的构成要素</p><p>了解关键组件至关重要:</p><p>块:数据存储的基本单位,包含事务列表。</p><p>交易:有关事件或操作的信息,例如资产转移。</p><p>加密哈希:一种为每个区块创建唯一标识符的安全方法,确保数据的完整性。</p><p>分散:没有中央管理机构,加强了安全性并促进了参与者的信任。</p><p>共识算法的作用</p><p>共识算法,如工作量证明或权益证明,在维护分布式账本的完整性方面发挥着至关重要的作用。这些机制确保网络中的所有节点都同意区块链的状态,从而降低欺诈或操纵的风险。</p><p>本文内容</p><p>以下部分将探讨如何使用 Go 编程语言实现区块链系统。Go 的简单性和效率使其成为理解区块链复杂性的绝佳选择,而不会造成不必要的复杂性。我们将介绍代码结构,讨论关键功能,并提供区块链的分步演示。</p><p>免责声明:本文帮助您了解区块、区块哈希、区块验证和区块完整性的基本概念。它不会教你成为一名区块链开发人员,但肯定是帮助你入门的好信息!</p><p>也就是说,让我们开始吧!</p><p>首先,你要设置你的环境,你应该在你的计算机上安装 GO,这样你就可以通过运行以下命令来为项目创建一个目录:</p><p>mkdir go-blockchain</p><p>这将为项目创建一个文件夹。接下来,我们进入该文件夹<span style="color: #6a9955;">;</span></p><p>cd go-blockchain</p><p>并通过运行以下命令在包中启动一个新的 GO 项目:</p><p>go mod init github.com/{your-username}/go-blockchain</p><p>上面的命令将创建一个在项目目录中调用的文件。go.mod</p><p>完成后,在项目目录中创建一个文件。我们将在这个文件中完成所有的烹饪。main.go</p><p>我们将创造什么</p><p>我们将创建一个简单而强大的区块链系统。该区块链将作为跟踪图书结账交易的强大账本,展示去中心化和防篡改系统的运作方式。我们的创作将包含以下关键要素:</p><p>区块链结构: - 我们将定义一个结构化的区块链,由单独的区块组成,每个区块都包含有关图书结账的基本信息。这些区块将链接在一起,形成一个不可变的交易链。</p><p>区块组件: - 区块链中的每个区块都将封装结账数据、位置、时间戳、哈希值和前一个区块的哈希值。此结构可确保数据完整性、可追溯性和安全性,如下图所示:</p><p>Representation of Blocks Within a Blockchain</p><p>哈希机制: - 区块链将利用加密哈希,特别是 SHA-256,为每个区块生成唯一标识符。这种哈希机制保证了过去交易的不变性,并保护整个区块链免受未经授权的更改。</p><p>创世区块: - 我们将建立创世区块的概念,标志着我们区块链的开始。这个初始区块是后续交易的基础,并构成了整个账本的基础。</p><p>HTTP 处理程序: - 为了与我们的区块链进行交互,我们将实现 HTTP 处理程序以执行基本操作,例如创建新书籍、检索整个区块链以及向链中添加新区块。这种实用的方法使我们能够通过HTTP请求见证区块链。</p><p>实时显示: - 作为奖励,我们将实时显示区块链中现有区块,展示存储在每个区块中的信息,包括之前的哈希值、数据详细信息和区块的唯一哈希值。</p><p>让我们看看所有这些是如何在代码中组合在一起的。我们将从设置我们的区块和区块链结构开始。打开文件并创建以下结构:main.go</p><p>// Book represents the item to be checked outtype Book struct {</p><p> ID string `json:<span style="color: #ce9178;">"id"</span>`</p><p> Title string `json:<span style="color: #ce9178;">"title"</span>`</p><p> Author string `json:<span style="color: #ce9178;">"author"</span>`</p><p> PublishDate string `json:<span style="color: #ce9178;">"publish_date"</span>`</p><p> ISBN string `json:<span style="color: #ce9178;">"isbn"</span>`}// BookCheckout represents the data of a Book to be checked outtype BookCheckout struct {</p><p> BookID string `json:<span style="color: #ce9178;">"book_id"</span>`</p><p> User string `json:<span style="color: #ce9178;">"user"</span>`</p><p> CheckoutDate string `json:<span style="color: #ce9178;">"checkout_date"</span>`</p><p> IsGenesis bool `json:<span style="color: #ce9178;">"is_genesis"</span>`}// Block represents the individual block on the blockchain// it holds the checkout data, position, timestamp, hash, and prevhashtype Block struct {</p><p> Pos int</p><p> Data BookCheckout</p><p> TimeStamp string</p><p> Hash string</p><p> PrevHash string}// BlockChain represents the core blockchain implementationtype BlockChain struct {</p><p> blocks []*Block</p><p>}</p><p>Book是表示要签出的项的结构。</p><p>它包含诸如 、 、 、 和 等字段,每个字段都提供有关特定书籍的信息。IDTitleAuthorPublishDateISBN</p><p>标记指示用于正确序列化/反序列化的 JSON 字段名称。json:<span style="color: #ce9178;">"..."</span></p><p>BookCheckout是一个结构体,表示要签出的书籍的数据。</p><p>它包括(图书的 ID)、(借出图书的人)、(借出日期)和(指示它是否是创世块的标志)等字段。BookIDUserCheckoutDateIsGenesis</p><p>Block是表示区块链中单个区块的结构。如上图所示,它包含仓位、交易的基础数据(在本例中,我们使用的是信息)、区块创建的时间戳、新区块的唯一哈希值,这也是基于前一个区块的哈希值BookCheckoutBlock</p><p>BlockChain是表示区块链核心实现的结构。它由指针 () 的切片组成,形成整个链。Blockblocks</p><p>太好了,现在我们有了我们的结构!让我们继续添加相关功能,我们将从创建新书开始,因为这是我们将要购买的项目。</p><p>但在此之前,我们需要设置我们的 HTTP 处理程序,因为我们将通过 HTTP 发送创建书籍的请求。您可以使用您选择的任何 HTTP 工具。对我来说,我会在 GO 中使用一个名为 BARF 的出色、易于使用的 HTTP 包。</p><p>若要安装 BARF,请导航到项目根目录,然后运行以下命令:</p><p>go get github.com/opensaucerer/barf</p><p>这会将包安装到您的项目中,您现在可以在文件的 main 函数中创建一个简单的 HTTP 服务器,如下所示:barf</p><p>func main() { // start barf server</p><p> if err := barf.Beck()<span style="color: #6a9955;">; err != nil {</span></p><p> barf.Logger().Error(err.Error())</p><p> }</p><p>}</p><p>现在,如果您再次转到终端并运行,您应该会看到一些如下日志:go run main.go</p><p>现在我们的服务器正在运行,我们可以编写函数来创建一个新的 book 函数作为处理程序,并将其连接到服务器中的路由。</p><p>func newBook(w http.ResponseWriter, r *http.Request) { var book Book</p><p> err := barf.Request(r).Body().Format(&book) if err != nil {</p><p> barf.Logger().Error(err.Error())</p><p> barf.Response(w).Status(http.StatusInternalServerError).JSON(barf.Res{</p><p> Status: false,</p><p> Data: nil,</p><p> Message: <span style="color: #ce9178;">"Error creating book"</span>,</p><p> })</p><p> }</p><p> h := md5.New()</p><p> io.WriteString(h, book.ISBN+book.PublishDate)</p><p> <span style="color: #569cd6;">book.ID</span> = hex.EncodeToString(h.Sum(nil))</p><p> barf.Response(w).Status(http.StatusOK).JSON(barf.Res{</p><p> Status: true,</p><p> Data: book,</p><p> Message: <span style="color: #ce9178;">"New Book Created"</span>,</p><p> })</p><p>}</p><p>该函数在我们的区块链模拟中处理要交易的订单或项目。它通过网络收集要购买的书籍的信息,并将传入的数据解析到结构中。newBookBook</p><p>barf已经帮助转换了以下行: .</p><p>如果解析 JSON 时出现错误,我们会向用户返回错误响应。</p><p>也可以快速解决这个问题(看看 barf 有多快速和易于使用? err := barf.Request(r).Body().Format(&book)barf</p><p>否则,如果一切顺利,我们就会使用哈希算法生成书籍的 ISBN 和发布日期组合的哈希值,以生成 .md5book.ID</p><p>然后,我们只需将创建的书籍的解析返回给 JSON,并将结果作为对请求的响应返回。毋庸置疑,它也可以优雅地处理这个问题。barf</p><p>将其连接到路由,在启动服务器之前指定首选路由:barf</p><p>func main() {</p><p> barf.Post(<span style="color: #ce9178;">"/new"</span>, newBook) // start barf server</p><p> if err := barf.Beck()<span style="color: #6a9955;">; err != nil {</span></p><p> barf.Logger().Error(err.Error())</p><p> }</p><p>}</p><p>完成此操作后,我们可以继续创建另一个将区块写入区块链的函数。</p><p>为了完成此过程,我们将再创建两个 HTTP 处理程序和一些帮助程序函数。</p><p>考虑到区块链的工作原理,我们需要创建单独的区块来保存系统中的敏感信息。在这个系统中,敏感数据是图书结账信息。</p><p>因此,让我们编写函数来创建一个新块:</p><p>func CreateBlock(prevBlock *Block, data BookCheckout) *Block { // function to create a new block</p><p> block := &Block{} if data.IsGenesis {</p><p> <span style="color: #569cd6;">block.Pos</span> = 0</p><p> } else {</p><p> <span style="color: #569cd6;">block.Pos</span> = prevBlock.Pos + 1</p><p> }</p><p> <span style="color: #569cd6;">block.TimeStamp</span> = time.Now().String()</p><p> <span style="color: #569cd6;">block.Data</span> = data</p><p> <span style="color: #569cd6;">block.PrevHash</span> = prevBlock.Hash</p><p> block.generateHash() return block</p><p>}</p><p>此函数接收先前的区块信息和我们打算为其创建新区块的新数据。请记住,在我们对区块链项目的分解中,我们强调我们将考虑创建创世区块。CreateBlock</p><p>函数中的条件检查检出数据是否为创世。如果是这样,它会分配初始位置 0,否则,它只会增加位置计数。</p><p>Timestamp根据区块链技术的规则,在创建区块方面非常重要。我们必须知道每个块的创建时间。</p><p>每个区块还保留前一个哈希值,即创建“链”的点,并且包含前一个哈希值(或父哈希值)也会在区块链上强制执行完整性。</p><p>这是让我对整个区块链技术着迷的部分。</p><p>每个区块都指向它前面的区块的哈希值,形成一系列相互连接的区块。它在块之间创建依赖关系,因此更改一个块中的数据需要更改哈希值,从而影响后续块。</p><p>这种相互依赖性使得在不改变所有后续区块的情况下篡改单个区块在计算上不可行,从而提供了一种维护整个区块链完整性的机制。是不是很漂亮?</p><p>好的,紧随其后的下一行是新创建的区块哈希值的生成。</p><p>是块结构体的方法,让我们在块结构体下声明该方法:generateHash()</p><p>type Block struct {</p><p> Pos int</p><p> Data BookCheckout</p><p> TimeStamp string</p><p> Hash string</p><p> PrevHash string}func (b *Block) generateHash() { // method to generate block hash based on the checkout data sttacched to the block</p><p> bytes, _ := json.Marshal(b.Data)</p><p> data := string(rune(b.Pos)) + b.TimeStamp + string(bytes) + b.PrevHash</p><p> hash := sha256.New()</p><p> <span style="color: #569cd6;">b.Hash</span> = hex.EncodeToString(hash.Sum([]byte(data)))</p><p>}</p><p>该方法获取关联的块并生成其哈希值。</p><p>它将整个区块结账数据转换为字节,并将其字符串等效值与位置、时间戳和前一个哈希连接起来,以生成哈希算法的输入。</p><p>在区块链中,标准哈希算法是SHA256。这就是为什么我们有这样的行:启动 sha256 算法,最后计算数据的哈希值,将结果转换为字符串,并将其分配给 .generateHash()hash := sha256.New()b.Hash</p><p>在创建新区块的过程中,许多哈希过程都依赖于结账数据。那么,让我们看看我们如何创建结帐数据。</p><p>为此,我们需要第二个 HTTP 处理程序来收集结帐数据:</p><p>func writeBlock(w http.ResponseWriter, r *http.Request) { var bookCheckout BookCheckout</p><p> err := barf.Request(r).Body().Format(&bookCheckout) if err != nil {</p><p> barf.Logger().Error(err.Error())</p><p> barf.Response(w).Status(http.StatusInternalServerError).JSON(barf.Res{</p><p> Status: false,</p><p> Data: nil,</p><p> Message: <span style="color: #ce9178;">"Error creating book checkout"</span>,</p><p> })</p><p> }</p><p> resp, err := json.MarshalIndent(bookCheckout, <span style="color: #ce9178;">""</span>, <span style="color: #ce9178;">" "</span>) if err != nil {</p><p> barf.Logger().Error(err.Error())</p><p> barf.Response(w).Status(http.StatusInternalServerError).JSON(barf.Res{</p><p> Status: false,</p><p> Data: nil,</p><p> Message: <span style="color: #ce9178;">"Error creating book checkout"</span>,</p><p> })</p><p> }</p><p> barf.Response(w).Status(http.StatusOK).JSON(barf.Res{</p><p> Status: true,</p><p> Data: string(resp),</p><p> Message: <span style="color: #ce9178;">"New Block Created"</span>,</p><p> })</p><p>}</p><p>这个函数非常简单,我们只是收集图书结账信息,将其解析为结构,然后返回结果作为对请求的响应。</p><p>将其添加到如下所示的路由中:</p><p>func main() {</p><p> barf.Post(<span style="color: #ce9178;">"/"</span>, writeBlock)</p><p> barf.Post(<span style="color: #ce9178;">"/new"</span>, newBook) // start barf server</p><p> if err := barf.Beck()<span style="color: #6a9955;">; err != nil {</span></p><p> barf.Logger().Error(err.Error())</p><p> }</p><p>}</p><p>擎!该函数已命名,但我们在这里没有看到任何正在创建或写入的块。这是故意的。</p><p>我们已经编写了收集要存储在单个区块中的数据的函数,但是,在我们验证区块并将其添加到区块链中的区块列表中之前,将区块写入区块链是不完整的。writeBlock()</p><p>由于我们有一个带有块数组的结构体,让我们实现一个函数,将一个新块作为结构体上的方法添加到链中:BlockChainBlockChain</p><p>type BlockChain struct {</p><p> blocks []*Block</p><p>}func (bc *BlockChain) AddBlock(data BookCheckout) { // method to add a block to the array of block chains</p><p> lastBlock := bc.blocks[len(bc.blocks)-1]</p><p> block := CreateBlock(lastBlock, data) if validBlock(block, lastBlock) {</p><p> <span style="color: #569cd6;">bc.blocks</span> = append(bc.blocks, block)</p><p> }</p><p>}</p><p>该方法使用该函数根据提供的(实例)和最后一个块的信息生成一个新块 ()。</p><p>然后,它使用该函数来检查新块是否对前一个块 () 有效。我们稍后会写出来。AddBlock()CreateBlockblockdataBookCheckoutvalidBlocklastBlock</p><p>最后,如果新区块有效,则将其附加到区块链中的区块数组中()。<span style="color: #569cd6;">bc.blocks</span> = append(bc.blocks, block)bc.blocks</p><p>我们如何知道一个区块是否有效?此验证通常包括检查哈希值是否匹配以及位置是否正确递增:</p><p>func validBlock(block, prevBlock *Block) bool { if prevBlock.Hash != block.PrevHash { return false</p><p> } if !block.validateHash(block.Hash) { return false</p><p> } if prevBlock.Pos+1 != block.Pos { return false</p><p> } return true}</p><p>如上面的代码片段所示,我们需要在块结构上使用另一种方法来验证生成的 Hash:</p><p>func (b *Block) validateHash(hash string) bool {</p><p> b.generateHash() return <span style="color: #569cd6;">b.Hash</span> == hash</p><p>}</p><p>此方法再次重新计算给定块的哈希值,并将其与提供的 has 进行比较,并返回相等作为其结果。简单而时尚!</p><p>伟大!我们现在可以创建、验证和添加新区块到我们的区块链中。但是,请注意,我们尚未在任何地方调用该函数。我们需要仔细考虑我们打算如何管理区块链阵列的状态。AddBlock</p><p>我们什么时候启动一个新的区块链?它将在哪里更新?</p><p>新的区块链应该在main功能之前启动,原因有两个。首先,我们需要在文件的某个地方提供基本的区块链“对象”,使其全局可用于每个区块/函数。</p><p>广告 我们还需要确保每次我们向服务器发起另一个请求时,这个区块链都不会被重新实例化。我们只需要每个服务器实例一个链!</p><p>所以,让我们把它放在结构定义的正下方,如下所示:</p><p>type BlockChain struct {</p><p> blocks []*Block</p><p>}func (bc *BlockChain) AddBlock(data BookCheckout) { // method to add a block to the array of block chains</p><p> lastBlock := bc.blocks[len(bc.blocks)-1]</p><p> block := CreateBlock(lastBlock, data) if validBlock(block, lastBlock) {</p><p> <span style="color: #569cd6;">bc.blocks</span> = append(bc.blocks, block)</p><p> }</p><p>}var MyBlockChain *BlockChain</p><p>好!现在我们可以在这个已经初始化的区块链上调用该方法。我们将在处理程序中调用 AddBlock 方法,在我们成功解析检出数据后,我们可以将函数更新为:AddBLockwriteBlockwriteBlock</p><p>func writeBlock(w http.ResponseWriter, r *http.Request) { var bookCheckout BookCheckout</p><p> err := barf.Request(r).Body().Format(&bookCheckout) if err != nil {</p><p> barf.Logger().Error(err.Error())</p><p> barf.Response(w).Status(http.StatusInternalServerError).JSON(barf.Res{</p><p> Status: false,</p><p> Data: nil,</p><p> Message: <span style="color: #ce9178;">"Error creating book checkout"</span>,</p><p> })</p><p> } // Add block here</p><p> MyBlockChain.AddBlock(bookCheckout)</p><p> resp, err := json.MarshalIndent(bookCheckout, <span style="color: #ce9178;">""</span>, <span style="color: #ce9178;">" "</span>) if err != nil {</p><p> barf.Logger().Error(err.Error())</p><p> barf.Response(w).Status(http.StatusInternalServerError).JSON(barf.Res{</p><p> Status: false,</p><p> Data: nil,</p><p> Message: <span style="color: #ce9178;">"Error creating book checkout"</span>,</p><p> })</p><p> }</p><p> barf.Response(w).Status(http.StatusOK).JSON(barf.Res{</p><p> Status: true,</p><p> Data: string(resp),</p><p> Message: <span style="color: #ce9178;">"New Block Created"</span>,</p><p> })</p><p>}</p><p>现在,我们已经完成了发起交易、创建区块、验证区块以及最终将该区块添加到链中的过程。</p><p>To verify that our results are accurate and valid, we should write the last HTTP handler that traverses the blocks within the blockchain and returns their content as a JSON response:</p><p>COPY</p><p>COPY</p><p>func getBlockChain(w http.ResponseWriter, r *http.Request) {</p><p> jbytes, err := json.MarshalIndent(MyBlockChain.blocks, <span style="color: #ce9178;">""</span>, <span style="color: #ce9178;">" "</span>) if err != nil {</p><p> barf.Logger().Error(err.Error())</p><p> barf.Response(w).Status(http.StatusInternalServerError).JSON(barf.Res{</p><p> Status: false,</p><p> Data: nil,</p><p> Message: <span style="color: #ce9178;">"Error getting blocks from chain"</span>,</p><p> }) return</p><p> }</p><p> barf.Response(w).Status(http.StatusOK).JSON(barf.Res{</p><p> Status: true,</p><p> Data: string(jbytes),</p><p> Message: <span style="color: #ce9178;">"Error getting blocks from chain"</span>,</p><p> })</p><p>}</p><p>并将其连接到函数中的路由:main()</p><p>func main() {</p><p> barf.Get(<span style="color: #ce9178;">"/"</span>, getBlockChain)</p><p> barf.Post(<span style="color: #ce9178;">"/"</span>, writeBlock)</p><p> barf.Post(<span style="color: #ce9178;">"/new"</span>, newBook) // start barf server</p><p> if err := barf.Beck()<span style="color: #6a9955;">; err != nil {</span></p><p> barf.Logger().Error(err.Error())</p><p> }</p><p>}</p><p>就是这样!我们刚刚按照标准的区块链程序构建了一个完整的功能区块链实现,以强制交易或敏感对象的信任和完整性。</p><p>为了测试一切检查是否正确,这里有一个测试文件来验证我们程序的功能和行为:</p><p>package mainimport ( <span style="color: #ce9178;">"net/http"</span></p><p> <span style="color: #ce9178;">"net/http/httptest"</span></p><p> <span style="color: #ce9178;">"strings"</span></p><p> <span style="color: #ce9178;">"testing"</span>)func TestCreateBlock(t *testing.T) {</p><p> prevBlock := &Block{</p><p> Pos: 1,</p><p> TimeStamp: <span style="color: #ce9178;">"2022-01-01"</span>,</p><p> Hash: <span style="color: #ce9178;">"prevHash"</span>,</p><p> }</p><p> data := BookCheckout{</p><p> BookID: <span style="color: #ce9178;">"123"</span>,</p><p> User: <span style="color: #ce9178;">"John Doe"</span>,</p><p> CheckoutDate: <span style="color: #ce9178;">"2022-01-02"</span>,</p><p> IsGenesis: false,</p><p> }</p><p> block := CreateBlock(prevBlock, data) // Add your assertions based on the expected behavior of CreateBlock</p><p> if block.Pos != prevBlock.Pos+1 {</p><p> t.Errorf(<span style="color: #ce9178;">"Expected Pos to be %d, got %d"</span>, prevBlock.Pos+1, block.Pos)</p><p> }</p><p>}func TestAddBlock(t *testing.T) { // Initialize a sample blockchain</p><p> blockchain := NewBlockChain() // Create a sample book checkout data</p><p> bookData := BookCheckout{</p><p> BookID: <span style="color: #ce9178;">"456"</span>,</p><p> User: <span style="color: #ce9178;">"Jane Doe"</span>,</p><p> CheckoutDate: <span style="color: #ce9178;">"2022-01-03"</span>,</p><p> IsGenesis: false,</p><p> } // Add a block to the blockchain</p><p> blockchain.AddBlock(bookData) // Add your assertions based on the expected behavior of AddBlock</p><p> if len(blockchain.blocks) != 2 {</p><p> t.Errorf(<span style="color: #ce9178;">"Expected blockchain length to be 2, got %d"</span>, len(blockchain.blocks))</p><p> }</p><p>}func TestHTTPHandlers(t *testing.T) { // Create a sample HTTP request for the newBook handler</p><p> reqBody := `{<span style="color: #ce9178;">"isbn"</span>: <span style="color: #ce9178;">"123456789"</span>, <span style="color: #ce9178;">"publish_date"</span>: <span style="color: #ce9178;">"2022-01-01"</span>, <span style="color: #ce9178;">"title"</span>: <span style="color: #ce9178;">"Sample Book"</span>, <span style="color: #ce9178;">"author"</span>: <span style="color: #ce9178;">"John Doe"</span>}`</p><p> req, err := http.NewRequest(<span style="color: #ce9178;">"POST"</span>, <span style="color: #ce9178;">"/new"</span>, strings.NewReader(reqBody)) if err != nil {</p><p> t.Fatal(err)</p><p> } // Create a sample response recorder</p><p> rr := httptest.NewRecorder() // Call the newBook handler</p><p> newBook(rr, req) // Check the response status code</p><p> if status := rr.Code<span style="color: #6a9955;">; status != http.StatusOK {</span></p><p> t.Errorf(<span style="color: #ce9178;">"Handler returned wrong status code: got %v want %v"</span>, status, http.StatusOK)</p><p> }</p><p>}func TestGenesisBlock(t *testing.T) { // Create a Genesis block</p><p> genesisBlock := GenesisBlock() // Ensure that the Genesis block has the expected properties</p><p> if genesisBlock.Pos != 0 {</p><p> t.Errorf(<span style="color: #ce9178;">"Expected Genesis block Pos to be 1, got %d"</span>, genesisBlock.Pos)</p><p> }</p><p>}func TestHashGeneration(t *testing.T) { // Create a sample block</p><p> block := &Block{</p><p> Pos: 1,</p><p> TimeStamp: <span style="color: #ce9178;">"2022-01-01"</span>,</p><p> PrevHash: <span style="color: #ce9178;">"prevHash"</span>,</p><p> Data: BookCheckout{</p><p> BookID: <span style="color: #ce9178;">"123"</span>,</p><p> User: <span style="color: #ce9178;">"John Doe"</span>,</p><p> CheckoutDate: <span style="color: #ce9178;">"2022-01-02"</span>,</p><p> IsGenesis: false,</p><p> },</p><p> } // Generate the hash for the block</p><p> block.generateHash() // Validate that the block<span style="color: #ce9178;">'s hash is not empty</span></p><p><span style="color: #ce9178;"> if block.Hash == "" {</span></p><p><span style="color: #ce9178;"> t.Error("Expected non-empty hash, got an empty string")</span></p><p><span style="color: #ce9178;"> }</span></p><p><span style="color: #ce9178;">}func TestBlockchainStartsWithGenesisBlock(t *testing.T) { // Create a new blockchain</span></p><p><span style="color: #ce9178;"> blockchain := NewBlockChain() // Ensure that the blockchain starts with the Genesis block</span></p><p><span style="color: #ce9178;"> if len(blockchain.blocks) != 1 || blockchain.blocks[0].Pos != 0 {</span></p><p><span style="color: #ce9178;"> t.Error("Blockchain should start with the Genesis block")</span></p><p><span style="color: #ce9178;"> }</span></p><p><span style="color: #ce9178;">}</span></p><p><span style="color: #ce9178;">我们已经展示了区块链作为一种技术如何应用于简单的图书购买商店。这个项目可能无法部署为一个全新的区块链,它将改变世界,但有了这个,你现在可以亲身了解该技术是如何在引擎盖下工作的!</span></p><p><br/></p><p><br/></p>
<p>ChatGPT 是大多数人用来使用 OpenAI 的大型语言模型的界面。但对于需要编程访问的多功能性和强大功能的人来说,OpenAI 的 API 是无可替代的。</p><p>该 API 是可用于将您自己的 PC 上运行的编程代码与 OpenAI 的 GPT 服务器连接起来的接口。</p><p>当然,在使用 API 时,您可以包含简单的语言提示,您可以在其中向 GPT 询问答案和生成的内容。但您也可以应用该编程代码的所有内置功能来创建涉及 GPT 的复杂和自动化操作。</p><p>例如,您可以要求 ChatGPT 写一篇关于特定主题的文章。但是使用 API,您可以要求它编写 100 篇关于文本文档中列出的主题的文章,然后坐下来等待您的代码为您完成所有工作。</p><p>当您将 GPT 添加到等式中时,任何利用代码而不是执行手动操作的东西都会效率提高一千倍。</p><p>问题在于,与所有 API 一样,弄清楚语法和其他细节可能需要时间。为了帮助您克服这座山,OpenAI 在他们的 Playground 中创建了可视化工具。让我们看看这将如何帮助。</p><p>本文摘自我的 Manning 著作《The Complete Obsolete Guide to Generative AI》。</p><p>什么是游乐场?</p><p>下图所示的 Playground 甚至在 ChatGPT 之前就已经存在,这是我与 GPT 进行第一次互动的地方。尽管请记住,与 AI 世界中的其他一切一样,当您使用它时,界面可能已经更改了至少两次。</p><p>在本教程中,我们将使用 Playground 来学习如何与 GPT 交互。</p><p>盖-2-2OpenAI 的 Playground 界面</p><p>您可以从 OpenAI 登录帐户访问 Playground。当从屏幕左上角的下拉菜单中选择“聊天”选项时,Playground 中的文本字段一次只提供一次交流,而不是享受持续的对话,即后续的交流由先前的提示和完成通知。它所基于的模型也可能比 ChatGPT 版本更旧、更不精致。</p><p>但有两件事使 Playground 与 ChatGPT 区分开来。一个是上图中屏幕右侧显示的配置控件。第二个是右上角的“查看代码”功能。正是这些功能使 Playground 主要成为一种教育工具,而不仅仅是另一个 GPT 界面。</p><p>如何访问 Python 代码示例</p><p>下图显示了一个典型的 Playground 会话,我在其中输入提示,然后点击“查看代码”按钮并选择“Python”选项。我看到了工作代码,假设您将在第 4 行添加有效的 OpenAI API 密钥,可以从任何连接互联网的计算机复制和运行。</p><p>盖-2-3-1带有 Python 代码的 Playground 的 View 代码工具</p><p>现在不要担心细节,但花点时间浏览一下方法中包含的参数。openai.Completion.create()</p><p>当前在 Playground 右侧的“模型”字段中选择的模型在那里 (),我的实际提示 () 也是如此。事实上,我选择的每个配置选项都在那里。text-davinci-003Explain the purpose of...</p><p>换句话说,我可以在 Playground 中试验任何配置组合,然后复制代码并在任何地方运行它或它的变体。</p><p>事实上,这是您学习如何使用 API 的地方。换言之,这里会向你展示代码示例,这些示例可以构成你最终希望在自己的环境中运行的许多内容的基础。</p><p>如何访问 CURL 代码示例</p><p>下图向我们展示了如果我决定使用命令行工具 curl 而不是 Python,完全相同的提示将如何工作。</p><p>盖-2-4带有 curl 代码的 Playground 的 View 代码工具</p><p>curl是一个古老的开源命令行工具,通常默认可用。当您想要直接从命令行访问远程服务器时,通常会使用它。这些通常用于相对简单的请求。curl</p><p>另一方面,Python 将成为涉及编程逻辑的更复杂应用程序的首选工具。</p><p>要确认它在您的系统上可用,只需在任何命令行提示符下键入即可。您应该会看到某种帮助消息,其中包含正确使用的建议。curl</p><p>除了 Python 和 curl,您还可以在 Node.js(用于构建基于服务器的应用程序时)和 JSON(用于启用编程集成)中显示代码。</p><p>有了它,你就可以比简单的聊天会话更深入地了解了:你现在可以从你自己的命令行(或IDE)中精细地控制和以编程方式自动化你与GPT的交互。</p><p><br/></p><p style="box-sizing: inherit; margin-top: 0px; margin-bottom: 1.5em; padding: 0px; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: 1.5em; font-family: Lato, sans-serif; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 1.25em; vertical-align: baseline; min-width: 100%; color: rgb(10, 10, 35); text-wrap: wrap; background-color: rgb(255, 255, 255);"><br/></p>
<p>随着数字环境的不断发展和技术的进步,云计算仍然是开发人员学习的重要主题。</p><p>您可能在招聘信息中遇到过云要求,在对话中听到过人们谈论它,或者看到过提供云服务的公司的广告,现在您有兴趣了解更多信息。</p><p>在本文中,我将为您揭开云的神秘面纱,将复杂的概念分解为易于理解的部分。</p><p>以下是我们将在本文中介绍的内容:</p><p>云计算之前的世界</p><p>什么是云计算?</p><p>云服务提供商</p><p>不同的云服务</p><p>云计算帮助解决的挑战</p><p>云计算如何为您提供帮助?</p><p>云部署模型</p><p>因此,让我们深入研究并学习云计算基础知识,以便您可以开始进入这个不断发展的领域。</p><p>云计算之前的世界</p><p>要理解云计算,重要的是要了解云计算试图解决的问题。这将帮助您了解它的历史,并欣赏它为技术世界带来的便利。</p><p>在云出现之前,部署 Web 服务是一个非常昂贵的过程。若要部署 Web 应用程序,必须购买具有正确存储量和内存的服务器,将它们保存在现场,设置这些服务器,然后使用它们来托管应用程序。</p><p>除了原始服务器成本外,这还会带来许多额外成本,例如每次保持服务器正常运行和在线所需的电力和数据。还有安全问题,因为您需要防止您的服务器被不良行为者损坏或窃取。</p><p>除此之外,许多开发人员并不完全是服务器专家。因此,您要么必须培训您的开发人员成为系统管理员,要么雇用系统管理员来设置和配置服务器以使用您的应用程序。然后,要部署新的应用程序,您必须以新的成本重复整个过程。</p><p>在云出现之前,扩展应用程序并不容易。如果您试图管理成本而不购买太多服务器,或者雇用许多系统工程师,那么最终可能会没有足够的计算能力,并且需要扩展。扩展意味着购买更多服务器(或用更好的服务器替换现有服务器),配置它们以匹配现有配置,并在其上部署应用程序。</p><p>这些是我们在云计算之前面临的一些问题。现在,您可能想知道,“云计算如何解决这些问题?让我们来了解一下。</p><p>什么是云计算?</p><p>云计算是计算机系统资源的按需可用性,特别是数据存储(云存储)和计算能力,无需用户直接主动管理。</p><p>云计算依靠资源共享来实现一致性,通常使用即用即付模式,这有助于减少资本支出,但也可能导致用户意外的运营费用。- 维基百科</p><p>把云计算想象成从网吧租一台电脑。在网吧里,他们已经准备好了电脑——你只需进来,支付你想要的时间,然后在那段时间内使用电脑。</p><p>在云计算的情况下,您不必担心购买计算机、保护计算机或支付运行成本。云提供商涵盖了所有这些担忧。您很少需要担心软件问题,或者这些计算机上缺少特定软件。最好的部分是什么?您只需为您使用的计算机付费,并且只需为您使用它的时间付费。</p><p>因此,用更相关的术语来说,假设您有一个要部署的 Web 应用程序。您可以转到云服务提供商,选择应用程序所需的特定服务器要求,选择软件依赖项,然后部署应用程序,无需担心。</p><p>向一个五岁的孩子解释的云计算是“让别人管理你的计算需求”。你有一些网站需要上线,或者是一个移动应用程序,或者其他一些技术,而不是购买服务器来部署它,其他人购买并设置服务器。您只需上传文件即可。这几乎与租房相同,但现在您正在另一台计算机上租用计算空间。</p><p>云服务提供商</p><p>提供这些云服务的公司称为云服务提供商。这些公司已经拥有许多服务器和系统工程师。他们关心所有您不必担心的事情,例如服务器成本和运行成本。他们提供了一个 Web 界面,您可以在其中访问他们的服务器并在需要时使用它们。</p><p>目前,最受欢迎的云服务提供商是 Google(Google Cloud Platform)、Amazon AWS(Amazon Web Services)和 Microsoft (Azure)。这些公司提供类似的服务,但价格模型、功能等各不相同。</p><p>以下是他们各自提供的摘要:</p><p>谷歌</p><p>他们提供 IaaS(计算引擎)、容器即服务、CaaS(Kubernetes Engine)和 PaaS(App Engine)服务。他们还提供数据存储服务,包括 Google Cloud Storage、Cloud SQL 和 Cloud Bigtable 等工具。</p><p>AWS公司</p><p>这是第一家云服务提供商。他们提供 IaaS(弹性计算,EC2)、CaaS(弹性 Kubernetes 服务,EKS)和 PaaS(Elastic Beanstalk)服务。他们还提供数据存储服务,包括 Amazon S3 和 DynamoDb 等工具。</p><p>Microsoft</p><p>它们提供 IaaS(虚拟机)、CaaS(Kubernetes 服务、AKS)和 PaaS(应用服务)服务。他们还使用 Cosmos DB 等工具提供数据存储服务。</p><p>为了最大限度地利用资源,云服务提供商让其客户共享服务器。客户不必知道或担心此分配。</p><p>现在,让我们了解上述一些首字母缩略词/术语的含义,以便更好地了解这些云服务是什么。</p><p>不同的云服务</p><p>云服务提供商提供许多不同的计算服务。以下是最常见的服务:</p><p>SaaS(软件即服务)</p><p>萨斯-778x445极客超级</p><p>这项服务涉及您只使用某些软件,而不了解其源代码或主机环境或开发细节。您只需使用它并相信它正在正确管理和更新。</p><p>PaaS(平台即服务)</p><p>在这里,您只专注于应用程序开发,因为所有事情都已处理完毕(硬件、计算环境和所需的软件)。</p><p>IaaS(基础架构即服务)</p><p>这是所有云服务中最灵活的。在这里,你对事物拥有最大的控制权。您可以根据需要自定义和更改内容。</p><p>但你不拥有服务器。云服务提供商仅为您提供所需的基础架构,您负责创建自己的计算环境并安装应用程序运行所需的软件。</p><p>这里的想法类似于购买硬件。唯一的区别是,现在,你正在租用它,而且它是虚拟的(对你来说)。</p><p>云计算帮助解决的挑战</p><p>云计算消除了每次需要开发新应用程序时在现场购买和设置服务器的需要。这是一个很大的优势,因为它为公司节省了大量资金——无论是在服务器上,还是在设置和配置服务器以为您的应用程序工作的工程师上。</p><p>扩展通常并不容易,因为它通常涉及大量的设置和配置。例如,若要为应用程序提供更多存储,可以获取外部数据存储并连接到当前服务器。不过,这肯定有其局限性。</p><p>当您需要更好的服务器或额外的服务器时,可能会出现一个点。无论哪种方式,您为使其在以前的服务器上运行而安装的每个软件都必须在新服务器上重新安装。必须移动应用程序文件,依此类推。</p><p>此外,这些应用程序通常未充分利用这些硬件资源。从商业角度来看,这是一种损失。对此的初步解决方法是虚拟化。这基本上意味着创建虚拟(包含)环境,其中应用程序具有所有必需的软件,并且可以在服务器上正常运行。这导致了资源的更好利用。但这绝对不是完美的解决方案。</p><p>云计算如何为您提供帮助?</p><p>云计算提供了许多好处,其中一些在这里重点介绍:</p><p>支付订阅费用比构建整个数据中心更便宜。您有计划只允许您使用您使用的内容。</p><p>扩展和管理是云服务提供商的责任。</p><p>易于设置。开发人员只需专注于代码,因为所有实际的服务器和硬件设置都可以使用云服务提供商提供的用户界面完成。这样可以缩短开发时间,并更快地交付应用程序。</p><p>可及性。云服务提供商往往在全球拥有多个数据中心,以确保您的用户始终能够尽快访问您的服务。</p><p>数据安全。由于您的数据不再存储在您的物理空间中,因此不良行为者更难进入并破坏或窃取您的服务器。</p><p>云部署模型</p><p>云部署模型决定了数据(和应用程序)的存储位置以及客户如何与之交互。</p><p>公有云</p><p>在这里,一切都由云服务提供商处理。这是最受欢迎的模型。使用公有云时,您无需担心服务器维护,并且确信高可靠性和无限可扩展性的可能性。这通常意味着您与其他人共享服务器。</p><p>这是最便宜的模式,公司通常使用现收现付模式(因此您永远不必在财务上投入太多)。</p><p>这样做的一个潜在问题是,由云服务提供商处理所有内容意味着您对服务几乎没有控制权。</p><p>私有云</p><p>这类似于托管应用程序的传统方式。在这里,您确实拥有自己的数据中心。这意味着只有您可以使用服务器并对它们进行无限控制。</p><p>现在,区别在于,您为服务器的用户(通常是公司中的开发人员)提供自助服务界面。您仍然需要管理物理服务器,并负责其维护和扩展。</p><p>这种方法可能比公有云更安全,因为您可以控制并可以设置尽可能多的安全性。如果您的数据由于合规性要求而不能泄露,它也非常方便。</p><p>但这种方法的成本更高,尽管具有更可预测或固定的定价模型。这些服务器和硬件价格昂贵。拥有它们意味着有人管理它们,这意味着更多的费用。扩展还意味着每次都购买新设备。</p><p>混合云</p><p>什么是混合云混合云 |W3Codemasters(W3代码大师)</p><p>此方法合并了公有方法和云方法。它基本上可以为您提供更大的灵活性。您可以使用公共服务,也可以在需要时设置自己的公共服务。在这里,您可以通过服务器遵守法规,还可以通过云服务提供商提供高度可访问性。这种方法非常适合尝试从一种模型迁移到另一种模型的组织,在过渡期间保持业务运营。</p><p>这方面的一个示例应用程序是在公共云上托管应用程序,并将其连接到安全私有云上的数据库。</p><p>这种方法也比公共云更昂贵,因为您必须满足某些服务器的需求。当数据分布在多个服务器和应用程序之间时,它也可能变得复杂。</p><p>如何选择部署模型</p><p>公有云、私有云或混合云部署模型之间的选择取决于组织的独特要求。</p><p>公有云提供成本效益和全球覆盖</p><p>私有云提供增强的安全性和控制力</p><p>混合云提供了灵活性,并在两者之间实现了战略平衡。</p><p>该决定最终取决于安全需求、合规性要求、可伸缩性以及所需的 IT 基础架构控制级别等因素。</p><p>结论</p><p>现在,您应该有足够的信息来启动您在云计算领域的职业生涯。</p><p><br/></p><p style="box-sizing: inherit; margin-top: 0px; margin-bottom: 1.5em; padding: 0px; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: 1.5em; font-family: Lato, sans-serif; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 1.25em; vertical-align: baseline; min-width: 100%; color: rgb(10, 10, 35); text-wrap: wrap; background-color: rgb(255, 255, 255);"><br/></p>
<p>Go,也称为 Golang,是一种静态类型的编译编程语言,旨在实现简单性和效率。</p><p>在处理切片、地图和通道等数据结构时,您可能会遇到 和 函数。虽然两者都用于内存分配,但它们有不同的用途。new()make()</p><p>在本文中,我们将探讨 Go 和 中的区别,并讨论何时使用它们。new()make()</p><p>功能new()</p><p>Go 中的函数是一个内置函数,它为指定类型的新零值分配内存,并返回指向该值的指针。它主要用于初始化和获取指向给定类型的新分配的零值的指针,通常用于结构等数据类型。new()</p><p>下面是一个简单的示例:</p><p>package mainimport <span style="color: #ce9178;">"fmt"</span>type Person struct {</p><p> Name string</p><p> Age int</p><p> Gender string}func main() {</p><p> // Using new() to allocate memory for a Person struct</p><p> p := new(Person)</p><p> // Initializing the fields</p><p> <span style="color: #569cd6;">p.Name</span> = <span style="color: #ce9178;">"John Doe"</span></p><p> <span style="color: #569cd6;">p.Age</span> = 30</p><p> <span style="color: #569cd6;">p.Gender</span> = <span style="color: #ce9178;">"Male"</span></p><p> fmt.Println(p)}</p><p>在此示例中,为新结构分配内存,并且是指向新分配的零值的指针。new(Person)Personp</p><p>功能make()</p><p>另一方面,该函数用于初始化切片、映射和通道——需要运行时初始化的数据结构。与 不同,返回指定类型的初始化(非零化)值。make()new()make()</p><p>让我们看一个使用切片的示例:</p><p>package mainimport <span style="color: #ce9178;">"fmt"</span>func main() {</p><p> // Using make() to create a slice with a specified length and capacity</p><p> s := make([]int, 10, 15)</p><p> // Initializing the elements</p><p> for i := 0<span style="color: #6a9955;">; i < 10; i++ {</span></p><p> s[i] = i + 1</p><p> }</p><p> fmt.Println(s)}</p><p>在此示例中,创建一个长度为 10、容量为 15 的整数切片。该函数确保使用非零值初始化切片。make([]int, 10, 15)make()</p><p>何时使用和在 Go 中new()make()</p><p>用于值类型new()</p><p>在处理结构等值类型时,可用于为新的归零值分配内存。这适用于需要指向初始化结构的指针的方案。new()</p><p>p := new(Person)</p><p>用于引用类型:make()</p><p>对于切片、映射和通道(其中初始化涉及设置数据结构和内部指针),请使用创建初始化的实例。make()</p><p>s := make([]int, 5, 10)</p><p>指针与值:</p><p>请记住,返回一个指针,而返回一个非零值。根据是需要指针还是初始化值来选择适当的方法。new()make()</p><p>结论</p><p>了解 Go 中 和 之间的区别对于编写干净高效的代码至关重要。通过对适当的数据类型使用正确的方法,您可以确保在 Go 程序中正确分配和初始化内存。new()make()</p><p><br/></p><p style="box-sizing: inherit; margin-top: 0px; margin-bottom: 1.5em; padding: 0px; border: 0px; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: 1.5em; font-family: Lato, sans-serif; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 1.25em; vertical-align: baseline; min-width: 100%; color: rgb(10, 10, 35); text-wrap: wrap; background-color: rgb(255, 255, 255);"><br/></p>