<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>PWXCOO</title>
  
  <subtitle>内部整修</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://blog.pwxcoo.com/"/>
  <updated>2022-03-24T05:48:48.874Z</updated>
  <id>https://blog.pwxcoo.com/</id>
  
  <author>
    <name>pwxcoo</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>【Source Code】HashMap</title>
    <link href="https://blog.pwxcoo.com/2020/02/25/%E3%80%90Source-Code%E3%80%91HashMap/"/>
    <id>https://blog.pwxcoo.com/2020/02/25/【Source-Code】HashMap/</id>
    <published>2020-02-25T15:03:14.000Z</published>
    <updated>2022-03-24T05:48:48.874Z</updated>
    
    <content type="html"><![CDATA[<p>通过 HashMap 的 field 和 method（<code>hash()</code>, <code>put()</code>, <code>get()</code>, <code>resize()</code>）入手了解了一下 Java 中 HashMap 的设计。</p><h2 id="存储结构-字段"><a href="#存储结构-字段" class="headerlink" title="存储结构-字段"></a>存储结构-字段</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The default initial capacity - MUST be a power of two.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_INITIAL_CAPACITY</span> <span class="operator">=</span> <span class="number">1</span> &lt;&lt; <span class="number">4</span>; <span class="comment">// aka 16</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The maximum capacity, used if a higher value is implicitly specified</span></span><br><span class="line"><span class="comment"> * by either of the constructors with arguments.</span></span><br><span class="line"><span class="comment"> * MUST be a power of two &lt;= 1&lt;&lt;30.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MAXIMUM_CAPACITY</span> <span class="operator">=</span> <span class="number">1</span> &lt;&lt; <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The load factor used when none specified in constructor.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">float</span> <span class="variable">DEFAULT_LOAD_FACTOR</span> <span class="operator">=</span> <span class="number">0.75f</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The bin count threshold for using a tree rather than list for a</span></span><br><span class="line"><span class="comment"> * bin.  Bins are converted to trees when adding an element to a</span></span><br><span class="line"><span class="comment"> * bin with at least this many nodes. The value must be greater</span></span><br><span class="line"><span class="comment"> * than 2 and should be at least 8 to mesh with assumptions in</span></span><br><span class="line"><span class="comment"> * tree removal about conversion back to plain bins upon</span></span><br><span class="line"><span class="comment"> * shrinkage.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">TREEIFY_THRESHOLD</span> <span class="operator">=</span> <span class="number">8</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The bin count threshold for untreeifying a (split) bin during a</span></span><br><span class="line"><span class="comment"> * resize operation. Should be less than TREEIFY_THRESHOLD, and at</span></span><br><span class="line"><span class="comment"> * most 6 to mesh with shrinkage detection under removal.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">UNTREEIFY_THRESHOLD</span> <span class="operator">=</span> <span class="number">6</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The smallest table capacity for which bins may be treeified.</span></span><br><span class="line"><span class="comment"> * (Otherwise the table is resized if too many nodes in a bin.)</span></span><br><span class="line"><span class="comment"> * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts</span></span><br><span class="line"><span class="comment"> * between resizing and treeification thresholds.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MIN_TREEIFY_CAPACITY</span> <span class="operator">=</span> <span class="number">64</span>;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The table, initialized on first use, and resized as</span></span><br><span class="line"><span class="comment"> * necessary. When allocated, length is always a power of two.</span></span><br><span class="line"><span class="comment"> * (We also tolerate length zero in some operations to allow</span></span><br><span class="line"><span class="comment"> * bootstrapping mechanics that are currently not needed.)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">transient</span> Node&lt;K,V&gt;[] table;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Holds cached entrySet(). Note that AbstractMap fields are used</span></span><br><span class="line"><span class="comment"> * for keySet() and values().</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">transient</span> Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The number of key-value mappings contained in this map.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">transient</span> <span class="type">int</span> size;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The number of times this HashMap has been structurally modified</span></span><br><span class="line"><span class="comment"> * Structural modifications are those that change the number of mappings in</span></span><br><span class="line"><span class="comment"> * the HashMap or otherwise modify its internal structure (e.g.,</span></span><br><span class="line"><span class="comment"> * rehash).  This field is used to make iterators on Collection-views of</span></span><br><span class="line"><span class="comment"> * the HashMap fail-fast.  (See ConcurrentModificationException).</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">transient</span> <span class="type">int</span> modCount;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The next size value at which to resize (capacity * load factor).</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@serial</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">// (The javadoc description is true upon serialization.</span></span><br><span class="line"><span class="comment">// Additionally, if the table array has not been allocated, this</span></span><br><span class="line"><span class="comment">// field holds the initial array capacity, or zero signifying</span></span><br><span class="line"><span class="comment">// DEFAULT_INITIAL_CAPACITY.)</span></span><br><span class="line"><span class="type">int</span> threshold;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * The load factor for the hash table.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@serial</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">final</span> <span class="type">float</span> loadFactor;</span><br></pre></td></tr></table></figure><ul><li>DEFAULT_INITIAL_CAPACITY，默认 HashMap 的大小</li><li>MAXIMUM_CAPACITY，最大的 HashMap 的大小</li><li>DEFAULT_LOAD_FACTOR，默认的负载因子的大小</li><li>TREEIFY_THRESHOLD，将链表升级成红黑树的 threshold</li><li>UNTREEIFY_THRESHOLD，当 HashMap 删除元素后，红黑树退化成链表的 threshold</li><li>MIN_TREEIFY_CAPACITY，升级成红黑树的最小容量，表示 HashMap 达到这个容量之后，一定会升级成红黑树或者 resize。 (但是我没在别人的博客里看到过这个。。我只是按照注释理解了一下。。这个东西下面没用到)</li><li>table[]，存放数据的数组，每个元素是一个内部类 Node  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">Map</span>.Entry&lt;K,V&gt; &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> hash;</span><br><span class="line">    <span class="keyword">final</span> K key;</span><br><span class="line">    V value;</span><br><span class="line">    Node&lt;K,V&gt; next;</span><br><span class="line"></span><br><span class="line">    Node(<span class="type">int</span> hash, K key, V value, Node&lt;K,V&gt; next) &#123;</span><br><span class="line">        <span class="built_in">this</span>.hash = hash;</span><br><span class="line">        <span class="built_in">this</span>.key = key;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">        <span class="built_in">this</span>.next = next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> K <span class="title function_">getKey</span><span class="params">()</span>        &#123; <span class="keyword">return</span> key; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> V <span class="title function_">getValue</span><span class="params">()</span>      &#123; <span class="keyword">return</span> value; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> String <span class="title function_">toString</span><span class="params">()</span> &#123; <span class="keyword">return</span> key + <span class="string">&quot;=&quot;</span> + value; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Objects.hashCode(key) ^ Objects.hashCode(value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> V <span class="title function_">setValue</span><span class="params">(V newValue)</span> &#123;</span><br><span class="line">        <span class="type">V</span> <span class="variable">oldValue</span> <span class="operator">=</span> value;</span><br><span class="line">        value = newValue;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (o == <span class="built_in">this</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (o <span class="keyword">instanceof</span> Map.Entry) &#123;</span><br><span class="line">            Map.Entry&lt;?,?&gt; e = (Map.Entry&lt;?,?&gt;)o;</span><br><span class="line">            <span class="keyword">if</span> (Objects.equals(key, e.getKey()) &amp;&amp;</span><br><span class="line">                Objects.equals(value, e.getValue()))</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>hash 就是 hash 值</li><li>key 是存放的 key 值</li><li>value 是存放的 value 值</li><li>next 是指向下一个 Node 的引用，生成链表时用的，<strong>如果升级成红黑树，会用 TreeNode (也是一个内部类)  代替 Node</strong></li></ul></li><li>size，HashMap 的 size</li><li>modCount，表示 HashMap 的 structural modified 次数 (比如，添加删除元素，或者 resize 都算，但是如果仅仅是值的改变不算) 。当迭代操作或者序列化操作时，操作前后需要比较 modCount 是否相等，不相等就 Fail-Fast，抛出 ConcurrentModificationException。</li><li>threshold，resize 的大小，等于  (capacity * load factor) ，达到 threshold 的时候就会扩容 (会 rehash，复制数据等操作，比较消耗性能)</li><li>loadFactor，负载因子</li></ul><h2 id="功能实现-method"><a href="#功能实现-method" class="headerlink" title="功能实现 - method"></a>功能实现 - method</h2><h3 id="hash"><a href="#hash" class="headerlink" title="hash()"></a>hash()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hash</span><span class="params">(Object key)</span> &#123;   <span class="comment">//jdk1.8 &amp; jdk1.7</span></span><br><span class="line">     <span class="type">int</span> h;</span><br><span class="line">     <span class="comment">// h = key.hashCode() 为第一步 取 hashCode 值</span></span><br><span class="line">     <span class="comment">// h ^ (h &gt;&gt;&gt; 16)  为第二步 高位参与运算</span></span><br><span class="line">     <span class="keyword">return</span> (key == <span class="literal">null</span>) ? <span class="number">0</span> : (h = key.hashCode()) ^ (h &gt;&gt;&gt; <span class="number">16</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li>取 key 的 hashCode 值</li><li>高位运算</li><li>取模运算</li></ol><p>这个有一个点，就是 HashMap 中桶的个数 capacity 设计成 2 的幂次（<strong>一般都是设计成素数的</strong>）。之所以这样设计是为了在<strong>取模</strong>和<strong>扩容</strong>时做优化：</p><ul><li>取模可以直接优化成了 and 位运算。</li><li>扩容时重新计算元素位置时取模更快了，而且扩容时可以直接根据 mask 位置上的新增位数是 0 还是 1 来确定当前元素是在【当前位置】，还是在【当前位置 * 2】的位置上。</li></ul><p>但是这样设计也带来了一些问题，取模后的哈希值是有可能出现很差的哈希分布的，在 capacity 比较小的时候，hash 值高位 Bit 的信息完全丢失了。所以做了一个高位运算逻辑右移 <code>&gt;&gt;&gt;</code> 16 位（逻辑右移，左边全部使用 0 填充），减少高位 Bit 信息的丢失。</p><h3 id="put"><a href="#put" class="headerlink" title="put()"></a>put()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> V <span class="title function_">putVal</span><span class="params">(<span class="type">int</span> hash, K key, V value, <span class="type">boolean</span> onlyIfAbsent,</span></span><br><span class="line"><span class="params">                <span class="type">boolean</span> evict)</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; <span class="type">int</span> n, i;</span><br><span class="line">    <span class="keyword">if</span> ((tab = table) == <span class="literal">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">        n = (tab = resize()).length;</span><br><span class="line">    <span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) &amp; hash]) == <span class="literal">null</span>)</span><br><span class="line">        tab[i] = newNode(hash, key, value, <span class="literal">null</span>);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        Node&lt;K,V&gt; e; K k;</span><br><span class="line">        <span class="keyword">if</span> (p.hash == hash &amp;&amp;</span><br><span class="line">            ((k = p.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            e = p;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">            e = ((TreeNode&lt;K,V&gt;)p).putTreeVal(<span class="built_in">this</span>, tab, hash, key, value);</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">binCount</span> <span class="operator">=</span> <span class="number">0</span>; ; ++binCount) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((e = p.next) == <span class="literal">null</span>) &#123;</span><br><span class="line">                    p.next = newNode(hash, key, value, <span class="literal">null</span>);</span><br><span class="line">                    <span class="keyword">if</span> (binCount &gt;= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></span><br><span class="line">                        treeifyBin(tab, hash);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                p = e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123; <span class="comment">// existing mapping for key</span></span><br><span class="line">            <span class="type">V</span> <span class="variable">oldValue</span> <span class="operator">=</span> e.value;</span><br><span class="line">            <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="literal">null</span>)</span><br><span class="line">                e.value = value;</span><br><span class="line">            afterNodeAccess(e);</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ++modCount;</span><br><span class="line">    <span class="keyword">if</span> (++size &gt; threshold)</span><br><span class="line">        resize();</span><br><span class="line">    afterNodeInsertion(evict);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>判断当前 table 是否未初始化，未初始化就初始化一波</li><li>根据 hash 值定位到具体的 bin，如果这个 bin 为空，直接新建一个 bin 放入这个 Node，<strong>然后返回</strong></li><li>判断 key 是否和当前结点的 key 相等，相等就直接替换 Node (或者 key 都为 null 的时候，HashMap 允许 key 为 null，顺便再插一句，HashMap 中 null 无法计算其 hash 值，默认都是放到下标为 0 的  上)</li><li>如果当前结点是 TreeNode 红黑树结点，就按红黑树的方法插入 (具体就不展开了)</li><li>如果是链表，就遍历链表，找到相同的 key 的 Node 就可以直接替换 Node，如果没找到就 newNode() (这个方法会在对应的 bin 中插入 Node)</li><li>统一替换 Node (即 e 不为空)</li><li>modCount 自增</li><li>判断是否需要 resize</li></ul><h3 id="get"><a href="#get" class="headerlink" title="get()"></a>get()</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Returns the value to which the specified key is mapped,</span></span><br><span class="line"><span class="comment">* or &#123;<span class="doctag">@code</span> null&#125; if this map contains no mapping for the key.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* &lt;p&gt;More formally, if this map contains a mapping from a key</span></span><br><span class="line"><span class="comment">* &#123;<span class="doctag">@code</span> k&#125; to a value &#123;<span class="doctag">@code</span> v&#125; such that &#123;<span class="doctag">@code</span> (key==null ? k==null :</span></span><br><span class="line"><span class="comment">* key.equals(k))&#125;, then this method returns &#123;<span class="doctag">@code</span> v&#125;; otherwise</span></span><br><span class="line"><span class="comment">* it returns &#123;<span class="doctag">@code</span> null&#125;.  (There can be at most one such mapping.)</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* &lt;p&gt;A return value of &#123;<span class="doctag">@code</span> null&#125; does not &lt;i&gt;necessarily&lt;/i&gt;</span></span><br><span class="line"><span class="comment">* indicate that the map contains no mapping for the key; it&#x27;s also</span></span><br><span class="line"><span class="comment">* possible that the map explicitly maps the key to &#123;<span class="doctag">@code</span> null&#125;.</span></span><br><span class="line"><span class="comment">* The &#123;<span class="doctag">@link</span> #containsKey containsKey&#125; operation may be used to</span></span><br><span class="line"><span class="comment">* distinguish these two cases.</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* <span class="doctag">@see</span> #put(Object, Object)</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> V <span class="title function_">get</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt; e;</span><br><span class="line">    <span class="keyword">return</span> (e = getNode(hash(key), key)) == <span class="literal">null</span> ? <span class="literal">null</span> : e.value;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Implements Map.get and related methods</span></span><br><span class="line"><span class="comment">*</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> hash hash for key</span></span><br><span class="line"><span class="comment">* <span class="doctag">@param</span> key the key</span></span><br><span class="line"><span class="comment">* <span class="doctag">@return</span> the node, or null if none</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">final</span> Node&lt;K,V&gt; <span class="title function_">getNode</span><span class="params">(<span class="type">int</span> hash, Object key)</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; first, e; <span class="type">int</span> n; K k;</span><br><span class="line">    <span class="keyword">if</span> ((tab = table) != <span class="literal">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">        (first = tab[(n - <span class="number">1</span>) &amp; hash]) != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (first.hash == hash &amp;&amp; <span class="comment">// always check first node</span></span><br><span class="line">            ((k = first.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">            <span class="keyword">return</span> first;</span><br><span class="line">        <span class="keyword">if</span> ((e = first.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (first <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line">                <span class="keyword">return</span> ((TreeNode&lt;K,V&gt;)first).getTreeNode(hash, key);</span><br><span class="line">            <span class="keyword">do</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != <span class="literal">null</span> &amp;&amp; key.equals(k))))</span><br><span class="line">                    <span class="keyword">return</span> e;</span><br><span class="line">            &#125; <span class="keyword">while</span> ((e = e.next) != <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>判断 table 是否未初始化，未初始化或者定位到的 bin 为空的话，直接<strong>返回 null</strong></li><li>判断第一个 Node/TreeNode 的值是否为查询的 key，是的话直接返回 (always check first node) ，若不匹配就下一步</li><li>判断为 TreeNode，查找红黑树</li><li>判断链表，遍历链表查找</li></ul><h3 id="resize"><a href="#resize" class="headerlink" title="resize()"></a>resize()</h3><p>扩容 (resize) 就是重新计算容量，向 HashMap 对象里不停的添加元素，而 HashMap 对象内部的数组无法装载更多的元素时，对象就需要扩大数组的长度，以便能装入更多的元素。当然 Java 里的数组是无法自动扩容的，方法是使用一个新的数组代替已有的容量小的数组，就像我们用一个小桶装水，如果想装更多的水，就得换大水桶。<strong>所以扩容就涉及到哈希值的重新计算，复制元素等操作，是很消耗性能的，如果能在开始就能确定好元素的个数，建议在一开始就设置好。</strong></p><p>当插完元素后，<code>size &gt; threshold = capacity * Load factor</code>，扩容就开始了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">resize</span><span class="params">(<span class="type">int</span> newCapacity)</span> &#123;   <span class="comment">//传入新的容量</span></span><br><span class="line">    Entry[] oldTable = table;    <span class="comment">//引用扩容前的 Entry 数组</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">oldCapacity</span> <span class="operator">=</span> oldTable.length;</span><br><span class="line">    <span class="keyword">if</span> (oldCapacity == MAXIMUM_CAPACITY) &#123;  <span class="comment">//扩容前的数组大小如果已经达到最大 (2^30) 了</span></span><br><span class="line">        threshold = Integer.MAX_VALUE; <span class="comment">//修改阈值为 int 的最大值 (2^31-1)，这样以后就不会扩容了</span></span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    Entry[] newTable = <span class="keyword">new</span> <span class="title class_">Entry</span>[newCapacity];  <span class="comment">//初始化一个新的 Entry 数组</span></span><br><span class="line">    transfer(newTable);                         <span class="comment">//！！将数据转移到新的 Entry 数组里</span></span><br><span class="line">    table = newTable;                           <span class="comment">//HashMap 的 table 属性引用新的 Entry 数组</span></span><br><span class="line">    threshold = (<span class="type">int</span>)(newCapacity * loadFactor);<span class="comment">//修改阈值</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>transfer() 方法将原有 Entry 数组的元素拷贝到新的 Entry 数组里。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Entry[] newTable)</span> &#123;</span><br><span class="line">    Entry[] src = table;                   <span class="comment">//src 引用了旧的 Entry 数组</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">newCapacity</span> <span class="operator">=</span> newTable.length;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; src.length; j++) &#123; <span class="comment">//遍历旧的 Entry 数组</span></span><br><span class="line">        Entry&lt;K,V&gt; e = src[j];             <span class="comment">//取得旧 Entry 数组的每个元素</span></span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">            src[j] = <span class="literal">null</span>;<span class="comment">//释放旧 Entry 数组的对象引用（for 循环后，旧的 Entry 数组不再引用任何对象）</span></span><br><span class="line">            <span class="keyword">do</span> &#123;</span><br><span class="line">                Entry&lt;K,V&gt; next = e.next;</span><br><span class="line">                <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> indexFor(e.hash, newCapacity); <span class="comment">//！！重新计算每个元素在数组中的位置</span></span><br><span class="line">                e.next = newTable[i]; <span class="comment">//标记 [1]</span></span><br><span class="line">                newTable[i] = e;      <span class="comment">//将元素放在数组上</span></span><br><span class="line">                e = next;             <span class="comment">//访问下一个 Entry 链上的元素</span></span><br><span class="line">            &#125; <span class="keyword">while</span> (e != <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看出来复制的时候，链表是用头插法的方式复制的，所以原来的链表会被 revert 一下。<strong>在多线程场景，在两个线程 resize 的时候，可能会出现环形链表。</strong></p><h2 id="扩展问题"><a href="#扩展问题" class="headerlink" title="扩展问题"></a>扩展问题</h2><h3 id="1-Hash-时取模一定要模质数吗？"><a href="#1-Hash-时取模一定要模质数吗？" class="headerlink" title="1. Hash 时取模一定要模质数吗？"></a>1. Hash 时取模一定要模质数吗？</h3><p>好的 hash 算法期望是 hash 值在取模后能尽可能均匀地分布。不模 prime number 的话，很容易会有分布不均匀的情况产生。</p><p>假设需要放入长为 m 的 hash 表的数是 n，取模函数是： $f(n) = n \% m$ ， $f(n)$ 为需要放置在 hash 表中的位置的下标。</p><p>取 m 为质数的目的是：让 m 和 n 的最大公约数为 1，也就是： $gcd(m, n)=1$ 。因为在 m 为质数时，除非 n 恰好是 m 的倍数，否则他们的最大公约数都为 1。</p><p>$$  f(n) = n \% m $$</p><p>$$  \Rightarrow a \times m + f(n) = n(a = 0, 1, 2,3…) $$</p><p>$$ \Rightarrow f(n) = n - a \times m $$</p><p>$$ \Rightarrow f(n) = gcd(m, n) *( \frac{n}{gcd(m,n)} - \frac{a \times m}{gcd(m,n)}) $$</p><p>也就是我们算出来的 hash 值只可能是为 $gcd(m, n)$ 的倍数的那些值，倘若数据特别一点，全都是 gcd 的倍数，这样会让分布很不均匀。</p><ul><li><a href="https://www.zhihu.com/question/20806796/answer/231084056">Hash 时取模一定要模质数吗？ - Porzy 的回答 - 知乎</a></li></ul><h3 id="2-为什么-java-在计算-hashcode-时使用-31-作为乘数？"><a href="#2-为什么-java-在计算-hashcode-时使用-31-作为乘数？" class="headerlink" title="2. 为什么 java 在计算 hashcode 时使用 31 作为乘数？"></a>2. 为什么 java 在计算 hashcode 时使用 31 作为乘数？</h3><p>确实很奇怪。。感觉是个 magic number。把这个搞成了一个类似 31 进制的数字。看了很多说法，stackoverflow 上说：</p><blockquote><p>The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i &lt;&lt; 5) - i. Modern VMs do this sort of optimization automatically.</p></blockquote><p>是因为 31 是个奇素数，并且可以直接优化成位运算提高性能。偶数的话，相当于 shift 运算，会丢失信息，但是奇数因为在 shift 后在加上原数，所以比偶数好。之所以用素数是因为 traditional。。（我看到有的会用 33 来做，据说和 31 差不多）。</p><p>有个答案提到是根据实验得出发现 31 的，根据字典跑数据跑出的结果。（但是我看那个数据也是英文字典，ascii 字符，要是非 ascii 字符的实验结果呢？）</p><ul><li><a href="https://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier">Why does Java’s hashCode() in String use 31 as a multiplier? - Stack Overflow</a></li><li><a href="https://mp.weixin.qq.com/s/sCWQGU_OWiQkDUuSPXvw-w">为什么Java String哈希乘数为31？</a></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://tech.meituan.com/2016/06/24/java-hashmap.html">Java 8 系列之重新认识 HashMap - 美团技术团队</a></li><li><a href="https://crossoverjie.top/2018/07/23/java-senior/ConcurrentHashMap/">HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你！ | crossoverJie’s Blog</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="source" scheme="https://blog.pwxcoo.com/categories/source/"/>
    
    
      <category term="java" scheme="https://blog.pwxcoo.com/tags/java/"/>
    
      <category term="source_code" scheme="https://blog.pwxcoo.com/tags/source-code/"/>
    
  </entry>
  
  <entry>
    <title>mathematical vocabulary</title>
    <link href="https://blog.pwxcoo.com/2020/02/23/mathematical-vocabulary/"/>
    <id>https://blog.pwxcoo.com/2020/02/23/mathematical-vocabulary/</id>
    <published>2020-02-23T13:02:46.000Z</published>
    <updated>2022-03-24T05:48:48.871Z</updated>
    
    <content type="html"><![CDATA[<table><thead><tr><th style="text-align:center"><strong>ENGLISH</strong></th><th style="text-align:center"><strong>CHINESE</strong></th></tr></thead><tbody><tr><td style="text-align:center">annual rate of interest</td><td style="text-align:center">年利率</td></tr><tr><td style="text-align:center">approximate value</td><td style="text-align:center">近似值</td></tr><tr><td style="text-align:center">arithmetic sequence</td><td style="text-align:center">等差数列</td></tr><tr><td style="text-align:center">associative law</td><td style="text-align:center">结合律</td></tr><tr><td style="text-align:center">asymptotic line</td><td style="text-align:center">渐近线</td></tr><tr><td style="text-align:center">augmented matrix</td><td style="text-align:center">增广矩阵</td></tr><tr><td style="text-align:center">binomial</td><td style="text-align:center">二项式</td></tr><tr><td style="text-align:center">binomial theorem</td><td style="text-align:center">二项式定理</td></tr><tr><td style="text-align:center">Cartesian coordinates</td><td style="text-align:center">笛卡尔坐标</td></tr><tr><td style="text-align:center">Cartesian product</td><td style="text-align:center">笛卡尔积</td></tr><tr><td style="text-align:center">Cauchy inequality</td><td style="text-align:center">柯西不等式</td></tr><tr><td style="text-align:center">circulating decimal</td><td style="text-align:center">循环小数</td></tr><tr><td style="text-align:center">clockwise</td><td style="text-align:center">顺时针的</td></tr><tr><td style="text-align:center">coefficient</td><td style="text-align:center">系数</td></tr><tr><td style="text-align:center">common factor</td><td style="text-align:center">公因子</td></tr><tr><td style="text-align:center">common logarithm</td><td style="text-align:center">常用对数</td></tr><tr><td style="text-align:center">common multiple</td><td style="text-align:center">公倍数</td></tr><tr><td style="text-align:center">common ratio</td><td style="text-align:center">公比</td></tr><tr><td style="text-align:center">commutative law</td><td style="text-align:center">交换律</td></tr><tr><td style="text-align:center">complement</td><td style="text-align:center">余</td></tr><tr><td style="text-align:center">component</td><td style="text-align:center">分量；联通分量</td></tr><tr><td style="text-align:center">compound interest</td><td style="text-align:center">复利</td></tr><tr><td style="text-align:center">convergence</td><td style="text-align:center">收敛</td></tr><tr><td style="text-align:center">cosine</td><td style="text-align:center">余弦</td></tr><tr><td style="text-align:center">cotangent</td><td style="text-align:center">余切</td></tr><tr><td style="text-align:center">counterclockwise</td><td style="text-align:center">逆时针的</td></tr><tr><td style="text-align:center">cross product</td><td style="text-align:center">叉积；向量积；外积</td></tr><tr><td style="text-align:center">definite integral</td><td style="text-align:center">定积分</td></tr><tr><td style="text-align:center">denominator</td><td style="text-align:center">分母</td></tr><tr><td style="text-align:center">derivative</td><td style="text-align:center">导数</td></tr><tr><td style="text-align:center">diameter</td><td style="text-align:center">直径</td></tr><tr><td style="text-align:center">differentiable</td><td style="text-align:center">可微分的</td></tr><tr><td style="text-align:center">divergence</td><td style="text-align:center">发散</td></tr><tr><td style="text-align:center">dividend</td><td style="text-align:center">被除数</td></tr><tr><td style="text-align:center">divisor</td><td style="text-align:center">因子；因式；因数；除数</td></tr><tr><td style="text-align:center">exponent</td><td style="text-align:center">指数</td></tr><tr><td style="text-align:center">extreme value</td><td style="text-align:center">极值</td></tr><tr><td style="text-align:center">factorial</td><td style="text-align:center">阶乘</td></tr><tr><td style="text-align:center">fraction</td><td style="text-align:center">分数；分式</td></tr><tr><td style="text-align:center">geometric sequence</td><td style="text-align:center">等比数列</td></tr><tr><td style="text-align:center">irrational number</td><td style="text-align:center">无理数</td></tr><tr><td style="text-align:center">least common multiple</td><td style="text-align:center">最小公倍数</td></tr><tr><td style="text-align:center">mean</td><td style="text-align:center">平均；平均值；平均数</td></tr><tr><td style="text-align:center">median</td><td style="text-align:center">中位数；中线</td></tr><tr><td style="text-align:center">minuend</td><td style="text-align:center">被减数</td></tr><tr><td style="text-align:center">monomial</td><td style="text-align:center">单项式；单项的</td></tr><tr><td style="text-align:center">natural logarithm</td><td style="text-align:center">自然对数</td></tr><tr><td style="text-align:center">normal vector</td><td style="text-align:center">法向量</td></tr><tr><td style="text-align:center">orthogonal</td><td style="text-align:center">正交；直交</td></tr><tr><td style="text-align:center">parabola</td><td style="text-align:center">拋物线</td></tr><tr><td style="text-align:center">parallel</td><td style="text-align:center">平行</td></tr><tr><td style="text-align:center">plane</td><td style="text-align:center">平面</td></tr><tr><td style="text-align:center">point of tangency</td><td style="text-align:center">切点</td></tr><tr><td style="text-align:center">polynomial</td><td style="text-align:center">多项式</td></tr><tr><td style="text-align:center">quadratic function</td><td style="text-align:center">二次函数</td></tr><tr><td style="text-align:center">rational number</td><td style="text-align:center">有理数</td></tr><tr><td style="text-align:center">sine</td><td style="text-align:center">正弦</td></tr><tr><td style="text-align:center">slope</td><td style="text-align:center">斜率</td></tr><tr><td style="text-align:center">square root</td><td style="text-align:center">平方根</td></tr><tr><td style="text-align:center">standard deviation</td><td style="text-align:center">标准差</td></tr><tr><td style="text-align:center">sum-to-product formula</td><td style="text-align:center">和差化积公式</td></tr><tr><td style="text-align:center">supplementary angle</td><td style="text-align:center">补角</td></tr><tr><td style="text-align:center">tangent</td><td style="text-align:center">正切</td></tr><tr><td style="text-align:center">tangent line</td><td style="text-align:center">切线</td></tr><tr><td style="text-align:center">wave crest</td><td style="text-align:center">波峰</td></tr><tr><td style="text-align:center">wave trough</td><td style="text-align:center">波谷</td></tr></tbody></table><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="http://www.mathland.idv.tw/scene/tables/mathchieng.pdf">數學名詞中英對照表</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="memo" scheme="https://blog.pwxcoo.com/tags/memo/"/>
    
      <category term="mathematic" scheme="https://blog.pwxcoo.com/tags/mathematic/"/>
    
  </entry>
  
  <entry>
    <title>volatile</title>
    <link href="https://blog.pwxcoo.com/2020/01/18/volatile/"/>
    <id>https://blog.pwxcoo.com/2020/01/18/volatile/</id>
    <published>2020-01-18T07:18:26.000Z</published>
    <updated>2022-03-24T05:48:48.872Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Lock"><a href="#Lock" class="headerlink" title="Lock"></a>Lock</h2><p>Locks offer two primary features: <strong>mutual exclusion</strong> and <strong>visibility</strong>.</p><ul><li><strong>Mutual exclusion</strong> means that only one thread at a time may hold a given lock, and this property can be used to implement protocols for coordinating access to shared data such that only one thread at a time will be using the shard data.</li><li><strong>Visibility</strong> is more subtle and has to do with ensuring that change made to shared data prior to releasing a lock are made visible to another thread that subsequently acquires that lock – without the visibility guarantees provides by synchronization, thread could see stale or inconsistent values for shared variables, which could cause a host of serious problems.</li></ul><h2 id="Volatile"><a href="#Volatile" class="headerlink" title="Volatile"></a>Volatile</h2><p>Volatile variables in the Java language can be thought of as “<code>synchronized</code> lite”; they require less coding to use than <code>synchronized</code> blocks and often have less runtime overhead, but they can only be used to do a subset of the things that <code>synchronized</code> can. In cases where reads greatly out number writes, volatile variables can often reduce the performance cost of synchronization compared to locking.</p><p>Volatile provides two guarantees:</p><ul><li><strong>visibility</strong>. Volatile variables share the visibility features of synchronized, but none of atomicity features. This mean that threads will automatically see the most up-to-date value for volatile variables.</li><li><strong>establish a happen-before relationship</strong>. (In Java5 or later)</li></ul><h3 id="Condition-for-correct-use-of-volatile"><a href="#Condition-for-correct-use-of-volatile" class="headerlink" title="Condition for correct use of volatile"></a>Condition for correct use of volatile</h3><p>You can use volatile variables instead of locks only under a restricted set of circumstances. Both of the following criteria must be met for volatile variables to provide the desired thread-safety:</p><ul><li>Writes to the variables do not depend on its current value.</li><li>The variable does not participate in invariants with other variables.</li></ul><h3 id="Implement-volatile"><a href="#Implement-volatile" class="headerlink" title="Implement volatile"></a>Implement volatile</h3><p><code>Memory Barrier</code>. More details see <a href="http://gee.cs.oswego.edu/dl/jmm/cookbook.html">The JSR-133 Cookbook for Compiler Writers</a>.</p><h3 id="Change-in-JSR-133"><a href="#Change-in-JSR-133" class="headerlink" title="Change in JSR-133"></a>Change in JSR-133</h3><p>Forbid reordering between volatile variable and normal variable.</p><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://www.ibm.com/developerworks/library/j-jtp06197/index.html">Managing volatility</a></li><li><a href="https://www.ibm.com/developerworks/library/j-jtp02244/index.html">Fixing the Java Memory Model, Part 1</a></li><li><a href="https://www.ibm.com/developerworks/library/j-jtp03304/">Fixing the Java Memory Model, Part 2</a></li><li><a href="http://www.cs.umd.edu/~pugh/java/memoryModel/">The Java Memory Model</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="note" scheme="https://blog.pwxcoo.com/categories/note/"/>
    
    
      <category term="java" scheme="https://blog.pwxcoo.com/tags/java/"/>
    
      <category term="concurrent" scheme="https://blog.pwxcoo.com/tags/concurrent/"/>
    
  </entry>
  
  <entry>
    <title>【Source Code】DelayQueue</title>
    <link href="https://blog.pwxcoo.com/2019/11/18/Source-Code-DelayQueue/"/>
    <id>https://blog.pwxcoo.com/2019/11/18/Source-Code-DelayQueue/</id>
    <published>2019-11-17T16:34:27.000Z</published>
    <updated>2022-03-24T05:48:48.870Z</updated>
    
    <content type="html"><![CDATA[<p>An unbound blocking queue of Delay elements, in which an element can only be taken when its delay has expired. The head of queue is that Delayed element whose delay expired furthest in the past. If no delay has expired there is no head and poll wil return null. Expiration occurs when an element’s getDelay(TimeUnit.NANOSECONDS) method return a value less than or equal to zero. Even though unexpired elements cannot be removed using take or poll, they are otherwise treated as normal elements. For example, the size method returns the count of both expired and unexpired elements. This queue does not permit null element.</p><p><strong>In short, the queue is a Priority Queue. Using leader-follower mode to reduce followers’ dispatch.</strong></p><h2 id="private-variable"><a href="#private-variable" class="headerlink" title="private variable"></a>private variable</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">transient</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> PriorityQueue&lt;E&gt; q = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;E&gt;();</span><br><span class="line"><span class="keyword">private</span> <span class="type">Thread</span> <span class="variable">leader</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Condition signalled when a newer element becomes available</span></span><br><span class="line"><span class="comment">* at the head of the queue or a new thread may need to</span></span><br><span class="line"><span class="comment">* become leader.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">available</span> <span class="operator">=</span> lock.newCondition();</span><br></pre></td></tr></table></figure><h2 id="offer"><a href="#offer" class="headerlink" title="offer()"></a>offer()</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Inserts the specified element into this delay queue.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> e the element to add</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> &#123;<span class="doctag">@code</span> true&#125;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NullPointerException if the specified element is null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e)</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        q.offer(e);</span><br><span class="line">        <span class="keyword">if</span> (q.peek() == e) &#123;</span><br><span class="line">            <span class="comment">// 如果新增的元素需要的延迟更小，leader置为null，唤醒线程来抢锁</span></span><br><span class="line">            leader = <span class="literal">null</span>;</span><br><span class="line">            available.signal();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="take"><a href="#take" class="headerlink" title="take()"></a>take()</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Retrieves and removes the head of this queue, waiting if necessary</span></span><br><span class="line"><span class="comment"> * until an element with an expired delay is available on this queue.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> the head of this queue</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> InterruptedException &#123;<span class="doctag">@inheritDoc</span>&#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> E <span class="title function_">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">E</span> <span class="variable">first</span> <span class="operator">=</span> q.peek();</span><br><span class="line">            <span class="keyword">if</span> (first == <span class="literal">null</span>)</span><br><span class="line">                <span class="comment">// 队列是空的，就等着offer之后插进元素来唤醒</span></span><br><span class="line">                available.await();</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">long</span> <span class="variable">delay</span> <span class="operator">=</span> first.getDelay(NANOSECONDS);</span><br><span class="line">                <span class="keyword">if</span> (delay &lt;= <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">return</span> q.poll();</span><br><span class="line">                <span class="comment">// 防止因为这个线程一直被阻塞，但其实这个对象可能已经被take掉了，却一直拿着对象的引用而无法被垃圾回收。这个点太细了</span></span><br><span class="line">                first = <span class="literal">null</span>; <span class="comment">// don&#x27;t retain ref while waiting</span></span><br><span class="line">                <span class="keyword">if</span> (leader != <span class="literal">null</span>)</span><br><span class="line">                    <span class="comment">// 有leader，follower就一直等着就好了</span></span><br><span class="line">                    available.await();</span><br><span class="line">                <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 没有leader，该线程变成leader</span></span><br><span class="line">                    <span class="type">Thread</span> <span class="variable">thisThread</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">                    leader = thisThread;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="comment">// 定时阻塞</span></span><br><span class="line">                        available.awaitNanos(delay);</span><br><span class="line">                    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                        <span class="comment">// 什么情况下 leader != thisThread 呢？offer之后重新搞了leader，然后唤醒了别的线程抢到了leader。</span></span><br><span class="line">                        <span class="keyword">if</span> (leader == thisThread)</span><br><span class="line">                            leader = <span class="literal">null</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (leader == <span class="literal">null</span> &amp;&amp; q.peek() != <span class="literal">null</span>)</span><br><span class="line">            <span class="comment">// 如果没有leader，队列不为空的情况下，需要找个线程来当leader</span></span><br><span class="line">            available.signal();</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="references"><a href="#references" class="headerlink" title="references"></a>references</h2><ul><li><a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/DelayQueue.html">oracle-docs: Class DelayQueue<E extends Delayed></a></li><li><a href="https://github.com/Himansu-Nayak/java7-sourcecode/blob/master/java/util/concurrent/DelayQueue.java">source code: DelayQueue</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="source" scheme="https://blog.pwxcoo.com/categories/source/"/>
    
    
      <category term="java" scheme="https://blog.pwxcoo.com/tags/java/"/>
    
      <category term="concurrent" scheme="https://blog.pwxcoo.com/tags/concurrent/"/>
    
      <category term="source_code" scheme="https://blog.pwxcoo.com/tags/source-code/"/>
    
  </entry>
  
  <entry>
    <title>IO Multiplexing: The select and poll Function</title>
    <link href="https://blog.pwxcoo.com/2019/06/10/IO-Multiplexing-The-select-and-poll-Function/"/>
    <id>https://blog.pwxcoo.com/2019/06/10/IO-Multiplexing-The-select-and-poll-Function/</id>
    <published>2019-06-10T09:16:12.000Z</published>
    <updated>2022-03-24T05:48:48.869Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>When the TCP client is handling two inputs at the same time: standard input and a TCP socket, we encounter a problem when the client was blocked in a call to <code>fgets</code> (on standard input) and the server process was killed. The server TCP correctly sent a FIN to the client TCP, but since the client process was blocked reading from standard input, it never saw the EOF until it read from the socket (possibly much later).</p><p>We want to be notified if one or more IO condition are ready (i.e., input is ready to be read, or the descriptor is capable of taking more output). The capability is called <strong>IO multiplexing</strong> and is provided by the <code>select</code> and <code>poll</code> functions, as well as a newer POSIX variation of the former, called <code>pselect</code>.</p><p>IO multiplexing is typically used in networking application in many scenarios. And IO multiplexing is not limited to networking programming, many nontrivial applications find a need for these techniques.</p><h2 id="IO-Models"><a href="#IO-Models" class="headerlink" title="IO Models"></a>IO Models</h2><p>We firstly examine the basic differences in the five IO models that are available to us under Unix:</p><ul><li>blocking IO</li><li>nonblocking IO</li><li>IO multiplexing (select and poll)</li><li>signal driven IO</li><li>asynchronous IO (the POSIX aio_ functions)</li></ul><p>There are normally two distinct phases for an input operations:</p><ol><li>Waiting for data to be ready. This involves waiting for data to arrive on the network. When the packet arrives, it is copied into a buffer within the kernel.</li><li>Copying the data from the kernel to the process. This means copying the (ready) data from the kernel’s buffer into our application buffer.</li></ol><h3 id="Blocking-IO-Model"><a href="#Blocking-IO-Model" class="headerlink" title="Blocking IO Model"></a>Blocking IO Model</h3><p>The most prevalent model for IO is the blocking IO model. By default, all socket are blocking. The scenario is shown in the figure below:</p><p><img src="/image/blocking IO.gif" alt="blocking IO.gif"></p><p>We use UDP for this example instead of TCP because with UDP, the concept of data being “ready” to read is simple: either an entire datagram has been received or it has not. With TCP it gets more complicated, as additional variables such as the socket’s low-water mark come into play.</p><p>We also refer to <code>recvfrom</code> as a system call to differentiating between our application and the kernel, regardless of how <code>recvfrom</code> is implemented (system call on BSD and function that invokes <code>getmsg</code> system call on System V). There is normally a switch from running in application to running in the kernel, followed at some time later by a return to the application.</p><p>In the figure above, the process calls <code>recvfrom</code> and the system call does not return until the datagram arrives and is copied into our application buffer, or an error occurs. The most common error is the system call being interrupted by signal. We say that the process is blocked the entire time from when it calls <code>recvfrom</code> until it returns. When <code>recvfrom</code> return successfully, our application process the datagram.</p><h3 id="Nonblocking-IO-Model"><a href="#Nonblocking-IO-Model" class="headerlink" title="Nonblocking IO Model"></a>Nonblocking IO Model</h3><p>When a socket is set to be nonblocking, we are telling the kernel “When an IO operation that I request cannot be completed without putting the process to sleep, do not put the process to sleep, but return an error instead”. The figure is below:</p><p><img src="/image/nonblocking-io.gif" alt="nonblocking-io.gif"></p><ul><li>For the first three <code>recvfrom</code>, there is no data to return and the kernel immediately returns an error of <code>EWOULDBLOCK</code>.</li><li>For the forth time we call <code>recvfrom</code>, a datagram is ready, it is copied into our application buffer, and <code>recvfrom</code> returns successfully. We then process the data.</li></ul><p>When an application sits in a loop calling <code>recvfrom</code> on a nonblocking descriptor like this, it is called <strong>polling</strong>. The application is continually polling the kernel to see if some operation is ready. This is often a waste of CPU time, but this model is occasionally encountered, normally on systems dedicated to one function.</p><h3 id="IO-Multiplexing-Model"><a href="#IO-Multiplexing-Model" class="headerlink" title="IO Multiplexing Model"></a>IO Multiplexing Model</h3><p>With <strong>IO multiplexing</strong>, we call <code>select</code> or <code>poll</code> and block in one of these two system calls, instead of blocking in the actual IO system call. The figure is a summary of the IO multiplexing model:</p><p><img src="/image/io-multiplexing.gif" alt="io-multiplexing.gif"></p><p>We block in a call to <code>select</code>, waiting for the datagram socket to be readable. When <code>select</code> returns that the socket is readable, we then call <code>recvfrom</code> to copy the datagram into our application buffer.</p><p>Comparing blocking model:</p><ul><li>Disadvantage: using <code>select</code> requires two system call (<code>select</code> and <code>recvfrom</code>) instead of one</li><li>Advantage: we can wait for more than one descriptor to be ready</li></ul><p>Another closely related IO model is to use multithreading with blocking IO. That model very closely resembles the model described above, except that instead of using <code>select</code> to block on multiple file descriptors, the program uses multiple threads (one per file descriptor), and each thread is then free to call blocking system calls like <code>recvfrom</code>.</p><h3 id="Signal-Driven-IO-Model"><a href="#Signal-Driven-IO-Model" class="headerlink" title="Signal-Driven IO Model"></a>Signal-Driven IO Model</h3><p>The <strong>signal-driven IO model</strong> uses signals, telling the kernel to notify us with the <code>SIGIO</code> signal when descriptor is ready. The figure is below:</p><p><img src="/image/signal-driven-io.gif" alt="signal-driven-io.gif"></p><ul><li>We first enable the socket for signal-driven IO and install a signal handler using the <code>sigaction</code> system call. The return from this system is immediate and our process continues; it is not blocked.</li><li>When the datagram is ready to be read, the <code>SIGIO</code> signal is generated for our process. We can either:<ul><li>read the datagram from the signal handler by calling <code>recvfrom</code> and then notify the main loop that data is ready to be process.</li><li>notify the main loop and let it read the datagram.</li></ul></li></ul><p>The advantage to this model is that we are not blocked while waiting for the datagram to arrive. The main loop can continue executing and just wait to be notified by the signal handler that either the data is ready to process or the datagram is ready to be read.</p><h3 id="Asynchronous-IO-model"><a href="#Asynchronous-IO-model" class="headerlink" title="Asynchronous IO model"></a>Asynchronous IO model</h3><p><strong>Asynchronous IO</strong> is defined by the POSIX specification, and various differences in the read-time function that appeared in the various standards which came together to form the current POSIX specification have been reconciled.</p><p>These functions work by telling the kernel to start the operation and to notify us when then entire operation (including the copy of the data from the kernel to our buffer) is complete. <strong>The main difference between this model and the signal-driven IO model is that with signal-driven IO, the kernel tells us when IO operation can be initiated, but asynchronous IO, the kernel tells us when an IO operation is complete.</strong> See the figure below for example:</p><p><img src="/image/asynchronous-io.gif" alt="asynchronous-io.gif"></p><ul><li>We call <code>aio_read</code> (the POSIX asynchronous IO function begin with <code>aio_</code> or <code>lio_</code>, <strong>This system call returns immediately and our process is not blocked while waiting for the IO to complete</strong>) and pass the kernel the following:<ul><li>descriptor, buffer pointer, buffer size (the same three arguments for <code>read</code>)</li><li>file offset (similar to <code>lseek</code>)</li><li>and how to notify us when the entire operation is complete<blockquote></blockquote></li></ul></li><li>We assume in this example that we ask the kernel to generate some signal when the operation is complete. This signal is not generated until the data has been copied into our application buffer, which is different from the signal-driven IO model.</li></ul><h2 id="Comparison-of-the-IO-models"><a href="#Comparison-of-the-IO-models" class="headerlink" title="Comparison of the IO models"></a>Comparison of the IO models</h2><p>The figure below is a comparison of the five different IO models:</p><p><img src="/image/comparison-io.gif" alt="comparison-io.gif"></p><p>The main difference between the first four models is the first phase, as the second phase in the first four models is the same: the process is blocked in a call to <code>recvfrom</code> while the data is copied from the kernel to the caller’s buffer. Asynchronous IO, however, handles both phases and is different from the first four.</p><h2 id="Synchronous-IO-versus-Asynchronous-IO"><a href="#Synchronous-IO-versus-Asynchronous-IO" class="headerlink" title="Synchronous IO versus Asynchronous IO"></a>Synchronous IO versus Asynchronous IO</h2><p>POSIX defines these two terms as follows:</p><ul><li>A synchronous IO operation causes the requesting process to be blocked until that IO operation completes.</li><li>An asynchronous IO operation does not cause the requesting process to be blocked.</li></ul><p>Using these definitions, the first four IO models (blocking, nonblocking, IO complexing and signal-driven IO) are all synchronous because the actual IO operation (<code>recvfrom</code>) blocks the process. Only the asynchronous IO model matches the asynchronous IO definition.</p><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://notes.shichao.io/unp/ch6/">Chapter 6. I/O Multiplexing: The select and poll Functions</a></li><li><a href="http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch06lev1sec2.html">UNP - 6.2 I/O Models</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="base" scheme="https://blog.pwxcoo.com/categories/base/"/>
    
    
      <category term="unp" scheme="https://blog.pwxcoo.com/tags/unp/"/>
    
      <category term="io" scheme="https://blog.pwxcoo.com/tags/io/"/>
    
  </entry>
  
  <entry>
    <title>【Advanced Java】缓存</title>
    <link href="https://blog.pwxcoo.com/2019/06/03/%E3%80%90Advanced-Java%E3%80%91%E7%BC%93%E5%AD%98/"/>
    <id>https://blog.pwxcoo.com/2019/06/03/【Advanced-Java】缓存/</id>
    <published>2019-06-03T11:31:46.000Z</published>
    <updated>2022-03-24T05:48:48.874Z</updated>
    
    <content type="html"><![CDATA[<h2 id="缓存优缺点"><a href="#缓存优缺点" class="headerlink" title="缓存优缺点"></a>缓存优缺点</h2><p>优点：</p><ol><li>高性能</li><li>高并发</li></ol><p>缺点，因为缓存可能出现的问题：</p><ol><li>缓存和数据库双写不一致</li><li>缓存雪崩，缓存穿透</li><li>缓存并发竞争</li></ol><h2 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h2><p>redis 内部使用的是文件事件处理器（file event handler），这个文件时间处理器是单线程的，所以说 redis 叫单线程模型。</p><h3 id="Redis为什么单线程效率也这么高："><a href="#Redis为什么单线程效率也这么高：" class="headerlink" title="Redis为什么单线程效率也这么高："></a>Redis为什么单线程效率也这么高：</h3><ol><li>纯内存操作</li><li>核心是基于非阻塞的 IO 多路复用</li><li>单线程避免了一些场景下频繁上下文切换的问题，预防了多线程可能产生的竞争问题</li></ol><h3 id="Redis过期策略"><a href="#Redis过期策略" class="headerlink" title="Redis过期策略"></a>Redis过期策略</h3><p>定期删除（部分） + 惰性删除</p><h3 id="Redis内存淘汰机制"><a href="#Redis内存淘汰机制" class="headerlink" title="Redis内存淘汰机制"></a>Redis内存淘汰机制</h3><ul><li>noeviction</li><li><strong>allkeys-lru</strong></li><li>allkeys-random</li><li>volatile-lru (volatile 表示从设置了过期时间的建空间里操作)</li><li>volatile-random</li><li>volatile-ttl</li></ul><h3 id="Redis实现高并发"><a href="#Redis实现高并发" class="headerlink" title="Redis实现高并发"></a>Redis实现高并发</h3><p>主从架构</p><h3 id="Redis实现高可用"><a href="#Redis实现高可用" class="headerlink" title="Redis实现高可用"></a>Redis实现高可用</h3><p>哨兵，实现主备切换</p><h3 id="Redis实现持久化"><a href="#Redis实现持久化" class="headerlink" title="Redis实现持久化"></a>Redis实现持久化</h3><ul><li>RDB：周期性持久化</li><li>AOF：对每条写入命令作为日志，以 append-only 的模式写入一个日志文件中</li></ul><p>如果同时使用 RDB 和 AOF 两种持久化机制，那么在 redis 重启的时候，会使用 AOF 来重新构建数据，因为 AOF 中的数据更加完整。</p><h3 id="Redis集群"><a href="#Redis集群" class="headerlink" title="Redis集群"></a>Redis集群</h3><ul><li>redis cluster 可以自动将数据切片，每个 master 上放一部分数据，节点之间通信用 gossip 协议（每个节点持有一份元数据）</li><li>分布式寻址（一致性哈希算法）</li><li>主从切换<ul><li>判断节点宕机（超过半数）</li><li>过滤 slave 节点（和 master 节点断开时间超过了某时间，不配变成 master）</li><li>从节点选举（超过半数）</li></ul></li></ul><h3 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h3><p>事前：redis 高可用，主从+哨兵，redis cluster，避免全盘崩溃。<br>事中：本地 ehcache 缓存 + hystrix 限流&amp;降级，避免 MySQL 被打死。<br>事后：redis 持久化，一旦重启，自动从磁盘上加载数据，快速恢复缓存数据。</p><h3 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h3><p>非正常请求也存一个空值到缓存里，并设置过期时间。</p><h3 id="缓存击穿"><a href="#缓存击穿" class="headerlink" title="缓存击穿"></a>缓存击穿</h3><p>热点数据突然失效。</p><ol><li>热点不过期</li><li>redis 或 zookeeper 实现互斥锁，等待第一个请求建完缓存后在释放锁</li></ol><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="https://doocs.github.io/advanced-java/#/">Java 进阶扫盲</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="base" scheme="https://blog.pwxcoo.com/categories/base/"/>
    
    
      <category term="advanced-java" scheme="https://blog.pwxcoo.com/tags/advanced-java/"/>
    
      <category term="cache" scheme="https://blog.pwxcoo.com/tags/cache/"/>
    
  </entry>
  
  <entry>
    <title>【Advanced Java】搜索引擎</title>
    <link href="https://blog.pwxcoo.com/2019/05/30/%E3%80%90Advanced-Java%E3%80%91%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/"/>
    <id>https://blog.pwxcoo.com/2019/05/30/【Advanced-Java】搜索引擎/</id>
    <published>2019-05-30T06:55:39.000Z</published>
    <updated>2022-03-24T05:48:48.874Z</updated>
    
    <content type="html"><![CDATA[<h2 id="ES简介"><a href="#ES简介" class="headerlink" title="ES简介"></a>ES简介</h2><p>elasticsearch 是基于 lucene /luːsɪŋ/ 的搜索库，<strong>分布式</strong>的<strong>文档存储</strong>，<strong>搜索</strong>，<strong>分析</strong>，支持 PB 级数据。</p><p>下面是 ES 里的一些概念</p><ul><li>Near Real Time，近实时<ul><li>写入数据到数据可以被搜索有一个小延迟（大概是 1s）</li><li>基于 es 执行搜索和分析可以达到秒级</li></ul></li><li>Cluster 集群</li><li>Node 节点</li><li>Document &amp; field<ul><li>document 是一条 record</li><li>field</li></ul></li><li>Index 索引，一个索引包含很多 document</li><li>Type，一个索引里有一个或者多个 type</li><li>shard，把一个索引中的数据切分成多个 shard</li><li>replica，shard 分为 primary shard 和 replica shard</li></ul><h2 id="ES-如何实现分布式"><a href="#ES-如何实现分布式" class="headerlink" title="ES 如何实现分布式"></a>ES 如何实现分布式</h2><p>ES shard 来实现分布式。</p><ul><li>支持横向扩展</li><li>提高性能</li></ul><h2 id="ES-工作原理"><a href="#ES-工作原理" class="headerlink" title="ES 工作原理"></a>ES 工作原理</h2><h3 id="写数据"><a href="#写数据" class="headerlink" title="写数据"></a>写数据</h3><ul><li>client 发送请求到 coordinating node</li><li>coordinating node 对 document 路由，转发给对应的 primary shard</li><li>primary 同步到 replica node，完成后响应结果</li></ul><h3 id="读数据"><a href="#读数据" class="headerlink" title="读数据"></a>读数据</h3><ul><li>client 发送请求到 coordinating node</li><li>client 路由，负载均衡到相应的 node（随机轮询算法）</li><li>node 返回到 coordinating node，完成后响应结果</li></ul><h3 id="es搜索"><a href="#es搜索" class="headerlink" title="es搜索"></a>es搜索</h3><ul><li>client 发送到 coordinating node</li><li>coordinating node 转发到<strong>所有</strong>的 shard（primary 或 replica）</li><li>query phase：shard 返回 doc id 给 coordinating node，coordinating node 对结果合并，排序，分页等操作，产出最终结果</li><li>fetch phase：由 coordinating node 根据 doc id 来拉取实际的 document 数据</li></ul><h3 id="es写数据底层原理"><a href="#es写数据底层原理" class="headerlink" title="es写数据底层原理"></a>es写数据底层原理</h3><p><img src="/image/es-write-detail.png" alt="es-write-detail.png"></p><p>因此是 1s 的准实时，5s 的数据丢失。</p><h3 id="es删除-更新数据底层原理"><a href="#es删除-更新数据底层原理" class="headerlink" title="es删除/更新数据底层原理"></a>es删除/更新数据底层原理</h3><p>如果是删除操作，commit 的时候会生成一个 .del 文件，里面将某个 doc 标识为 deleted 状态，那么搜索的时候根据 .del 文件就知道这个 doc 是否被删除了。</p><p>如果是更新操作，就是将原来的 doc 标识为 deleted 状态，然后新写入一条数据。</p><p>buffer 每 refresh 一次，就会产生一个 segment file，所以默认情况下是 1 秒钟一个 segment file，这样下来 segment file 会越来越多，此时会定期执行 merge。每次 merge 的时候，会将多个 segment file 合并成一个，同时这里会将标识为 deleted 的 doc 给物理删除掉，然后将新的 segment file 写入磁盘，这里会写一个 commit point，标识所有新的 segment file，然后打开 segment file 供搜索使用，同时删除旧的 segment file。</p><h3 id="ES-优化"><a href="#ES-优化" class="headerlink" title="ES 优化"></a>ES 优化</h3><ol><li>filesystem cache<ul><li>加内存</li><li>document 太大，可以 es + hbase</li></ul></li><li>数据预热</li><li>冷热分离，不要让冷数据冲刷掉热数据</li><li>不用分页，向微博一样用 scroll</li></ol><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://doocs.github.io/advanced-java/#/">Java 进阶扫盲</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="base" scheme="https://blog.pwxcoo.com/categories/base/"/>
    
    
      <category term="advanced-java" scheme="https://blog.pwxcoo.com/tags/advanced-java/"/>
    
      <category term="es" scheme="https://blog.pwxcoo.com/tags/es/"/>
    
  </entry>
  
  <entry>
    <title>【Advanced Java】消息队列</title>
    <link href="https://blog.pwxcoo.com/2019/03/20/%E3%80%90Advanced%20Java%E3%80%91%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"/>
    <id>https://blog.pwxcoo.com/2019/03/20/【Advanced Java】消息队列/</id>
    <published>2019-03-20T02:26:16.000Z</published>
    <updated>2022-03-24T05:48:48.874Z</updated>
    
    <content type="html"><![CDATA[<h2 id="为什么要用消息队列"><a href="#为什么要用消息队列" class="headerlink" title="为什么要用消息队列"></a>为什么要用消息队列</h2><p>优点：</p><ul><li>解耦，producer 无需关心 consumer</li><li>异步，consumer 异步写库</li><li>削峰，高峰期</li></ul><p>缺点：</p><ul><li>可用性降低</li><li>复杂度提高。重复消费，消息丢失，消息顺序性</li><li>一致性</li></ul><table><thead><tr><th style="text-align:center"><strong>特性</strong></th><th style="text-align:center"><strong>ActiveMQ</strong></th><th style="text-align:center"><strong>RabbitMQ</strong></th><th style="text-align:center"><strong>RocketMQ</strong></th><th style="text-align:center"><strong>Kafka</strong></th></tr></thead><tbody><tr><td style="text-align:center">单机吞吐量</td><td style="text-align:center">万级，比 RocketMQ、Kafka 低一个数量级</td><td style="text-align:center">同 ActiveMQ</td><td style="text-align:center">10 万级，支撑高吞吐</td><td style="text-align:center">10 万级，高吞吐，一般配合大数据类的系统来进行实时数据计算、日志采集等场景</td></tr><tr><td style="text-align:center">topic 数量对吞吐量的影响</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center">topic 可以达到几百/几千的级别，吞吐量会有较小幅度的下降，这是 RocketMQ 的一大优势，在同等机器下，可以支撑大量的 topic</td><td style="text-align:center">topic 从几十到几百个时候，吞吐量会大幅度下降，在同等机器下，Kafka 尽量保证 topic 数量不要过多，如果要支撑大规模的 topic，需要增加更多的机器资源</td></tr><tr><td style="text-align:center">时效性</td><td style="text-align:center">ms 级</td><td style="text-align:center">微秒级，这是 RabbitMQ 的一大特点，延迟最低</td><td style="text-align:center">ms 级</td><td style="text-align:center">延迟在 ms 级以内</td></tr><tr><td style="text-align:center">可用性</td><td style="text-align:center">高，基于主从架构实现高可用</td><td style="text-align:center">同 ActiveMQ</td><td style="text-align:center">非常高，分布式架构</td><td style="text-align:center">非常高，分布式，一个数据多个副本，少数机器宕机，不会丢失数据，不会导致不可用</td></tr><tr><td style="text-align:center">消息可靠性</td><td style="text-align:center">有较低的概率丢失数据</td><td style="text-align:center">基本不丢</td><td style="text-align:center">经过参数优化配置，可以做到 0 丢失</td><td style="text-align:center">同 RocketMQ</td></tr><tr><td style="text-align:center">功能支持</td><td style="text-align:center">MQ 领域的功能极其完备</td><td style="text-align:center">基于 erlang 开发，并发能力很强，性能极好，延时很低</td><td style="text-align:center">MQ 功能较为完善，还是分布式的，扩展性好</td><td style="text-align:center">功能较为简单，主要支持简单的 MQ 功能，在大数据领域的实时计算以及日志采集被大规模使用</td></tr></tbody></table><h2 id="高可用"><a href="#高可用" class="headerlink" title="高可用"></a>高可用</h2><p>HA, HighAvailability</p><h3 id="RabbitMQ"><a href="#RabbitMQ" class="headerlink" title="RabbitMQ"></a>RabbitMQ</h3><ul><li>单机模式</li><li>普通集群模式(无高可用)。数据在一台实例上，其他的同步 queue 元信息(用元信息找到存放数据的实例)</li><li>镜像集群模式</li></ul><h3 id="Kafka"><a href="#Kafka" class="headerlink" title="Kafka"></a>Kafka</h3><p><img src="/image/kafka-arch.png" alt="kafka-arch.png"></p><p><strong>写数据</strong>的时候，生产者就写 leader，然后 leader 将数据落地写本地磁盘，接着其他 follower 自己主动从 leader 来 pull 数据。一旦所有 follower 同步好数据了，就会发送 ack 给 leader，leader 收到所有 follower 的 ack 之后，就会返回写成功的消息给生产者。（当然，这只是其中一种模式，还可以适当调整这个行为）</p><p><strong>消费</strong>的时候，只会从 leader 去读，但是只有当一个消息已经被所有 follower 都同步成功返回 ack 的时候，这个消息才会被消费者读到。</p><h2 id="重复消费"><a href="#重复消费" class="headerlink" title="重复消费"></a>重复消费</h2><p>通常由下游保证。</p><h2 id="可靠消息"><a href="#可靠消息" class="headerlink" title="可靠消息"></a>可靠消息</h2><ul><li>生产者丢了 =&gt; MQ ack</li><li>MQ 丢了 =&gt; MQ ack until persistence completion</li><li>consumer 丢了 =&gt; consumer ack</li></ul><h2 id="消费积压"><a href="#消费积压" class="headerlink" title="消费积压"></a>消费积压</h2><ul><li>紧急扩容，加速消费</li><li>大量积压快过期了，可以先批量重导</li></ul><h2 id="设计一个-MQ"><a href="#设计一个-MQ" class="headerlink" title="设计一个 MQ"></a>设计一个 MQ</h2><ul><li>可伸缩性，broker-&gt;topic-&gt;partition</li><li>可靠性，持久化到磁盘，顺序写</li><li>可用性，leader-&gt;follower</li></ul><h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul><li><a href="https://doocs.github.io/advanced-java/#/">Java 进阶扫盲</a></li><li><a href="https://www.cnblogs.com/linkenpark/p/5393666.html">RabbitMQ 中 exchange、route、queue 的关系</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="base" scheme="https://blog.pwxcoo.com/categories/base/"/>
    
    
      <category term="mq" scheme="https://blog.pwxcoo.com/tags/mq/"/>
    
      <category term="advanced-java" scheme="https://blog.pwxcoo.com/tags/advanced-java/"/>
    
  </entry>
  
  <entry>
    <title>【编程笔记】0</title>
    <link href="https://blog.pwxcoo.com/2019/02/18/%E3%80%90%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0%E3%80%910/"/>
    <id>https://blog.pwxcoo.com/2019/02/18/【编程笔记】0/</id>
    <published>2019-02-18T14:43:12.000Z</published>
    <updated>2022-03-24T05:48:48.875Z</updated>
    
    <content type="html"><![CDATA[<h2 id="文件打开模式"><a href="#文件打开模式" class="headerlink" title="文件打开模式"></a>文件打开模式</h2><p>文件打开模式的选项在每个编程语言里都有（因为其实基本都只是 wrap 了 C 的 IO 模块，所以大同小异）。</p><p><img src="/image/file_mode.png" alt="file_mode.png"></p><p><strong>source: <a href="https://stackoverflow.com/questions/1466000/python-open-built-in-function-difference-between-modes-a-a-w-w-and-r">open-built-in-function-difference-between-modes-a-a-w-w-and-r?</a></strong></p><h2 id="remove-trailing-spaces"><a href="#remove-trailing-spaces" class="headerlink" title="remove trailing spaces"></a>remove trailing spaces</h2><p>移除行尾后的空格，不然有时候会污染 git 的 diff。</p><p>在 VSCode 里把 <code>&quot;files.trimTrailingWhitespace&quot;: true</code> 就好了。</p><p><strong>source: <a href="https://stackoverflow.com/questions/30884131/remove-trailing-spaces-automatically-or-with-a-shortcut">Remove trailing spaces automatically or with a shortcut</a></strong></p><h2 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h2><p>因为这几天正则搞得比较多。主要是 <code>?</code> 的 case 的问题之前不太清晰。还寻思着过段时间把上次挖坑的 regular expression engine 写掉的。</p><ul><li><code>^</code>, start</li><li><code>$</code>, end</li><li><code>*</code>, {0, }</li><li><code>+</code>, {1, }</li><li><code>?</code>, {0, 1}</li><li><code>.</code>, any character except newline</li><li><code>(?:x)</code>, non-capture</li><li><code>x(?=y)</code>, match x only if suffix with y</li><li><code>x(?=y)</code>, match x only if not suffix with y</li><li><code>x|y</code>, or</li><li><code>&#123;&#125;</code>, limit times</li><li><code>\d</code>, [0-9]</li><li><code>\D</code>, [^0-9]</li><li><code>\s</code>, space</li><li><code>\S</code>, non space</li><li><code>\w</code>, [A-Za-z0-9_]</li><li><code>\W</code>, [^A-Za-z0-9_]</li></ul><p><strong>source: <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions">正则表达式 - MDN</a></strong></p><h2 id="带宽（bandwidth）"><a href="#带宽（bandwidth）" class="headerlink" title="带宽（bandwidth）"></a>带宽（bandwidth）</h2><p>1Mbps = 1M bit per second = 1024 bit per second = 128 kb/s</p><h2 id="二项式定理（binomial-theorem）"><a href="#二项式定理（binomial-theorem）" class="headerlink" title="二项式定理（binomial theorem）"></a>二项式定理（binomial theorem）</h2><p>$$<br>(x + y)^{n} = \sum_{k=0}^{n} {n \choose k} x^{n-k} y^{k}<br>$$</p><h2 id="操作系统时间线"><a href="#操作系统时间线" class="headerlink" title="操作系统时间线"></a>操作系统时间线</h2><p><img src="/image/os-timeline.svg" alt="os-timeline.svg"></p><h2 id="0x3f3f3f3f"><a href="#0x3f3f3f3f" class="headerlink" title="0x3f3f3f3f"></a>0x3f3f3f3f</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> INF = <span class="number">0x3f3f3f3f</span>;</span><br></pre></td></tr></table></figure><p>在数据范围不超过 $10^{9}$ 的情况下，可以作为无穷大使用，有两个好处：</p><ol><li>INF + INF 仍然是正数</li><li>可以在 memset 中使用 <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">memset</span>(a, <span class="number">0x3f</span>, <span class="built_in">sizeof</span>(a))</span><br></pre></td></tr></table></figure></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="memo" scheme="https://blog.pwxcoo.com/tags/memo/"/>
    
  </entry>
  
  <entry>
    <title>《阿里巴巴Java开发手册》注意点</title>
    <link href="https://blog.pwxcoo.com/2019/01/23/%E3%80%8A%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%E3%80%8B%E6%B3%A8%E6%84%8F%E7%82%B9/"/>
    <id>https://blog.pwxcoo.com/2019/01/23/《阿里巴巴Java开发手册》注意点/</id>
    <published>2019-01-23T08:21:10.000Z</published>
    <updated>2022-03-24T05:48:48.873Z</updated>
    
    <content type="html"><![CDATA[<p>大概把这本书里之前没注意到的整理了一下。</p><h2 id="prerequisites"><a href="#prerequisites" class="headerlink" title="prerequisites"></a>prerequisites</h2><ul><li>POJO: plain ordinary java object<ul><li>PO: persistent object</li><li>BO: business object</li><li>VO: value/view object</li><li>DTO: data Transfer object </li><li>DAO: data access object</li></ul></li></ul><h2 id="编程规约"><a href="#编程规约" class="headerlink" title="编程规约"></a>编程规约</h2><h3 id="（一）命名风格"><a href="#（一）命名风格" class="headerlink" title="（一）命名风格"></a>（一）命名风格</h3><ul><li>抽象类命名使用 Abstract 或 Base 开头 ;</li><li>POJO 类中布尔类型的变量，都不要加 is 前缀 ，否则部分框架解析会引起序列化错误。</li><li>枚举类名建议带上 Enum 后缀</li></ul><h3 id="（二）常量定义"><a href="#（二）常量定义" class="headerlink" title="（二）常量定义"></a>（二）常量定义</h3><ul><li>在 long 或者 Long 赋值时，数值后使用大写的 L ，不能是小写的 l ，小写容易跟数字 1 混淆，造成误解。</li></ul><h3 id="（三）代码格式"><a href="#（三）代码格式" class="headerlink" title="（三）代码格式"></a>（三）代码格式</h3><ul><li>采用 4 个空格缩进，禁止使用 tab 字符。</li></ul><h3 id="（四）OOP-规约"><a href="#（四）OOP-规约" class="headerlink" title="（四）OOP 规约"></a>（四）OOP 规约</h3><ul><li>Object 的 equals 方法容易抛空指针异常，应使用常量或确定有值的对象来调用 equals 。推荐使用 java . util . Objects # equals(JDK 7 引入的工具类 )</li><li>关于基本数据类型与包装数据类型的使用标准如下: <ol><li>【强制】所有的 POJO 类属性必须使用包装数据类型。</li><li>【强制】 RPC 方法的返回值和参数必须使用包装数据类型。</li><li>【推荐】所有的局部变量使用基本数据类型。</li></ol></li><li>禁止在 POJO 类中，同时存在对应属性 xxx 的 isXxx() 和 getXxx() 方法。框架在调用属性 xxx 的提取方法时，并不能确定哪个方法一定是被优先调用到。</li><li>使用索引访问用 String 的 split 方法得到的数组时，需做最后一个分隔符后有无内容的检查，否则会有抛 IndexOutOfBoundsException 的风险。（源码的实现会舍去末尾的空字符）  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Construct result</span></span><br><span class="line"><span class="type">int</span> <span class="variable">resultSize</span> <span class="operator">=</span> list.size();</span><br><span class="line"><span class="keyword">if</span> (limit == <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">while</span> (resultSize &gt; <span class="number">0</span> &amp;&amp; list.get(resultSize - <span class="number">1</span>).length() == <span class="number">0</span>) &#123;</span><br><span class="line">        resultSize--;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">String[] result = <span class="keyword">new</span> <span class="title class_">String</span>[resultSize];</span><br><span class="line"><span class="keyword">return</span> list.subList(<span class="number">0</span>， resultSize).toArray(result);</span><br></pre></td></tr></table></figure></li><li>循环体内，字符串的连接方式，使用 StringBuilder 的 append 方法进行扩展。</li></ul><h3 id="（五）集合处理"><a href="#（五）集合处理" class="headerlink" title="（五）集合处理"></a>（五）集合处理</h3><ul><li>ArrayList 的 subList 结果不可强转成 ArrayList 。subList 返回的是 ArrayList 的内部类 SubList ，并不是 ArrayList 而是 ArrayList<br>的一个视图，对于 SubList 子列表的所有操作最终会反映到原列表上。</li><li>使用集合转数组的方法，必须使用集合的 toArray(T[] array) ，传入的是类型完全一样的数组，大小就是 <code>list.size()</code> 。直接使用 toArray 无参方法存在问题，此方法返回值只能是 Object[] 类，若强转其它类型数组将出现 ClassCastException 错误。</li><li>使用工具类 Arrays . asList() 把数组转换成集合时，不能使用其修改集合相关的方法。sList 的返回对象是一个 Arrays 内部类，并没有实现集合的修改方法。 Arrays . asList 体现的是适配器模式，只是转换接口，后台的数据仍是数组。</li><li>泛型通配符&lt;? extends T &gt;来接收返回的数据，此写法的泛型集合不能使用 add 方法，而 &lt;? super T&gt; 不能使用 get 方法，作为接口调用赋值时易出错。扩展说一下 PECS(Producer Extends Consumer Super) 原则:第一、频繁往外读取内容的，适合用&lt;? extends T &gt;。第二、经常往里插入的，适合用 &lt;? super T&gt; 。</li><li>集合初始化时,指定集合初始值大小。</li><li>使用 entrySet 遍历 Map 类集合 KV ,而不是 keySet 方式进行遍历。</li><li>Map 类对 null 值的处理。</li></ul><blockquote><table><thead><tr><th>集合类</th><th>Key</th><th>Value</th><th>Super</th><th>说明</th></tr></thead><tbody><tr><td>Hashtable</td><td>no</td><td>no</td><td>Dictionary</td><td>thread safe</td></tr><tr><td>ConcurrentHashMap</td><td>no</td><td>no</td><td>AbstractMap</td><td>锁分段（JDK8: CAS）</td></tr><tr><td>TreeMap</td><td>no</td><td>yes</td><td>AbstractMap</td><td>thread unsafe</td></tr><tr><td>HashMap</td><td>yes</td><td>yes</td><td>AbstractMap</td><td>thread unsafe</td></tr></tbody></table></blockquote><h3 id="（六）-并发处理"><a href="#（六）-并发处理" class="headerlink" title="（六） 并发处理"></a>（六） 并发处理</h3><ul><li>【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。</li><li>【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。<ol><li>FixedThreadPool 和 SingleThreadPool :<br>允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。</li><li>CachedThreadPool 和 ScheduledThreadPool :<br>允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。</li></ol></li><li>并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。</li><li>多线程并行处理定时任务时, Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。</li><li>使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至 await 方法,直到超时才返回结果。</li><li>HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升, 在开发过程中可以使用其它数据结构或加锁来规避此风险。</li><li>ThreadLocal 无法解决共享对象的更新问题, ThreadLocal 对象建议使用 static修饰。这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象 ( 只要是这个线程内定义的 ) 都可以操控这个变量。</li></ul><h3 id="（七）控制语句"><a href="#（七）控制语句" class="headerlink" title="（七）控制语句"></a>（七）控制语句</h3><ul><li>【参考】下列情形,需要进行参数校验:<ol><li>调用频次低的方法。</li><li>执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参<br>数错误导致中间执行回退,或者错误,那得不偿失。</li><li>需要极高稳定性和可用性的方法。</li><li>对外提供的开放接口,不管是 RPC / API / HTTP 接口。</li><li>敏感权限入口。</li></ol></li><li>【参考】下列情形,不需要进行参数校验:<ol><li>极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。</li><li>底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底<br>层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一台服务器中,所<br>以 DAO 的参数校验,可以省略。</li><li>被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参<br>数已经做过检查或者肯定不会有问题,此时可以不校验参数。</li></ol></li></ul><h3 id="（八）-注释规约"><a href="#（八）-注释规约" class="headerlink" title="（八） 注释规约"></a>（八） 注释规约</h3><h3 id="（九）其它"><a href="#（九）其它" class="headerlink" title="（九）其它"></a>（九）其它</h3><ul><li>【强制】后台输送给页面的变量必须加 $!{var} ——中间的感叹号。</li><li>在 JDK 8 中, 针对统计时间等场景,推荐使用 Instant 类。</li></ul><h2 id="异常日志"><a href="#异常日志" class="headerlink" title="异常日志"></a>异常日志</h2><h3 id="一-异常处理"><a href="#一-异常处理" class="headerlink" title="(一) 异常处理"></a>(一) 异常处理</h3><ul><li>【推荐】防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景:<ol><li>返回类型为基本数据类型, return 包装数据类型的对象时,自动拆箱有可能产生 NPE 。</li><li>数据库的查询结果可能为 null 。</li><li>集合里的元素即使 isNotEmpty ,取出的数据元素也可能为 null 。</li><li>远程调用返回对象时,一律要求进行空指针判断,防止 NPE 。</li><li>对于 Session 中获取的数据,建议 NPE 检查,避免空指针。</li><li>级联调用 <code>obj.getA().getB().getC()</code>; 一连串调用,易产生 NPE 。</li></ol></li></ul><h3 id="二-日志规约"><a href="#二-日志规约" class="headerlink" title="(二) 日志规约"></a>(二) 日志规约</h3><ul><li>应用中不可直接使用日志系统 (Log 4 j 、 Logback) 中的 API ,而应依赖使用日志框架 SLF4J（Simple Logging Facade for Java）中的 API ,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。</li></ul><h2 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h2><h2 id="安全规约"><a href="#安全规约" class="headerlink" title="安全规约"></a>安全规约</h2><ul><li>【强制】表单、 AJAX 提交必须执行 CSRF 安全验证。</li></ul><h2 id="MySQL-数据库"><a href="#MySQL-数据库" class="headerlink" title="MySQL 数据库"></a>MySQL 数据库</h2><h3 id="一-建表规约"><a href="#一-建表规约" class="headerlink" title="(一) 建表规约"></a>(一) 建表规约</h3><ul><li>【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是,0 表示否)。</li><li>【强制】主键索引名为 pk_ 字段名;唯一索引名为 uk _字段名 ; 普通索引名则为 idx _字段名。</li><li>【强制】小数类型为 decimal ,禁止使用 float 和 double 。</li><li>【强制】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。</li><li>【强制】表必备三字段: id , gmt _ create , gmt _ modified 。</li><li>【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:<ol><li>不是频繁修改的字段。</li><li>不是 varchar 超长字段,更不能是 text 字段。</li></ol></li><li>【推荐】单表行数超过 500 万行或者单表容量超过 2 GB ,才推荐进行分库分表。</li></ul><h3 id="二-索引规约"><a href="#二-索引规约" class="headerlink" title="(二) 索引规约"></a>(二) 索引规约</h3><ul><li>【强制】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。</li><li>【强制】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90% 以上,可以使用 count(distinct left( 列名, 索引长度 )) / count( * ) 的区分度来确定。</li><li>【强制】页面搜索严禁左模糊或者全模糊</li><li>【推荐】如果有 order by 的场景,请注意利用索引的有序性。</li><li>【推荐】利用覆盖索引来进行查询操作,避免回表。</li><li>【推荐】利用延迟关联或者子查询优化超多分页场景。</li><li>【推荐】建组合索引的时候,区分度最高的在最左边。</li></ul><h3 id="三-SQL-语句"><a href="#三-SQL-语句" class="headerlink" title="(三) SQL 语句"></a>(三) SQL 语句</h3><ul><li>【强制】不要使用 count( 列名 ) 或 count( 常量 ) 来替代 count( <em> ) , count( </em> ) 是 SQL 92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。count( * ) 会统计值为 NULL 的行,而 count( 列名 ) 不会统计此列为 NULL 值的行。</li><li>【强制】 count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col 1, col 2 ) 如果其中一列全为 NULL ,那么即使另一列有不同的值,也返回为 0。</li><li>【强制】当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为 NULL ,因此使用 sum() 时需注意 NPE 问题。</li><li>【强制】使用 ISNULL() 来判断是否为 NULL 值。NULL 与任何值的直接比较都为 NULL。</li><li>【强制】不得使用外键与级联,一切外键概念必须在应用层解决。</li></ul><h3 id="四-ORM-映射"><a href="#四-ORM-映射" class="headerlink" title="(四) ORM 映射"></a>(四) ORM 映射</h3><ul><li>【强制】 POJO 类的布尔属性不能加 is ,而数据库字段必须加 is _,要求在 resultMap 中进行字段与属性之间的映射。</li><li>【强制】不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义 ; 反过来,每一个表也必然有一个 POJO 类与之对应。</li><li>【强制】 sql. xml 配置参数使用: #{}, # param # 不要使用${} 此种方式容易出现 SQL 注入。</li><li>【参考】&lt; isEqual &gt;中的 compareValue 是与属性值对比的常量,一般是数字,表示相等时带上此条件 ; &lt; isNotEmpty &gt;表示不为空且不为 null 时执行 ; &lt; isNotNull &gt;表示不为 null 值时执行。</li></ul><h2 id="工程结构"><a href="#工程结构" class="headerlink" title="工程结构"></a>工程结构</h2><h3 id="一-应用分层"><a href="#一-应用分层" class="headerlink" title="(一) 应用分层"></a>(一) 应用分层</h3><h3 id="二-二方库依赖"><a href="#二-二方库依赖" class="headerlink" title="(二) 二方库依赖"></a>(二) 二方库依赖</h3><ul><li>【强制】二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。</li></ul><h3 id="三-服务器"><a href="#三-服务器" class="headerlink" title="(三) 服务器"></a>(三) 服务器</h3><ul><li>【推荐】高并发服务器建议调小 TCP 协议的 time _ wait 超时时间。操作系统默认 240 秒。在 linux 服务器上请通过变更/ etc / sysctl . conf 文件去修改该缺省值 ( 秒 ) :  <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net.ipv4.tcp_fin_timeout = 30</span><br></pre></td></tr></table></figure></li><li>【推荐】调大服务器所支持的最大文件句柄数 (File Descriptor ,简写为 fd) 。</li><li>【推荐】给 JVM 环境参数设置- XX :+ HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。</li><li>【推荐】在线上生产环境, JVM 的 Xms（JVM初始分配的堆内存 ） 和 Xmx（JVM最大允许分配的堆内存，按需分配 ） 设置一样大小的内存容量,避免在 GC 后调整堆大小带来的压力。</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="java" scheme="https://blog.pwxcoo.com/tags/java/"/>
    
  </entry>
  
  <entry>
    <title>linux notes</title>
    <link href="https://blog.pwxcoo.com/2018/12/28/linux-notes/"/>
    <id>https://blog.pwxcoo.com/2018/12/28/linux-notes/</id>
    <published>2018-12-28T08:30:58.000Z</published>
    <updated>2022-03-24T05:48:48.871Z</updated>
    
    <content type="html"><![CDATA[<p>a collection of frequently-used linux operations.</p><h2 id="ssh-service"><a href="#ssh-service" class="headerlink" title="ssh service"></a>ssh service</h2><ul><li><code>useradd -m [username]</code>, <code>-m</code> will automatically create new user folder in <code>/home</code></li><li><code>passwd [username]</code>, configure password</li><li><code>usermod -s /bin/bash [username]</code>, change default login shell<ul><li><code>cat /etc/passwd</code> list all users</li><li><code>cat /etc/shells</code> list all available shells</li></ul></li><li><code>usermod -aG sudo pwxcoo</code>，grant <code>sudo</code> privileges<ul><li><code>groups</code> check group of current user</li></ul></li><li>in client，<code>scp id_rsa.pub [username]@[server address]:/home/[username]</code> transfer public key to server</li><li>back to server，<code>mv id_rsa.pub .ssh/authorized_keys</code>  will be ok</li></ul><h2 id="nginx"><a href="#nginx" class="headerlink" title="nginx"></a>nginx</h2><ul><li><code>sudo apt update</code></li><li><code>sudo apt install nginx</code></li><li><code>cd /etc/nginx/conf.d</code> (NGINX site-specific configuration files are kept in <code>/etc/nginx/conf.d</code>)</li><li>simple template. <code>example.com.conf</code>  <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">    listen       80;</span><br><span class="line">    server_name  example.com;</span><br><span class="line"></span><br><span class="line">    #charset koi8-r;</span><br><span class="line">    #access_log  /var/log/nginx/host.access.log  main;</span><br><span class="line"></span><br><span class="line">    location / &#123;</span><br><span class="line">        root   /usr/share/nginx/html;</span><br><span class="line">        index  index.html index.htm;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    #error_page  404              /404.html;</span><br><span class="line"></span><br><span class="line">    # redirect server error pages to the static page /50x.html</span><br><span class="line">    #</span><br><span class="line">    error_page   500 502 503 504  /50x.html;</span><br><span class="line">    location = /50x.html &#123;</span><br><span class="line">        root   /usr/share/nginx/html;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>test nginx configration is ok<ul><li><code>sudo nginx -t</code></li><li><code>sudo nginx -s reload</code></li></ul></li></ul><h2 id="environment-variables"><a href="#environment-variables" class="headerlink" title="environment variables"></a>environment variables</h2><p>linux will execute <code>/etc/profile</code> while booting up, this script will execute all script in <code>/etc/profile.d</code>, so add some scripts in <code>/etc/profile.d</code> will be easily maintained and convenient.</p><p>like <code>jdk.sh</code>, it will set environment variables for Java.</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> J2SDKDIR=/usr/lib/jvm/java-8-oracle</span><br><span class="line"><span class="built_in">export</span> J2REDIR=/usr/lib/jvm/java-8-oracle/jre</span><br><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$PATH</span>:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin</span><br><span class="line"><span class="built_in">export</span> JAVA_HOME=/usr/lib/jvm/java-8-oracle</span><br><span class="line"><span class="built_in">export</span> DERBY_HOME=/usr/lib/jvm/java-8-oracle/db</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="memo" scheme="https://blog.pwxcoo.com/tags/memo/"/>
    
      <category term="linux" scheme="https://blog.pwxcoo.com/tags/linux/"/>
    
  </entry>
  
  <entry>
    <title>vim notes</title>
    <link href="https://blog.pwxcoo.com/2018/12/14/vim%20notes/"/>
    <id>https://blog.pwxcoo.com/2018/12/14/vim notes/</id>
    <published>2018-12-14T08:44:25.000Z</published>
    <updated>2022-03-24T05:48:48.871Z</updated>
    
    <content type="html"><![CDATA[<p>自己使用 Vim 中的一些常见问题。</p><p>我的 vimrc: <a href="https://github.com/pwxcoo/vimrc">pwxcoo/vimrc</a></p><h2 id="Update-vim-on-ubuntu"><a href="#Update-vim-on-ubuntu" class="headerlink" title="Update vim on ubuntu"></a>Update vim on ubuntu</h2><p><a href="https://vi.stackexchange.com/questions/10817/how-can-i-get-a-newer-version-of-vim-on-ubuntu">How can I get a newer version of Vim on Ubuntu</a></p><ul><li><code>sudo add-apt-repository ppa:jonathonf/vim</code></li><li><code>sudo apt update</code></li><li><code>sudo apt install vim</code></li></ul><h2 id="Use-System-Clipboard"><a href="#Use-System-Clipboard" class="headerlink" title="Use System Clipboard"></a>Use System Clipboard</h2><ul><li><code>sudo apt install vim-gnome</code></li><li><code>vim --version | grep &quot;clipboard&quot;</code> 查看 clipboard 前面是否是 <code>+</code></li></ul><p>然后</p><ul><li><code>&quot;+y</code> 复制到系统剪切板</li><li><code>&quot;+p</code> 粘贴</li></ul><h2 id="replace-one-by-one"><a href="#replace-one-by-one" class="headerlink" title="replace one by one"></a>replace one by one</h2><p><a href="https://blog.csdn.net/feelang/article/details/38408875">Vim -&gt; 边确认边查找替换</a></p><ul><li><code>/&#123;which&#125;</code> 开始查找</li><li><code>cw &#123;what&#125; [ESC]</code> 删除并替换</li><li><code>n</code> 下一个单词</li><li><code>.</code> 重复上一个操作 (删除并替换)</li></ul><h2 id="operate-multiple-rows"><a href="#operate-multiple-rows" class="headerlink" title="operate multiple rows"></a>operate multiple rows</h2><p>块操作</p><ul><li><code>C-v</code> 开始块操作</li><li><code>C-d</code> 向下移动</li><li>操作<ul><li><code>shift + i</code> 插入</li><li><code>J</code> 连结所有行 (Join)</li><li><code>&gt;</code> / <code>&lt;</code> 左右缩进</li><li><code>=</code> 自动缩进</li></ul></li><li><code>[ESC]</code> 生效</li></ul><h2 id="Plugins-keyboard-shortcut"><a href="#Plugins-keyboard-shortcut" class="headerlink" title="Plugins keyboard shortcut"></a>Plugins keyboard shortcut</h2><h3 id="NERDTree"><a href="#NERDTree" class="headerlink" title="NERDTree"></a>NERDTree</h3><ul><li><code>shift + i</code> 显示隐藏文件</li><li><code>m</code> 一系列操作<ul><li><code>a</code> 创造新文件</li></ul></li></ul><h3 id="Tabbar"><a href="#Tabbar" class="headerlink" title="Tabbar"></a>Tabbar</h3><ul><li><code>:bd</code> 关闭 buffer  (<strong>Recommend</strong>)</li><li><code>:bw</code> 关闭并不保存</li></ul><h3 id="NERDCommenter"><a href="#NERDCommenter" class="headerlink" title="NERDCommenter"></a>NERDCommenter</h3><ul><li><code>&lt;leader&gt; + c + &lt;space&gt;</code> 快速注释</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="memo" scheme="https://blog.pwxcoo.com/tags/memo/"/>
    
      <category term="linux" scheme="https://blog.pwxcoo.com/tags/linux/"/>
    
      <category term="vim" scheme="https://blog.pwxcoo.com/tags/vim/"/>
    
  </entry>
  
  <entry>
    <title>一次http请求全过程</title>
    <link href="https://blog.pwxcoo.com/2018/09/17/%E4%B8%80%E6%AC%A1http%E8%AF%B7%E6%B1%82%E5%85%A8%E8%BF%87%E7%A8%8B/"/>
    <id>https://blog.pwxcoo.com/2018/09/17/一次http请求全过程/</id>
    <published>2018-09-17T15:37:40.000Z</published>
    <updated>2022-03-24T05:48:48.875Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Question"><a href="#Question" class="headerlink" title="Question"></a>Question</h2><p>Q: <strong>一次 http 请求的过程？</strong></p><h2 id="得到自己的IP地址"><a href="#得到自己的IP地址" class="headerlink" title="得到自己的IP地址"></a>得到自己的IP地址</h2><p> (假设此时主机还未配置自己的 IP 地址。) </p><p>用 DHCP (Dynamic Host Configuration Protocol) 得到自己的 IP 地址。</p><ol><li><p>发送 DHCP 请求</p><ul><li>应用层: 生成一个 DHCP 请求报文</li><li>传输层: 加上一个 UDP 头<ul><li>源端口 68</li><li>目标端口 67</li></ul></li><li>网络层: 加上一个 IP 头<ul><li>源IP: 0.0.0.0</li><li>目标IP: 255.255.255.255</li></ul></li><li>数据链路层: 封装成帧<ul><li>源MAC: 自己的 MAC</li><li>目标MAC: FF:FF:FF:FF:FF:FF</li></ul></li></ul></li><li><p>DHCP 服务器 (连接在同一个交换机上) 就到请求后，返回一个 DHCP ACK 报文: </p></li></ol><ul><li>分配给 DHCP 请求的 IP</li><li>DNS 服务器的 IP</li><li>默认网关路由器的 IP</li><li>子网掩码</li></ul><ol start="3"><li>交换机收到之后，缓存一下，发给主机，主机就得到这些信息</li></ol><h2 id="查找默认网关的MAC地址"><a href="#查找默认网关的MAC地址" class="headerlink" title="查找默认网关的MAC地址"></a>查找默认网关的MAC地址</h2><p> (刚刚配置好 DHCP 需要去找到默认网关的 MAC 地址) </p><p>请求要发给默认网关的 MAC 地址，才能把请求发到局域网外部去，相当于发给默认网关路由器后，会根据 NAT 把局域网内部的 IP 地址变成局域网的 IP 地址然后发给局域网外部。</p><ol start="4"><li>生成一个 ARP 查询报文，目标 IP 是默认网关路由器，封装成帧后是一个广播帧</li><li>路由器返回一个 ARP 应答报文给主机，包含了自己的 IP 地址</li></ol><h2 id="DNS"><a href="#DNS" class="headerlink" title="DNS"></a>DNS</h2><ol start="6"><li>生成 DNS 查询报文<ul><li>应用层: 目的域名</li><li>传输层: 目的端口 53</li><li>数据链路层: 默认网关路由器 MAC 地址</li></ul></li><li>默认网关路由器收到后，根据路由表转发<ul><li>内部网关协议 (RIP，OSPF) </li><li>外部网关协议 (BGP) </li></ul></li><li>DNS 解析完了，封装成 UDP =&gt; 默认网关路由器 =&gt; 交换机 =&gt; 主机</li></ol><h2 id="HTTP请求"><a href="#HTTP请求" class="headerlink" title="HTTP请求"></a>HTTP请求</h2><ol start="9"><li>生成 TCP 套接字</li><li>3 次握手</li><li>发送 HTTP 报文</li><li>响应返回，浏览器渲染</li></ol><h2 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h2><p>有的网站是 https 的，通常会把 http 请求 301 重定向到 443 端口。</p><ul><li>server 发送证书和公钥给 client</li><li>client 去 CA 验证安全性</li><li>client 用公钥加密一个随机生成的对称密钥</li><li>server 用私钥解密，然后之后都用这个对称密钥来加密</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="interview" scheme="https://blog.pwxcoo.com/categories/interview/"/>
    
    
      <category term="computer network" scheme="https://blog.pwxcoo.com/tags/computer-network/"/>
    
  </entry>
  
  <entry>
    <title>计算机网络笔记</title>
    <link href="https://blog.pwxcoo.com/2018/06/24/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E7%AC%94%E8%AE%B0/"/>
    <id>https://blog.pwxcoo.com/2018/06/24/计算机网络笔记/</id>
    <published>2018-06-24T15:54:55.000Z</published>
    <updated>2022-03-24T05:48:48.875Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><h3 id="TCP-IP"><a href="#TCP-IP" class="headerlink" title="TCP/IP"></a>TCP/IP</h3><ul><li>数据链路层: ARP，RARP</li><li>网络层: IP，ICMP，IGMP</li><li>传输层: TCP，UDP，UGP</li><li>应用层: Telnet，FTP，SMTP，SNMP</li></ul><h3 id="OSI"><a href="#OSI" class="headerlink" title="OSI"></a>OSI</h3><ul><li>物理层: EIA/TIA-232, EIA/TIA-499, V.35, V.24, RJ45, Ethernet, 802.3, 802.5, FDDI, NRZI, NRZ, B8ZS</li><li>数据链路层: Frame Relay, HDLC, PPP, IEEE 802.3/802.2, FDDI, ATM,  IEEE 802.5/802.2</li><li>网络层: IP，IPX，AppleTalk DDP</li><li>传输层: TCP，UDP，SPX</li><li>会话层: RPC,SQL,NFS,NetBIOS,names,AppleTalk,ASP,DECnet,SCP</li><li>表示层:TIFF,GIF,JPEG,PICT,ASCII,EBCDIC,encryption,MPEG,MIDI,HTML</li><li>应用层: FTP,WWW,Telnet,NFS,SMTP,Gateway,SNMP</li></ul><h4 id="应用层"><a href="#应用层" class="headerlink" title="应用层"></a>应用层</h4><p>应用层: 为特定应用程序提供数据传输服务，例如 HTTP，DNS (port 53) 。数据单位为报文 (message) 。</p><ol><li>主要功能 : 用户接口、应用程序<br>application</li><li>典型设备: 网关</li><li>典型协议、标准和应用: TELNET, FTP, HTTP</li></ol><h4 id="表示层"><a href="#表示层" class="headerlink" title="表示层"></a>表示层</h4><p>表示层: 数据压缩，加密以及数据描述。使得应用程序不必担心中各个主机中表示/存储内部格式的不同。</p><ol><li>主要功能 : 数据的表示、压缩和加密<br>presentation</li><li>典型设备: 网关</li><li>典型协议、标准和应用: ASCLL、PICT、TIFF、JPEG、 MIDI、MPEG</li></ol><h4 id="会话层"><a href="#会话层" class="headerlink" title="会话层"></a>会话层</h4><p>会话层: 建立和管理会话。 (PS: 在 TCP/IP 中只有应用层，这些功能都是留给应用程序开发者处理的。)</p><ol><li>主要功能 : 会话的建立和结束<br>session</li><li>典型设备: 网关</li><li>典型协议、标准和应用: RPC、SQL、NFS 、X WINDOWS、ASP</li></ol><h4 id="传输层"><a href="#传输层" class="headerlink" title="传输层"></a>传输层</h4><p>运输层: 提供的是进程间通用数据传输服务。TCP (报文段segment) , UDP (用户数据报 datagram) 。 (PS: segment 是因为 TCP 会切分，每个 segment 是属于某个整体的，每个 segment 的在整体中的位置是确定的，A，B，C 三个 segment 最后解析后一定是 ABC 顺序的，这是 TCP 协议所保证的，而 datagram 是 independent，每个数据报是独立的，不属于某个整体。)</p><ol><li>主要功能 : 端到端控制<br>transport</li><li>典型设备: 网关</li><li>典型协议、标准和应用: TCP、UDP、SPX</li></ol><h4 id="网络层"><a href="#网络层" class="headerlink" title="网络层"></a>网络层</h4><p>网络层: 为主机间提供数据传输服务。把运输层传来的封装成分组 (packet)</p><ol><li>主要功能 : 路由，寻址<br>network</li><li>典型设备: 路由器</li><li>典型协议、标准和应用: IP、IPX、APPLETALK、ICMP</li></ol><h4 id="数据链路层"><a href="#数据链路层" class="headerlink" title="数据链路层"></a>数据链路层</h4><p>数据链路层: 主机间可以有很多链路，链路层协议就是为同一链路的节点提供服务。把网络层传来传来的分组封装成帧 (frame) 。</p><ol><li>主要功能 : 保证误差错的数据链路<br>data link</li><li>典型设备: 交换机、网桥、网卡</li><li>典型协议、标准和应用: 802.2、802.3ATM、HDLC、FRAME RELAY</li></ol><h4 id="物理层"><a href="#物理层" class="headerlink" title="物理层"></a>物理层</h4><p>物理层: 考虑的是怎样中传输媒体上传输数据比特流 (bit) ，而不是具体传输媒介。尽可能屏蔽传输媒体和通信手段的差异，使数据链路层感受不到这些差异。</p><ol><li>主要功能 : 传输比特流<br>physical</li><li>典型设备: 集线器、中继器</li><li>典型协议、标准和应用: V.35、EIA/TIA-232</li></ol><hr><p>整个传输过程中就是应用层的数据往下传，加首部，加首部，加首部 (在链路层可能要加尾部 —— CRC 校验加了 FCS) 直到解析成比特流 (1001010…) 。然后到了目标地点，去首部尾部这样，解析成最后的数据。</p><h2 id="物理层-1"><a href="#物理层-1" class="headerlink" title="物理层"></a>物理层</h2><ul><li>单工通信</li><li>半双工通信</li><li>全双工通信</li></ul><h2 id="数据链路层-1"><a href="#数据链路层-1" class="headerlink" title="数据链路层"></a>数据链路层</h2><h3 id="差错检测"><a href="#差错检测" class="headerlink" title="差错检测"></a>差错检测</h3><p>循环冗余校验 CRC (Cyclic Redundancy Check)</p><h3 id="信道"><a href="#信道" class="headerlink" title="信道"></a>信道</h3><h4 id="广播信道"><a href="#广播信道" class="headerlink" title="广播信道"></a>广播信道</h4><p>一对多通信，一个节点发送的数据能够被广播信道上所有的节点接收到。</p><p>所有的节点都在同一个广播信道上发送数据，因此需要有专门的控制方法进行协调，避免发生冲突 (冲突也叫碰撞) 。</p><p>主要有两种控制方法进行协调，一个是使用信道复用技术，一是使用 CSMA/CD 协议。</p><p>CSMA/CD (Carrier Sense Multiple Access with Collision Detection) ，载波监听多点接入/碰撞检测。原理:</p><ul><li>先听后发，边发边听，冲突停发，随机延迟后重发</li></ul><h4 id="点对点信道"><a href="#点对点信道" class="headerlink" title="点对点信道"></a>点对点信道</h4><p>一对一通信，用 PPP 协议，信息部分字节不超过 1500</p><h4 id="信道复用技术"><a href="#信道复用技术" class="headerlink" title="信道复用技术"></a>信道复用技术</h4><ul><li>频分复用 (占用不同的频率带宽)</li><li>时分复用，每个主机发个时间片</li><li>统计时分复用，把数据集中起来组成统计时分复用帧发送</li><li>波分复用，光的波长</li><li>码分复用</li></ul><h3 id="MAC地址"><a href="#MAC地址" class="headerlink" title="MAC地址"></a>MAC地址</h3><p>长度 6 字节 (48位) 。</p><h2 id="网络层-1"><a href="#网络层-1" class="headerlink" title="网络层"></a>网络层</h2><h3 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h3><ul><li>IP (Internet Protocol)</li><li>地址解析协议 ARP (Address Resolution Protocol)</li><li>网际控制报文协议 ICMP (Internet Control Message Protocol)</li><li>网际组管理协议 IGMP (Internet Group Management Protocol)</li></ul><h3 id="IP数据报格式"><a href="#IP数据报格式" class="headerlink" title="IP数据报格式"></a>IP数据报格式</h3><p><img src="/image/ip-format.png" alt="ip-format.png"></p><ul><li>版本 (4位) : 4, 6 两个值。</li><li>首部长度 (4位) : 最大值 15，单位是 4 字节 (32位) ，首部固定长度 20 字节，所以最小值 5 。如果可选字段的长度不是 4 字节的整数倍，就用尾部的填充部分来填充。</li><li>区分服务 (8位) : 来获得更好的服务，一般情况下不使用。</li><li>总长度 (16位) : 包括首部长度和数据部分长度。</li><li>标识 (16位) : 在数据报长度过长从而发生分片的情况下，相同数据报的不同分片具有相同的标识符。</li><li>片偏移 (16位)  : 和标志 (3位) 一起，用于标志分片的情况。标志中目前只有前两位有意义。标志字段的最低位是MF (More Fragment)。MF = 1 表示后面“还有分片”。MF = 0 表示最后一个分片。标志字段中间的一位是 DF (Don’t Fragment)。只有当 DF = 0时才允许分片。片偏移的单位为 8 字节。</li><li>生存时间 (8位) : TTL，防止无法交付的数据报在互联网中兜圈子。以路由器跳数为单位，TTL 为 0 时就丢弃数据报。</li><li>协议 (8位) 指出携带的数据应该交给那个协议来处理，例如 TCP，UDP 等。</li><li>首部校验和 (16位)</li><li>源地址 (32位)</li><li>目标地址 (32位)</li><li>可选字段 + 填充 (32位)</li></ul><h3 id="地址解析协议ARP"><a href="#地址解析协议ARP" class="headerlink" title="地址解析协议ARP"></a>地址解析协议ARP</h3><p>ARP 实现由 IP 地址得到 MAC 地址。每个主机都有一个 ARP 高速缓存，里面有本局域网上各主机和路由器 IP 地址到硬件地址的映射表。</p><h3 id="网际控制报文协议ICMP"><a href="#网际控制报文协议ICMP" class="headerlink" title="网际控制报文协议ICMP"></a>网际控制报文协议ICMP</h3><p>ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。分为差错报告报文，询问报文。</p><ol><li>Ping，ICMP 的一个重要应用，发送的 IP 数据报是无法交付的 UDP 用户数据报。</li><li>Tracroute，跟踪一个分组从源点到终点的路径。</li></ol><h3 id="网络地址转换NAT"><a href="#网络地址转换NAT" class="headerlink" title="网络地址转换NAT"></a>网络地址转换NAT</h3><p>将本地 IP 转换成全球 IP。</p><h3 id="路由选择协议"><a href="#路由选择协议" class="headerlink" title="路由选择协议"></a>路由选择协议</h3><ul><li>自治系统内部的路由选择协议: RIP 和 OSPF</li><li>自治系统间的路由选择: BGP</li></ul><h4 id="RIP-Routing-Information-Protocol"><a href="#RIP-Routing-Information-Protocol" class="headerlink" title="RIP (Routing Information Protocol)"></a>RIP (Routing Information Protocol)</h4><p>基于向量的路由选择协议，实现简单，开销小。但是最大使用距离为 15，限制了网络的规模。</p><h4 id="OSPF-Open-Shortest-Path-First"><a href="#OSPF-Open-Shortest-Path-First" class="headerlink" title="OSPF (Open Shortest Path First)"></a>OSPF (Open Shortest Path First)</h4><p>开放最短路径优先 OSPF。相比与 RIP，OSPF 更新收敛地更快。</p><h4 id="BGP-Border-Gateway-Protocol"><a href="#BGP-Border-Gateway-Protocol" class="headerlink" title="BGP (Border Gateway Protocol)"></a>BGP (Border Gateway Protocol)</h4><p>在每个 AS (Autonomous System) 都必须配置 BGP 发言人，通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。</p><h2 id="运输层"><a href="#运输层" class="headerlink" title="运输层"></a>运输层</h2><p>提供进程间的通信。</p><h3 id="TCP和UDP"><a href="#TCP和UDP" class="headerlink" title="TCP和UDP"></a>TCP和UDP</h3><ul><li>用户数据报协议 UDP (User Datagram Protocol) 是无连接的，尽最大努力交付，没有拥塞控制，面向报文，支持一对一，一对多，多对一，多对多的交互通信。</li><li>传输控制协议 TCP (Transmission Control Protocol) 是面向连接的，提供可靠交付，有流量控制，拥塞控制，提供全双工通信，面向字节流的，每个 TCP 连接只能是点对点 (一对一的) 。</li></ul><h3 id="UDP"><a href="#UDP" class="headerlink" title="UDP"></a>UDP</h3><p><img src="/image/udp-header.png" alt="udp-header.png"></p><p>首部字段只有 8 个字节，包括源端口，目的端口，长度，检验和。12 字节的伪首部是为了计算检验和临时添加的。</p><h3 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h3><h4 id="TCP首部"><a href="#TCP首部" class="headerlink" title="TCP首部"></a>TCP首部</h4><p><img src="/image/tcp-header.png" alt="tcp-header.png"></p><ul><li>序号 (32位) :  用于对字节流进行编号，例如序号为 301，表示第一个字节的编号为 301，如果携带的数据长度为 100 字节，那么下一个报文段的序号应为 401。</li><li>确认号 (32位) : 期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段，序号为 501，携带的数据长度为 200 字节，因此 B 期望下一个报文段的序号为 701，B 发送给 A 的确认报文段中确认号就为 701。</li><li>数据偏移 (4位) : 指的是数据部分距离报文段起始处的偏移量，实际上指的是首部的长度。</li><li>确认 ACK (1位) : 当 ACK=1 时确认号字段有效，否则无效。TCP 规定，在连接建立后所有传送的报文段都必须把 ACK 置 1。</li><li>同步 SYN  (1位) : 在连接建立时用来同步序号。当 SYN=1，ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接，则响应报文中 SYN=1，ACK=1。</li><li>终止 FIN  (1位) : 用来释放一个连接，当 FIN=1 时，表示此报文段的发送方的数据已发送完毕，并要求释放连接。</li><li>窗口 (16位) : 窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制，是因为接收方的数据缓存空间是有限的。表示传送的数据字节数。</li></ul><h4 id="TCP握手"><a href="#TCP握手" class="headerlink" title="TCP握手"></a>TCP握手</h4><p><img src="/image/tcp-handshake.png" alt="tcp-handshake.png"></p><ul><li>首先 B 处于 LISTEN (监听) 状态，等待客户的连接请求。</li><li>A 向 B 发送连接请求报文段，SYN=1，ACK=0，选择一个初始的序号 x。</li><li>B 收到连接请求报文段，如果同意建立连接，则向 A 发送连接确认报文段，SYN=1，ACK=1，确认号为 x+1，同时也选择一个初始的序号 y。</li><li>A 收到 B 的连接确认报文段后，还要向 B 发出确认，确认号为 y+1，序号为 x+1。</li><li>B 收到 A 的确认后，连接建立。</li></ul><p><strong>PS: 如果第三次 ACK 一直没收到，server 端会重传第二次握手，一定次数后会关闭连接。但此时 client 以为连接以及建立，发送请求后，server 端会在回复报文中将 RST 置为 1，client 就知道了连接未建立，重置 TCP 连接</strong></p><h4 id="TCP挥手"><a href="#TCP挥手" class="headerlink" title="TCP挥手"></a>TCP挥手</h4><p><img src="/image/tcp-wave.png" alt="tcp-wave.png"></p><ul><li>A 发送连接释放报文段，FIN=1。</li><li>B 收到之后发出确认，此时 TCP 属于半关闭状态，B 能向 A 发送数据但是 A 不能向 B 发送数据。</li><li>当 B 不再需要连接时，发送连接释放请求报文段，FIN=1。</li><li>A 收到后发出确认，进入 TIME-WAIT 状态，等待 2 MSL 时间后释放连接。</li><li>B 收到 A 的确认后释放连接。</li></ul><p>客户端接收到服务器端的 FIN 报文后进入此状态，此时并不是直接进入 CLOSED 状态，还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由:</p><ul><li>确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段，那么就会重新发送连接释放请求报文段，A 等待一段时间就是为了处理这种情况的发生。</li><li>等待一段时间是为了让本连接持续时间内所产生的所有报文段都从网络中消失，使得下一个新的连接不会出现旧的连接请求报文段。</li></ul><h3 id="TCP可靠性"><a href="#TCP可靠性" class="headerlink" title="TCP可靠性"></a>TCP可靠性</h3><ul><li>检验和</li><li>序列号</li><li>ACK</li><li>超时重传</li><li>连接管理</li><li>流量控制</li><li>拥塞控制</li></ul><h4 id="TCP滑动窗口"><a href="#TCP滑动窗口" class="headerlink" title="TCP滑动窗口"></a>TCP滑动窗口</h4><p>窗口是缓存的一部分，用来暂时存放字节流。接收窗口只会对窗口内最后一个按序到达的字节进行确认。</p><h4 id="TCP流量控制"><a href="#TCP流量控制" class="headerlink" title="TCP流量控制"></a>TCP流量控制</h4><p>流量控制是为了控制发送方发送速率，保证接收方来得及接收。</p><h4 id="TCP拥塞控制"><a href="#TCP拥塞控制" class="headerlink" title="TCP拥塞控制"></a>TCP拥塞控制</h4><p>出现拥塞时，应当控制发送方的速率。</p><p>TCP 主要通过四种算法来进行拥塞控制: 慢开始、拥塞避免、快重传、快恢复。</p><p>发送方需要维护一个叫做拥塞窗口 (cwnd) 的状态变量，注意拥塞窗口与发送方窗口的区别: 拥塞窗口只是一个状态变量，实际决定发送方能发送多少数据的是发送方窗口。</p><ol><li><p>慢开始与拥塞避免: 发送的最初执行慢开始，令 cwnd=1，发送方只能发送 1 个报文段；当收到确认后，将 cwnd 加倍，因此之后发送方能够发送的报文段数量为: 2、4、8 … 注意到慢开始每个轮次都将 cwnd 加倍，这样会让 cwnd 增长速度非常快，从而使得发送方发送的速度增长速度过快，网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh，当 cwnd &gt;= ssthresh 时，进入拥塞避免，每个轮次只将 cwnd 加 1。如果出现了超时，则令 ssthresh = cwnd/2，然后重新执行慢开始。</p></li><li><p>快重传与快恢复: 在接收方，要求每次接收到报文段都应该发送对已收到有序报文段的确认，例如已经接收到 M1 和 M2，此时收到 M4，应当发送对 M2 的确认。在发送方，如果收到三个重复确认，那么可以确认下一个报文段丢失，例如收到三个 M2 ，则 M3 丢失。此时执行快重传，立即重传下一个报文段。在这种情况下，只是丢失个别报文段，而不是网络拥塞，因此执行快恢复，令 ssthresh = cwnd/2 ，cwnd = ssthresh，注意到此时直接进入拥塞避免。慢开始和快恢复的快慢指的是 cwnd 的设定值，而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1，而快恢复 cwnd 设定为 ssthresh。</p></li></ol><h2 id="应用层-1"><a href="#应用层-1" class="headerlink" title="应用层"></a>应用层</h2><h3 id="DNS-Domain-Name-System-port-53"><a href="#DNS-Domain-Name-System-port-53" class="headerlink" title="DNS (Domain Name System)  (port 53)"></a>DNS (Domain Name System)  (port 53)</h3><p>大多数情况下 DNS 使用 UDP 进行传输，这就要求域名解析器和域名服务器都必须自己处理超时和重传来保证可靠性。在两种情况下会使用 TCP 进行传输:</p><ul><li>数据太大，用 TCP 进行传输。</li><li>区域传送是主域名服务器向辅助域名服务器传送变化的那部分数据，区域传送需要使用 TCP 进行传输。</li></ul><h3 id="FTP-File-Transfer-Protocol-port-21-20"><a href="#FTP-File-Transfer-Protocol-port-21-20" class="headerlink" title="FTP (File Transfer Protocol) (port 21/20)"></a>FTP (File Transfer Protocol) (port 21/20)</h3><p>FTP 使用 TCP 进行连接，它需要两个连接来传送一个文件:</p><ul><li>控制连接: 服务器以打开端口号 21 等待客户端的连接，客户端主动建立连接后，使用这个连接将客户端的命令传送给服务器，并传回服务器的应答。</li><li>数据连接: 用来传送一个文件。</li></ul><p>FTP 有主动和被动两种模式:</p><ul><li>主动模式: 服务器端主动建立数据连接，其中服务器端的端口号为 20，客户端的端口号随机，但是必须大于 1024，因为 0~1023 是熟知端口号。</li><li>被动模式: 客户端主动建立数据连接，其中客户端的端口号由客户端自己指定，服务器端的端口号随机。</li></ul><p>主动模式要求客户端开放端口号给服务器端，需要去配置客户端的防火墙。被动模式只需要服务器端开放端口号即可，无需客户端配置防火墙。但是被动模式会导致服务器端的安全性减弱，因为开放了过多的端口号。</p><h3 id="Telnet-port-23"><a href="#Telnet-port-23" class="headerlink" title="Telnet(port 23)"></a>Telnet(port 23)</h3><p>远程登录协议</p><h3 id="电子邮件协议"><a href="#电子邮件协议" class="headerlink" title="电子邮件协议"></a>电子邮件协议</h3><p>发送协议常用 SMTP (Simple Mail Transfer Protocol) ，读取协议常用 POP3 (Post Office Protocol - Version 3)  和 IMAP (Internet Mail Access Protocol) 。</p><ul><li>POP3 (port 110) : POP3 的特点是只要用户从服务器上读取了邮件，就把该邮件删除。</li><li>IMAP (port 143) : IMAP 协议中客户端和服务器上的邮件保持同步，如果不去手动删除邮件，那么服务器上的邮件也不会被删除。IMAP 这种做法可以让用户随时随地去访问服务器上的邮件。</li><li>SMTP (port 25) : SMTP 只能发送 ASCII 码，而互联网邮件扩充 MIME 可以发送二进制文件。MIME 并没有改动或者取代 SMTP，而是增加邮件主体的结构，定义了非 ASCII 码的编码规则。</li></ul><h3 id="DHCP-Dynamic-Host-Configuration-Protocol"><a href="#DHCP-Dynamic-Host-Configuration-Protocol" class="headerlink" title="DHCP (Dynamic Host Configuration Protocol)"></a>DHCP (Dynamic Host Configuration Protocol)</h3><p>DHCP (Dynamic Host Configuration Protocol) 提供了即插即用的连网方式，用户不再需要去手动配置 IP 地址等信息。</p><p>DHCP 工作过程如下:</p><ul><li>客户端发送 Discover 报文，该报文的目的地址为 255.255.255.255:67，源地址为 0.0.0.0:68，被放入 UDP 中，该报文被广播到同一个子网的所有主机上。</li><li>DHCP 服务器收到 Discover 报文之后，发送 Offer 报文给客户端，该报文包含了客户端所需要的信息。因为客户端可能收到多个DHCP 服务器提供的信息，因此客户端需要进行选择。</li><li>如果客户端选择了某个 DHCP 服务器提供的信息，那么就发送 Request 报文给该 DHCP 服务器。</li><li>DHCP 服务器发送 Ack 报文，表示客户端此时可以使用提供给它的信息。</li></ul><p>如果客户端和 DHCP 服务器不在同一个子网，就需要使用中继代理。</p><h2 id="References"><a href="#References" class="headerlink" title="References"></a>References</h2><ul><li><a href="https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#udp-%E9%A6%96%E9%83%A8%E6%A0%BC%E5%BC%8F">Interview-Notebook/notes/计算机网络.md</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="base" scheme="https://blog.pwxcoo.com/categories/base/"/>
    
    
      <category term="computer network" scheme="https://blog.pwxcoo.com/tags/computer-network/"/>
    
  </entry>
  
  <entry>
    <title>leetcode150题</title>
    <link href="https://blog.pwxcoo.com/2018/02/25/leetcode150%E9%A2%98/"/>
    <id>https://blog.pwxcoo.com/2018/02/25/leetcode150题/</id>
    <published>2018-02-25T14:15:39.000Z</published>
    <updated>2022-03-24T05:48:48.870Z</updated>
    
    <content type="html"><![CDATA[<p>刷了 10 多天，刷了 leetcode 上前面 150 题。</p><p>因为很多题以前做过，所以做的还是比较快的。因为想熟悉一下 Java ,所以全部都用 Java 写的。leetcode 很多都是面试题，所以题目都是基本，没有什么特别难的题目，几道 hard 也没有特别难的。但是很多题目很经典，而且社区很活跃，Discuss 里很多神仙。有几道题目真的让我感觉到“还有这种操作？”的感受，因为思路太秀了。</p><p>之所以特地现在做一个中断，是因为前面 150 道比较经典，虽然很多类型的题目还没出现过，但是最新的题目我做过一些，有凸包，有最短路径什么的，而且 150-200 题之前很多 easy 和 medium，太水了，我懒得做了。我大概浏览了一下 150-200 的 hard，知道了基本的思路，然后就打算先这样中断一下，不打算最近撸了，之后如果没什么特别需要的地方，可能就每周做一下 Contest 吧。</p><p>因为有几道题目很帅，所以特地记录一下，有空可以在做一遍这几道。</p><h2 id="leetcode32"><a href="#leetcode32" class="headerlink" title="leetcode32"></a>leetcode32</h2><p>leetcode32. Longest Valid Parentheses</p><p>可以用 DP ，也可以用栈来做。我用了 DP 。官方 Solution 很详细了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">longestValidParentheses</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> s.length();</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[len];</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; len; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(i - <span class="number">1</span> &gt;= <span class="number">0</span> &amp;&amp; s.charAt(i) == <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>(s.charAt(i - <span class="number">1</span>) == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">if</span>(i &gt;= <span class="number">2</span>) dp[i] = dp[i - <span class="number">2</span>] + <span class="number">2</span>;</span><br><span class="line">                    <span class="keyword">else</span> dp[i] = <span class="number">2</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span>(s.charAt(i - <span class="number">1</span>) == <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">if</span>(i - <span class="number">1</span> - dp[i - <span class="number">1</span>] &gt;= <span class="number">0</span> &amp;&amp; s.charAt(i - <span class="number">1</span> - dp[i - <span class="number">1</span>]) == <span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">                    &#123;</span><br><span class="line">                        dp[i] = dp[i - <span class="number">1</span>] + <span class="number">2</span>;</span><br><span class="line">                        <span class="keyword">if</span>(i - <span class="number">2</span> - dp[i - <span class="number">1</span>] &gt;= <span class="number">0</span>) dp[i] += dp[i - <span class="number">2</span> - dp[i - <span class="number">1</span>]];</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                res = Math.max(res, dp[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="leetcode42"><a href="#leetcode42" class="headerlink" title="leetcode42"></a>leetcode42</h2><p>leetcode42. Trapping Rain Water</p><p>用两个指针向中间收敛来维护一个变量。官方的 Solution 也很详细。也可以用 DP 或者栈来做。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">trap</span><span class="params">(<span class="type">int</span>[] height)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(height.length &lt;= <span class="number">2</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">0</span>, right = height.length - <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>, level = Math.min(height[left], height[right]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(left &lt; right)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(height[left] &lt;= height[right])</span><br><span class="line">            &#123;</span><br><span class="line">                left++;</span><br><span class="line">                <span class="keyword">if</span>(height[left] &gt; level)</span><br><span class="line">                &#123;</span><br><span class="line">                    level = Math.min(height[left], height[right]);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    res += level - height[left];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                right--;</span><br><span class="line">                <span class="keyword">if</span>(height[right] &gt; level)</span><br><span class="line">                &#123;</span><br><span class="line">                    level = Math.min(height[left], height[right]);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    res += level - height[right];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="leetcode69"><a href="#leetcode69" class="headerlink" title="leetcode69"></a>leetcode69</h2><p>leetcode69. Sqrt(x)</p><p>题目贴的 tag 是 binary search，想考查的是二分查找，用二分查找可以。但是一般标准库里用的是 sqrt() 用的是牛顿迭代法。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *  1. 二分查找</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">mySqrt</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (x == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">1</span>, right = Integer.MAX_VALUE;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> left + (right - left)/<span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span> (mid &gt; x/mid) &#123;</span><br><span class="line">                right = mid - <span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (mid + <span class="number">1</span> &gt; x/(mid + <span class="number">1</span>))</span><br><span class="line">                    <span class="keyword">return</span> mid;</span><br><span class="line">                left = mid + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *  2. 牛顿迭代法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">mySqrt</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">r</span> <span class="operator">=</span> x;</span><br><span class="line">        <span class="keyword">while</span> (r*r &gt; x)</span><br><span class="line">            r = (r + x/r) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">return</span> (<span class="type">int</span>) r;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="leetcode84"><a href="#leetcode84" class="headerlink" title="leetcode84"></a>leetcode84</h2><p>leetcode84. Largest Rectangle in Histogram</p><p>可以转化成最大01矩阵问题，但是会超时。当然用栈来模拟。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">largestRectangleArea</span><span class="params">(<span class="type">int</span>[] heights)</span> &#123;</span><br><span class="line">        Stack&lt;Integer&gt; st = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>, cnt = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; heights.length; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(st.empty() || heights[i] &gt;= st.peek())</span><br><span class="line">            &#123;</span><br><span class="line">                st.push(heights[i]);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                cnt = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">while</span>(!st.empty() &amp;&amp; heights[i] &lt; st.peek())</span><br><span class="line">                &#123;</span><br><span class="line">                    res = Math.max(res, ++cnt * st.pop());</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">while</span>(cnt-- &gt;= <span class="number">0</span>) st.push(heights[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        cnt = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(!st.empty())</span><br><span class="line">        &#123;</span><br><span class="line">            res = Math.max(res, ++cnt * st.pop());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="leetcode132"><a href="#leetcode132" class="headerlink" title="leetcode132"></a>leetcode132</h2><p>leetcode132. Palindrome Partitioning II</p><p>两种DP。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minCut</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        <span class="type">boolean</span>[][] isPalindrome = <span class="keyword">new</span> <span class="title class_">boolean</span>[s.length()][s.length()];</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[s.length() + <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;= s.length(); i++) dp[i] = s.length() - i - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> s.length() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i --)</span><br><span class="line">        &#123;</span><br><span class="line">            isPalindrome[i][i] = <span class="literal">true</span>;</span><br><span class="line">            dp[i] = dp[i + <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i + <span class="number">1</span>; j &lt; s.length(); j++)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>(s.charAt(i) == s.charAt(j) &amp;&amp; (i + <span class="number">1</span> == j || isPalindrome[i + <span class="number">1</span>][j - <span class="number">1</span>]))</span><br><span class="line">                &#123;</span><br><span class="line">                    isPalindrome[i][j] = <span class="literal">true</span>;</span><br><span class="line">                    <span class="keyword">if</span>(dp[j + <span class="number">1</span>] + <span class="number">1</span> &lt; dp[i])</span><br><span class="line">                    &#123;</span><br><span class="line">                        dp[i] = dp[j + <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minCut</span><span class="params">(String s)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[s.length() + <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;= s.length(); i++) dp[i] = i - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; s.length(); i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; i - j &gt;= <span class="number">0</span> &amp;&amp; i + j &lt; s.length() &amp;&amp; s.charAt(i - j) == s.charAt(i + j); j++) <span class="comment">// odd</span></span><br><span class="line">                dp[i + j + <span class="number">1</span>] = Math.min(dp[i + j + <span class="number">1</span>], dp[i - j] + <span class="number">1</span>);</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; i - j - <span class="number">1</span> &gt;= <span class="number">0</span> &amp;&amp; i + j &lt; s.length() &amp;&amp; s.charAt(i - j - <span class="number">1</span>) == s.charAt(i + j); j++) <span class="comment">// even</span></span><br><span class="line">                dp[i + j + <span class="number">1</span>] = Math.min(dp[i + j + <span class="number">1</span>], dp[i - j - <span class="number">1</span>] + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[s.length()];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="leetcode137"><a href="#leetcode137" class="headerlink" title="leetcode137"></a>leetcode137</h2><p>leetcode137. Single Number II</p><p>感受到布尔代数的魅力了。一波位运算毁天灭地。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">singleNumber</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">one</span> <span class="operator">=</span> <span class="number">0</span>, two = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> num : nums)</span><br><span class="line">        &#123;</span><br><span class="line">            one = (one ^ num) &amp; ~two;</span><br><span class="line">            two = (two ^ num) &amp; ~one;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> one;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="leetcode146"><a href="#leetcode146" class="headerlink" title="leetcode146"></a>leetcode146</h2><p>leetcode146. LRU Cache</p><p>实现一个 LRU(Least Recently Used) 的 cache 类。突然感受了一下学操作系统学到的东西，这个看起来很简单的东西，实现起来还是有点秀的。用双向链表和哈希表来实现，让查询和插入都为O(1)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">LRUCache</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">DLinkedNode</span> &#123;</span><br><span class="line">        <span class="type">int</span> key;</span><br><span class="line">        <span class="type">int</span> value;</span><br><span class="line">        DLinkedNode pre;</span><br><span class="line">        DLinkedNode post;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Always add the new node right after head;</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">addNode</span><span class="params">(DLinkedNode node)</span>&#123;</span><br><span class="line">        node.pre = head;</span><br><span class="line">        node.post = head.post;</span><br><span class="line"></span><br><span class="line">        head.post.pre = node;</span><br><span class="line">        head.post = node;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Remove an existing node from the linked list.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">removeNode</span><span class="params">(DLinkedNode node)</span>&#123;</span><br><span class="line">        <span class="type">DLinkedNode</span> <span class="variable">pre</span> <span class="operator">=</span> node.pre;</span><br><span class="line">        <span class="type">DLinkedNode</span> <span class="variable">post</span> <span class="operator">=</span> node.post;</span><br><span class="line"></span><br><span class="line">        pre.post = post;</span><br><span class="line">        post.pre = pre;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Move certain node in between to the head.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">moveToHead</span><span class="params">(DLinkedNode node)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.removeNode(node);</span><br><span class="line">        <span class="built_in">this</span>.addNode(node);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// pop the current tail.</span></span><br><span class="line">    <span class="keyword">private</span> DLinkedNode <span class="title function_">popTail</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">DLinkedNode</span> <span class="variable">res</span> <span class="operator">=</span> tail.pre;</span><br><span class="line">        <span class="built_in">this</span>.removeNode(res);</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Map&lt;Integer, DLinkedNode&gt; cache = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;Integer, DLinkedNode&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> count;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> capacity;</span><br><span class="line">    <span class="keyword">private</span> DLinkedNode head, tail;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LRUCache</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.count = <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line"></span><br><span class="line">        head = <span class="keyword">new</span> <span class="title class_">DLinkedNode</span>();</span><br><span class="line">        head.pre = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        tail = <span class="keyword">new</span> <span class="title class_">DLinkedNode</span>();</span><br><span class="line">        tail.post = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        head.post = tail;</span><br><span class="line">        tail.pre = head;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">get</span><span class="params">(<span class="type">int</span> key)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">DLinkedNode</span> <span class="variable">node</span> <span class="operator">=</span> cache.get(key);</span><br><span class="line">        <span class="keyword">if</span>(node == <span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>; <span class="comment">// should raise exception here.</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// move the accessed node to the head;</span></span><br><span class="line">        <span class="built_in">this</span>.moveToHead(node);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> node.value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(<span class="type">int</span> key, <span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="type">DLinkedNode</span> <span class="variable">node</span> <span class="operator">=</span> cache.get(key);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(node == <span class="literal">null</span>)&#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">DLinkedNode</span> <span class="variable">newNode</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DLinkedNode</span>();</span><br><span class="line">            newNode.key = key;</span><br><span class="line">            newNode.value = value;</span><br><span class="line"></span><br><span class="line">            <span class="built_in">this</span>.cache.put(key, newNode);</span><br><span class="line">            <span class="built_in">this</span>.addNode(newNode);</span><br><span class="line"></span><br><span class="line">            ++count;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(count &gt; capacity)&#123;</span><br><span class="line">                <span class="comment">// pop the tail</span></span><br><span class="line">                <span class="type">DLinkedNode</span> <span class="variable">tail</span> <span class="operator">=</span> <span class="built_in">this</span>.popTail();</span><br><span class="line">                <span class="built_in">this</span>.cache.remove(tail.key);</span><br><span class="line">                --count;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">// update the value.</span></span><br><span class="line">            node.value = value;</span><br><span class="line">            <span class="built_in">this</span>.moveToHead(node);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Your LRUCache object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment"> * LRUCache obj = new LRUCache(capacity);</span></span><br><span class="line"><span class="comment"> * int param_1 = obj.get(key);</span></span><br><span class="line"><span class="comment"> * obj.put(key,value);</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h2 id="leetcode166"><a href="#leetcode166" class="headerlink" title="leetcode166"></a>leetcode166</h2><p>leetcode166. Fraction to Recurring Decimal</p><p>写一个除法生成分数。比较难的用无线循环小数的时候，用哈希表存储余数，key存储余数，value存储起始的位置。如果重复就在起始位置插入’(‘。</p><h2 id="leetcode187"><a href="#leetcode187" class="headerlink" title="leetcode187"></a>leetcode187</h2><p>leetcode187. Repeated DNA Sequences</p><p>用三位就可以表示 A，C，G，T。10个的话用 30 位表示一个序列保存到哈希表中。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这几道是真的很秀。我这几天的代码全在<a href="https://github.com/pwxcoo/ac-game">这里</a>了。</p><p>放一张图片。</p><p><img src="/image/leetcode150.png" alt="leetcode150.png"></p>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="algorithm" scheme="https://blog.pwxcoo.com/tags/algorithm/"/>
    
      <category term="leetcode" scheme="https://blog.pwxcoo.com/tags/leetcode/"/>
    
  </entry>
  
  <entry>
    <title>《汇编语言》检测点笔记</title>
    <link href="https://blog.pwxcoo.com/2018/01/25/%E3%80%8A%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E3%80%8B%E6%A3%80%E6%B5%8B%E7%82%B9%E7%AC%94%E8%AE%B0/"/>
    <id>https://blog.pwxcoo.com/2018/01/25/《汇编语言》检测点笔记/</id>
    <published>2018-01-25T10:22:03.000Z</published>
    <updated>2022-03-24T05:48:48.873Z</updated>
    
    <content type="html"><![CDATA[<p>之前一直觉得《汇编语言》很秀。Assembly Language 阿。本来下个学期选修想选那门“微机接口和汇编语言”。但这个学期以为要挂的选修课，老师还是给了面子。所以学分讲道理是够了。斟酌了一下，决定自己看书爽一爽《汇编》。</p><p>果断选择了王爽的《汇编语言 (第三版) 》。</p><p>因为是有习题的，所以把习题就顺便记录了下来。</p><p>前面 11 章的答案我都 debug 验证过了，网上的答案有很多错误。从 12 章开始，我开始瞎屁股浏览了。主要是因为前面 11 章的内容让我比较喜欢，后面的较多的理论元素和一些指令完善，我就粗略的学习了。</p><h2 id="检测点1-1"><a href="#检测点1-1" class="headerlink" title="检测点1.1"></a>检测点1.1</h2><ol><li>1个 CPU 的寻址能力为 8KB，那么它的地址总线的宽度为 <code>13</code>。  (8KB = 8 * 1024 = 2 ^ 13。 1字节为一个存储单元)</li><li>1KB 的存储器有 <code>1024</code>个存储单元。存储单元的编号从 <code>0</code> 到 <code>1023</code>。</li><li>1KB 的存储器可以存储 <code>2^13</code>个bit，个 <code>1024</code>Byte。</li><li>1GB, 1MB, 1KB 分别是 <code>2^30</code>, <code>2^20</code>, <code>2^10</code>Byte。</li><li>8080，8088，80286，80386 的地址总线宽度分别为 16 根， 20 根， 24 根， 32 根，则它们的寻址能力分别为:  <code>2^6</code> (KB) , <code>1</code>(MB), <code>16</code>(MB), <code>4</code>(GB)。</li><li>8080， 8088， 8086， 80286， 80386 的数据总线宽度分别为 8 根， 8 根， 16 根， 16 根， 32 根。则它们一次可以传送的数据为:  <code>1</code> (B), <code>1</code>(B), <code>2</code>(B), <code>2</code>(B), <code>4</code>(B)。</li><li>从内存中读取 1024 字节的数据， 8086 至少要读 <code>512</code> 次， 80386至少要读 <code>256</code> 次。</li><li>在存储器中， 数据和程序以 <code>二进制</code>形式存放。</li></ol><h2 id="检测点2-1"><a href="#检测点2-1" class="headerlink" title="检测点2.1"></a>检测点2.1</h2><ol><li><p>写出每条汇编指令执行后相关寄存器中的值。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">mov ax,62627        ; AX = `F4A3H`</span><br><span class="line">mov ah,31H          ; AX = `31A3H`</span><br><span class="line">mov al,23H          ; AX = `3123H`</span><br><span class="line">add ax,ax           ; AX = `6246H`</span><br><span class="line">mov bx,826CH        ; BX = `826CH`</span><br><span class="line">mov cx,ax           ; CX = `6246H`</span><br><span class="line">mov ax,bx           ; AX = `826CH`</span><br><span class="line">add ax,bx           ; AX = `04D8H`</span><br><span class="line">mov al,bh           ; AX = `0482H`</span><br><span class="line">mov ah,bl           ; AX = `6C82H`</span><br><span class="line">add ah,ah           ; AX = `D882H`</span><br><span class="line">add al,6            ; AX = `D888H`</span><br><span class="line">add al,al           ; Ax = `D810H`</span><br><span class="line">mov ax,cx           ; AX = `6246H`</span><br></pre></td></tr></table></figure></li><li><p>用目前学过的汇编指令，最多使用 4 条指令，编程计算 2 的 4 次方。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mov ax,2</span><br><span class="line">add ax,ax</span><br><span class="line">add ax,ax</span><br><span class="line">add ax,ax</span><br></pre></td></tr></table></figure></li></ol><h2 id="检测点2-2"><a href="#检测点2-2" class="headerlink" title="检测点2.2"></a>检测点2.2</h2><ol><li>给定段地址为 0001H, 仅通过变化偏移地址寻址，CPU 的寻址范围为 <code>00010H</code>到 <code>1000FH</code>。</li><li>有一数据存放在内存 20000H 单元中，现给定段地址为 SA，若想用偏移地址寻到此单元。则 SA应满足的条件是: 最小为 <code>1001H</code>，最大为 <code>20000H</code>。</li></ol><h2 id="检测点2-3"><a href="#检测点2-3" class="headerlink" title="检测点2.3"></a>检测点2.3</h2><p>下面的三条指令执行后，CPU几次修改IP？都是在什么时候？最后IP中的值是多少？<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mov ax,bx</span><br><span class="line">sub ax,ax</span><br><span class="line">jmp ax</span><br></pre></td></tr></table></figure><br><code>4次。每条执行完都修改一次加上一次 jmp 的修改。0。</code></p><h2 id="检测点3-1"><a href="#检测点3-1" class="headerlink" title="检测点3.1"></a>检测点3.1</h2><ol><li>在Debug中， 用“d 0:0 1f” 查看内存，结果如下:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">0000:0000   70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60</span><br><span class="line">0000:0010   62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88</span><br></pre></td></tr></table></figure>下面的程序执行前，AX=0,BX=0,写出每条汇编指令执行完后相关寄存器中的值。</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">mov ax,1</span><br><span class="line">mov ds,ax         ; 将段地址ds设置为0001</span><br><span class="line">mov ax,[0000]     ; AX=`2662H`</span><br><span class="line">mov bx,[0001]     ; BX=`E626H`</span><br><span class="line">mov ax,bx         ; AX=`E626H`</span><br><span class="line">mov ax,[0000]     ; AX=`2662H`</span><br><span class="line">mov bx,[0002]     ; BX=`D6E6H`</span><br><span class="line">add ax,bx         ; AX=`FD48H`</span><br><span class="line">add ax,[0004]     ; AX=`2C14H`</span><br><span class="line">mov ax,0          ; AX=`0000H`</span><br><span class="line">mov al,[0002]     ; AX=`00E6H`</span><br><span class="line">mov bx,0          ; BX=`0000H`</span><br><span class="line">mov bl,[000C]     ; BX=`0026H`</span><br><span class="line">add al,bl         ; AX=`000CH`</span><br></pre></td></tr></table></figure><ol start="2"><li>内存中的情况如图3.6所示。<br>各寄存器的初始值: CS=2000H， IP=0, DS=1000H, AX=0, BX=0;<br><img src="/image/图3.6.png" alt="图3.6.png"></li></ol><ul><li>写出CPU执行的指令序列 (用汇编指令写出)</li><li>写出CPU执行每条指令后，CS，IP和相关寄存器中的数值。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mov ax,6622H    ; CS=2000H, IP=0003H, DS=1000H, AX=6622H, BX=0000H</span><br><span class="line">jmp 0ff0:0100   ; CS=0FF0H, IP=0100H, DS=1000H, Ax=6622H, BX=0000H</span><br><span class="line">mov ax,2000H    ; CS=0FF0H, IP=0103H, DS=1000H, AX=2000H, BX=0000H</span><br><span class="line">mov ds,ax       ; CS=0FF0H, IP=0105H, DS=2000H, Ax=2000H, BX=0000H</span><br><span class="line">mov ax,[0008]   ; CS=0FF0H, IP=0108H, DS=2000H, AX=C389H, BX=0000H</span><br><span class="line">mov ax,[0002]   ; CS=0FF0H, IP=010BH, Ds=2000H, Ax=EA66H, BX=0000H</span><br></pre></td></tr></table></figure></li><li>再次体会: 数据和程序有区别吗？如何确定内存中的信息哪些是数据，哪些是程序?</li></ul><p><code>没有区别。存储在数据段中就是数据，存储在程序段中就是程序。</code></p><h2 id="检测点3-2"><a href="#检测点3-2" class="headerlink" title="检测点3.2"></a>检测点3.2</h2><ol><li><p>补全下面的程序，使其可以将 10000H~1000FH 中的八个字，逆序复制到 20000H~2000FH 中。逆序复制的含义如图3.17所示 (图中内存里的数据均为假设) 。 (图我就不贴了。不重要)</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">mov ax,1000H</span><br><span class="line">mov ds,ax</span><br><span class="line"></span><br><span class="line">mov ax,2000H</span><br><span class="line">mov ss,ax</span><br><span class="line">mov sp,0</span><br><span class="line"></span><br><span class="line">push [0]</span><br><span class="line">push [2]</span><br><span class="line">push [4]</span><br><span class="line">push [6]</span><br><span class="line">push [8]</span><br><span class="line">push [A]</span><br><span class="line">push [C]</span><br><span class="line">push [E]</span><br></pre></td></tr></table></figure></li><li><p>补全下面的程序，使其可以将 10000H~1000FH 中的 8 个字，逆序复制到20000H~2000FH中。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">mov ax,2000H</span><br><span class="line">mov ds,ax</span><br><span class="line"></span><br><span class="line">mov ax,1000H</span><br><span class="line">mov ss,ax</span><br><span class="line">mov sp,0</span><br><span class="line"></span><br><span class="line">pop [E]</span><br><span class="line">pop [C]</span><br><span class="line">pop [A]</span><br><span class="line">pop [8]</span><br><span class="line">pop [6]</span><br><span class="line">pop [4]</span><br><span class="line">pop [2]</span><br><span class="line">pop [0]</span><br></pre></td></tr></table></figure><h2 id="检测点6-1"><a href="#检测点6-1" class="headerlink" title="检测点6.1"></a>检测点6.1</h2></li><li>下面的程序实现依次用内存 0:0~0:15 单元中的内容改写程序中数据，完成程序:</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">assume cs:codesg</span><br><span class="line"></span><br><span class="line">codesg segment</span><br><span class="line">    dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h</span><br><span class="line"></span><br><span class="line">start:  mov ax,0</span><br><span class="line">        mov ds,ax</span><br><span class="line">        mov bx,0</span><br><span class="line"></span><br><span class="line">        mov cx,8</span><br><span class="line">    s:  mov ax,[bx]</span><br><span class="line"></span><br><span class="line">        mov cs:[bx],ax</span><br><span class="line"></span><br><span class="line">        add bx,2</span><br><span class="line">        loop s</span><br><span class="line"></span><br><span class="line">        mov ax,4c00h</span><br><span class="line">        int 21h</span><br><span class="line">codesg ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure><ol start="2"><li>下面的程序实现依次用内存 0:0~0:15 单元中的内容改写程序中数据，数据的传送用栈来进行。栈空间设置在程序内。完成程序:</li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">assume cs:codesg</span><br><span class="line"></span><br><span class="line">codesg segment</span><br><span class="line">    dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h</span><br><span class="line">    dw 0,0,0,0,0,0,0,0,0,0              ;10个字单元用作栈空间</span><br><span class="line"></span><br><span class="line">start:  mov ax,cs</span><br><span class="line">        mov ss,ax</span><br><span class="line">        mov sp,36   ; (网上的答案不是36，虽然跑起来没问题，但36才是最好的)</span><br><span class="line"></span><br><span class="line">        mov ax,0</span><br><span class="line">        mov ds,ax</span><br><span class="line">        mov bx,0</span><br><span class="line">        mov cx,8</span><br><span class="line">    s:  push [bx]</span><br><span class="line"></span><br><span class="line">        pop cs:[bx]</span><br><span class="line"></span><br><span class="line">        add bx,2</span><br><span class="line">        loop s</span><br><span class="line"></span><br><span class="line">        mov ax,4c00h</span><br><span class="line">        int 21h</span><br><span class="line">codesg ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure><p><code>ps:我调试的时候发现一件很骚的事情，就是调试的时候，栈空间的值会发生改变。主要是因为中断机制，中断的时候某些关键值要存在内存里。。就用了程序的栈空间。我还纳闷了半天。。</code></p><h2 id="实验5"><a href="#实验5" class="headerlink" title="实验5"></a>实验5</h2><p>这个实验做完主要有两点总结:</p><ul><li><p>第二题的第4小道:  <code>16 * (N / 16 + 1)</code>。关于内存的问题。在16位机器上，不足16字节的数据按16字节存储，意思就是17个字节，在程序中其实占的是32字节。其它机器应该可以递推。</p></li><li><p>编译器编译汇编文件，从上往下编译。同理，数据段要是写在代码段后面，执行加载到内存中时，在内存中的位置也在代码段的后面。</p></li></ul><h2 id="检测点9-1"><a href="#检测点9-1" class="headerlink" title="检测点9.1"></a>检测点9.1</h2><ol><li>程序如下:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line"></span><br><span class="line">data segment</span><br><span class="line">    db 8 dup(0)</span><br><span class="line">data ends</span><br><span class="line"></span><br><span class="line">code segment</span><br><span class="line">    start:  mov ax,data</span><br><span class="line">            mov ds,ax</span><br><span class="line">            mov bx,0</span><br><span class="line">            jmp word ptr [bx + 1]</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></li></ol><p>若要使程序中的 jmp 指令执行后， CS:IP 指向程序的第一条指令，在 data 段中应该定义哪些数据？<br><code></code></p><ol start="2"><li><p>程序如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line"></span><br><span class="line">data segment</span><br><span class="line">    dd 12345678H</span><br><span class="line">data ends</span><br><span class="line"></span><br><span class="line">code segment</span><br><span class="line"></span><br><span class="line">    start:  mov ax,data</span><br><span class="line">            mov ds,ax</span><br><span class="line">            mov bx,0</span><br><span class="line">            mov [bx],offset start</span><br><span class="line">            mov [bx+2],cs</span><br><span class="line">            jmp dword ptr ds:[0]</span><br><span class="line">code ends</span><br><span class="line"></span><br><span class="line">end start</span><br></pre></td></tr></table></figure><p>补全程序，使 jmp 指令执行后，CS: IP 指向程序的第一条指令。</p></li><li><p>用 Debug 查看内存，结果如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2000:1000 BE 00 06 00 00 00 ...</span><br></pre></td></tr></table></figure><p>则此时，CPU 执行指令:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mov ax,2000H</span><br><span class="line">mov es,ax</span><br><span class="line">jmp dword ptr es:[1000H]</span><br></pre></td></tr></table></figure><p>后， (CS) = <code>0060h</code>， (IP) = <code>00BE</code></p></li></ol><h2 id="检测点9-2"><a href="#检测点9-2" class="headerlink" title="检测点9.2"></a>检测点9.2</h2><p>补全程序，利用 jcxz 指令，实现在内存 2000H 段中查找第一个值为 0 的字节，找到后，将它的偏移地址存储在 dx 中。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">code segment</span><br><span class="line">    start:  mov ax,2000H</span><br><span class="line">            mov ds,ax</span><br><span class="line">            mov bx,0</span><br><span class="line"></span><br><span class="line">        s:  mov cl,[bx]</span><br><span class="line">            mov ch,0    ; 注意寻找的是字节</span><br><span class="line">            jcxz short ok</span><br><span class="line">            inc bx</span><br><span class="line"></span><br><span class="line">            jmp short s</span><br><span class="line">        ok: mov dx,bx</span><br><span class="line">            mov ax,4c00h</span><br><span class="line">            int 21h</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></p><h2 id="检测点9-3"><a href="#检测点9-3" class="headerlink" title="检测点9.3"></a>检测点9.3</h2><p>补全编程，利用 loop 指令，实现在内存 2000H 段中查找第一个值为 0 的字节， 找到后，将它的偏移地址存储在 dx 中。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">code segment</span><br><span class="line">    start:  mov ax,2000H</span><br><span class="line">            mov ds,ax</span><br><span class="line">            mov bx,0</span><br><span class="line">        s:  mov cl,[bx]</span><br><span class="line">            mov ch,0</span><br><span class="line"></span><br><span class="line">            inc cx  ; loop指令，将 cx 加一在判定是否为0</span><br><span class="line"></span><br><span class="line">            inc bx</span><br><span class="line">            loop s</span><br><span class="line">        ok: dec bx</span><br><span class="line">            mov dx,bx</span><br><span class="line">            mov ax,4c00h</span><br><span class="line">            int 21h</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></p><h2 id="检测点10-1"><a href="#检测点10-1" class="headerlink" title="检测点10.1"></a>检测点10.1</h2><p>补全程序，实现从内存 1000:0000 处开始执行指令<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line"></span><br><span class="line">stack segment</span><br><span class="line">    db 16 dup(0)</span><br><span class="line">stack ends</span><br><span class="line"></span><br><span class="line">code segment</span><br><span class="line">start:  mov ax,stack</span><br><span class="line">        mov ss,ax</span><br><span class="line">        mov sp,16</span><br><span class="line">        mov ax,1000</span><br><span class="line">        push ax</span><br><span class="line">        mov ax,0000</span><br><span class="line">        push ax</span><br><span class="line">        retf</span><br><span class="line">code ends</span><br><span class="line"></span><br><span class="line">end start</span><br></pre></td></tr></table></figure></p><h2 id="检测点10-2"><a href="#检测点10-2" class="headerlink" title="检测点10.2"></a>检测点10.2</h2><p>下面的程序执行后，ax 中的数值为多少？<br>内存地址 | 机器码   | 汇编指令<br>—|—|—<br>1000:0 | b8 00 00 |   mov ax,0<br>1000:3 | e8 01 00 |   call s<br>1000:6 | 40       |   inc ax<br>1000:8 | 58       | s:pop ax</p><p><code>0006h</code></p><h2 id="检测点10-3"><a href="#检测点10-3" class="headerlink" title="检测点10.3"></a>检测点10.3</h2><p>下面的程序执行后，ax 中的数值为多少？<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">内存地址 | 机器码   | 汇编指令</span><br><span class="line">---|---|---</span><br><span class="line">1000:0 | b8 00 00       |   mov ax,0</span><br><span class="line">1000:3 | 9A 09 00 00 10 |   call far ptr s</span><br><span class="line">1000:8 | 40             |   inc ax</span><br><span class="line">1000:9 | 58             | s:pop ax</span><br><span class="line">                            add ax,ax</span><br><span class="line">                            pop bx</span><br><span class="line">                            add ax,bx</span><br></pre></td></tr></table></figure></p><p><code>1010h</code></p><h2 id="检测点10-4"><a href="#检测点10-4" class="headerlink" title="检测点10.4"></a>检测点10.4</h2><p>下面的程序执行后，ax 中的数值为多少？<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">内存地址 | 机器码   | 汇编指令</span><br><span class="line">---|---|---</span><br><span class="line">1000:0 | b8 06 00       |   mov ax,6</span><br><span class="line">1000:3 | ff d0          |   call ax</span><br><span class="line">1000:5 | 40             |   inc ax</span><br><span class="line">1000:6 |                |   mov bp,sp</span><br><span class="line">                            add ax,[bp]</span><br></pre></td></tr></table></figure></p><p><code>000Bh</code></p><h2 id="检测点10-5"><a href="#检测点10-5" class="headerlink" title="检测点10.5"></a>检测点10.5</h2><ol><li>下面的程序执行后，ax 中的数值为多少？ (注意: 用 call 指令的原理来分析，不要在 Debug 中单步跟踪来验证你的结论。对于此程序，在Debug中单步跟踪的结果，不能代表 CPU 的实际执行结果。)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">stack segment</span><br><span class="line">    dw 8 dup(0)</span><br><span class="line">stack segment</span><br><span class="line">code segment</span><br><span class="line">    start:  mov ax,stack</span><br><span class="line">            mov ss,ax</span><br><span class="line">            mov sp,16</span><br><span class="line">            mov ds,ax</span><br><span class="line">            mov ax,0</span><br><span class="line">            call word ptr ds:[0EH]</span><br><span class="line">            inc ax</span><br><span class="line">            inc ax</span><br><span class="line">            inc ax</span><br><span class="line">            mov ax,4c00h</span><br><span class="line">            int 21h</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></li></ol><p><code>0003h</code></p><ol start="2"><li>下面的程序执行后，ax 和 bx 中的数值为多少？<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">data segment</span><br><span class="line">    dw 8 dup(0)</span><br><span class="line">data ends</span><br><span class="line">code segment</span><br><span class="line">    start:  mov ax,data</span><br><span class="line">            mov ss,ax</span><br><span class="line">            mov sp,16</span><br><span class="line">            mov word ptr ss:[0],offset s</span><br><span class="line">            mov ss:[2],cs</span><br><span class="line">            call dword ptr ss:[0]</span><br><span class="line">            nop</span><br><span class="line">        s:  mov ax,offset s</span><br><span class="line">            sub ax,ss:[0cH]</span><br><span class="line">            mov bx,cs</span><br><span class="line">            sub bx,ss:[0eH]</span><br><span class="line">            mov ax,4c00h</span><br><span class="line">            int 21h</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></li></ol><p>ax = <code>0001h</code>, bx = <code>0000h</code></p><h2 id="检测点11-1"><a href="#检测点11-1" class="headerlink" title="检测点11.1"></a>检测点11.1</h2><p>写出下面每条指令执行后，ZF，PF，SF等标志位的值。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">sub al,al           ; ZF= 1 ,PF= 1  ,SF= 0</span><br><span class="line">mov al,1            ; ZF= 1 ,PF= 1  ,SF= 0</span><br><span class="line">push ax             ; ZF= 1 ,PF= 1  ,SF= 0</span><br><span class="line">pop bx              ; ZF= 1 ,PF= 1  ,SF= 0</span><br><span class="line">add al,bl           ; ZF= 0 ,PF= 0  ,SF= 0</span><br><span class="line">add al,10           ; ZF= 0 ,PF= 1  ,SF= 0</span><br><span class="line">mul al              ; ZF= 0 ,PF= 1  ,SF= 0</span><br></pre></td></tr></table></figure></p><h2 id="检测点11-2"><a href="#检测点11-2" class="headerlink" title="检测点11.2"></a>检测点11.2</h2><p>写出下面每条指令执行后，ZF，PF，SF，CF，OF等标志位的值。</p><table><thead><tr><th>汇编代码</th><th>CF</th><th>OF</th><th>SF</th><th>ZF</th><th>PF</th></tr></thead><tbody><tr><td>sub al,al</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td></tr><tr><td>mov al,10H</td><td>0</td><td>0</td><td>0</td><td>1</td><td>1</td></tr><tr><td>add al,90H</td><td>0</td><td>0</td><td>1</td><td>0</td><td>0</td></tr><tr><td>mov al,80H</td><td>0</td><td>0</td><td>1</td><td>0</td><td>0</td></tr><tr><td>add al,80H</td><td>1</td><td>1</td><td>0</td><td>1</td><td>1</td></tr><tr><td>mov al,0FCH</td><td>1</td><td>1</td><td>0</td><td>1</td><td>1</td></tr><tr><td>add al,05H</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>mov al,7DH</td><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td></tr><tr><td>add al,0BH</td><td>0</td><td>1</td><td>1</td><td>0</td><td>1</td></tr></tbody></table><h2 id="检测点11-3"><a href="#检测点11-3" class="headerlink" title="检测点11.3"></a>检测点11.3</h2><ol><li>补全下面的程序，统计 F:000:0 处 32 个字节中，大小在[32,128]的数据的个数。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">mov ax,0f000h</span><br><span class="line">mov ds,ax</span><br><span class="line"></span><br><span class="line">mov bx,0</span><br><span class="line">mov dx,0</span><br><span class="line">mov cx,32</span><br><span class="line">s:  mov al,[bx]</span><br><span class="line">    cmp al,32</span><br><span class="line">    jb s0</span><br><span class="line">    cmp al,128</span><br><span class="line">    ja s0</span><br><span class="line">    inc dx</span><br><span class="line">s0: inc bx</span><br><span class="line">    loop s</span><br></pre></td></tr></table></figure></li><li>补全下面程序中，统计 F:000:0 处 32 个字节中，大小在 (32,128) 的数据的个数。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">mov ax,0f000h</span><br><span class="line">mov ds,ax</span><br><span class="line">mov bx,0</span><br><span class="line">mov dx,0</span><br><span class="line">mov cx,32</span><br><span class="line">s:  mov al,[bx]</span><br><span class="line">    cmp al,32</span><br><span class="line">    jna s0</span><br><span class="line">    cmp al,128</span><br><span class="line">    jnb s0</span><br><span class="line">    inc dx</span><br><span class="line">s0: inc bx</span><br><span class="line">    loop s</span><br></pre></td></tr></table></figure></li></ol><h2 id="检测点11-4"><a href="#检测点11-4" class="headerlink" title="检测点11.4"></a>检测点11.4</h2><p>下面的程序执行后:  (ax) =？<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mov ax,0</span><br><span class="line">push ax</span><br><span class="line">popf</span><br><span class="line">mov ax,0fff0h</span><br><span class="line">add ax,0010h</span><br><span class="line">pushf</span><br><span class="line">pop ax</span><br><span class="line">and al,11000101B</span><br><span class="line">and ah,00001000B</span><br></pre></td></tr></table></figure></p><p><code>0045h</code></p><h2 id="检测点12-1"><a href="#检测点12-1" class="headerlink" title="检测点12.1"></a>检测点12.1</h2><ol><li><p>用 Debug 查看内存，情况如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0000:0000 68 10 A7 00 8B 01 70 00-16 00 9D 03 8B 01 70 00</span><br></pre></td></tr></table></figure><p>则 3 号中断源对应的中断处理程序的入口地址为: <code>0070h:018Bh</code></p></li><li><p>存储 N 号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为: <code>[4N]</code>。<br>存储 N 号中断源对应的中断处理程序入口的段地址的内存单元的地址为: <code>[4N+2]</code>。</p></li></ol><h2 id="检测点13-1"><a href="#检测点13-1" class="headerlink" title="检测点13.1"></a>检测点13.1</h2><ol><li><p>在上面的内容中，我们用 7ch 中断例程实现 loop 的功能，则上面的 7ch 中断例程所能进行的最大转移位移是多少？<br><code>-128~127</code></p></li><li><p>用 7ch 中断例程完成 jmp near ptr s 指令的功能，用 bx 向中断例程传送转移位移。<br>应用举例: 在屏幕的第 12 行，显示 data 段中以 0 结尾的字符串。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code,ds:data</span><br><span class="line">    data segment</span><br><span class="line">        db &#x27;conversation&#x27;,0</span><br><span class="line">    data ends</span><br><span class="line"></span><br><span class="line">    code segment</span><br><span class="line">start:    mov ax,cs</span><br><span class="line">    mov ds,ax</span><br><span class="line">    mov si,offset show</span><br><span class="line">    mov ax,0</span><br><span class="line">    mov es,ax</span><br><span class="line">    mov di,200h</span><br><span class="line">    mov cx,offset showend-offset show</span><br><span class="line">    cld</span><br><span class="line">    rep movsb</span><br><span class="line"></span><br><span class="line">    mov ax,0</span><br><span class="line">    mov es,ax</span><br><span class="line">    mov word ptr es:[7ch*4],200h</span><br><span class="line">    mov word ptr es:[7ch*4+2],0</span><br><span class="line"></span><br><span class="line">    mov ax,data</span><br><span class="line">    mov ds,ax</span><br><span class="line">    mov si,0</span><br><span class="line">    mov ax,0b800h</span><br><span class="line">    mov es,ax</span><br><span class="line">    mov di,12*160</span><br><span class="line"></span><br><span class="line">s:    cmp byte ptr [si],0</span><br><span class="line">    je ok</span><br><span class="line">    mov al,[si]</span><br><span class="line">    mov es:[di],al</span><br><span class="line">    mov al,2</span><br><span class="line">    mov es:[di+1],al</span><br><span class="line">    inc si</span><br><span class="line">    add di,2</span><br><span class="line">    mov bx,offset s-offset ok</span><br><span class="line">    int 7ch</span><br><span class="line"></span><br><span class="line">ok:    mov ax,4c00h</span><br><span class="line">    int 21h</span><br><span class="line">show:    push bp</span><br><span class="line">    mov bp,sp</span><br><span class="line">    add [bp+2],bx</span><br><span class="line">    pop bp</span><br><span class="line">    iret</span><br><span class="line">showend:nop</span><br><span class="line">    code ends</span><br><span class="line">    end start</span><br></pre></td></tr></table></figure></li></ol><h2 id="检测点13-2"><a href="#检测点13-2" class="headerlink" title="检测点13.2"></a>检测点13.2</h2><ol><li><code>错误，BIOS在ROM中 (READ ONLY) ，不能被更改</code></li><li><code>错误，19h中断，操作系统还未被引导。</code></li></ol><h2 id="检测点14-1"><a href="#检测点14-1" class="headerlink" title="检测点14.1"></a>检测点14.1</h2><ol><li><p>编程，读取 CMOS RAM 的 2 号单元的内容</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mov al,2</span><br><span class="line">out 70h,al</span><br><span class="line">in al,71h</span><br></pre></td></tr></table></figure></li><li><p>编程，向 CMOS RAM 的 2 号单元写入 0。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">mov al,0</span><br><span class="line">out 71h,al</span><br><span class="line">mov al,2</span><br><span class="line">in 70h,al</span><br></pre></td></tr></table></figure></li></ol><h2 id="检测点14-2"><a href="#检测点14-2" class="headerlink" title="检测点14.2"></a>检测点14.2</h2><p>编程，用加法和移位指令计算 (ax) = (ax) <em> 10。 (提示， (ax) </em>10= (ax)<em>2+(ax)</em>8)<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mov ax,2</span><br><span class="line">shl ax,1</span><br><span class="line">mov bx,ax</span><br><span class="line">shl ax,1</span><br><span class="line">shl ax,1</span><br><span class="line">add ax,bx</span><br></pre></td></tr></table></figure></p><h2 id="检测点15-1"><a href="#检测点15-1" class="headerlink" title="检测点15.1"></a>检测点15.1</h2><ol><li>精简<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pushf</span><br><span class="line">call dword ptr ds:[0]</span><br></pre></td></tr></table></figure>2.<br><code>在中断向量表中设置新的int9中断入口地址的时候不让其发生中断。</code><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">cli</span><br><span class="line">mov word ptr es:[9*4],offset int9</span><br><span class="line">mov word ptr es:[9*4+2],cs</span><br><span class="line">sti</span><br></pre></td></tr></table></figure></li></ol><p><code>恢复中断向量表int9的源地址时同理:</code><br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">cli</span><br><span class="line">push ds:[0]</span><br><span class="line">pop es:[9*4]</span><br><span class="line">push ds:[2]</span><br><span class="line">pop es:[9*4+2]</span><br><span class="line">sti</span><br></pre></td></tr></table></figure></p><h2 id="检测点16-1"><a href="#检测点16-1" class="headerlink" title="检测点16.1"></a>检测点16.1</h2><p>下面的程序将 code 段中 a 处的 8 个数据累加，结果存储到 b 处的双字中，补全程序<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code</span><br><span class="line">code segment</span><br><span class="line">    a dw 1,2,3,4,5,6,7,8</span><br><span class="line">    b dd 0</span><br><span class="line"></span><br><span class="line">    start:  mov si,0</span><br><span class="line">            mov cx,8</span><br><span class="line">        s:  mov ax,a[si]</span><br><span class="line">            add b,ax</span><br><span class="line">            abc b,0</span><br><span class="line">            add si,1</span><br><span class="line">            loop s</span><br><span class="line"></span><br><span class="line">            mov ax,4c00h</span><br><span class="line">            int 21h</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></p><h2 id="检测点16-2"><a href="#检测点16-2" class="headerlink" title="检测点16.2"></a>检测点16.2</h2><p>下面的程序将 data 段中 a 处的 8 个数据累加，结果存储到 b 处的双字中，补全程序<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">assume cs:code,es:data</span><br><span class="line"></span><br><span class="line">data segment</span><br><span class="line">    a db 1,2,3,4,5,6,7,8</span><br><span class="line">    b dw 0</span><br><span class="line">data ends</span><br><span class="line"></span><br><span class="line">code segment</span><br><span class="line">start:  mov ax,data</span><br><span class="line">        mov es,ax</span><br><span class="line">        mov si,0</span><br><span class="line">        mov cx,8</span><br><span class="line">    s:  mov al,a[si]</span><br><span class="line">        mov ah,0</span><br><span class="line">        add b,ax</span><br><span class="line">        inc si</span><br><span class="line">        loop s</span><br><span class="line"></span><br><span class="line">        mov ax,4c00h</span><br><span class="line">        int 21h</span><br><span class="line">code ends</span><br><span class="line">end start</span><br></pre></td></tr></table></figure></p><h2 id="检测点17-1"><a href="#检测点17-1" class="headerlink" title="检测点17.1"></a>检测点17.1</h2><p><code>当执行int16中断时是从缓冲区中读出字符，若缓冲区为空，则int16应该可以响应int9的中断，故IF不一定为1</code></p>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="assembly" scheme="https://blog.pwxcoo.com/tags/assembly/"/>
    
  </entry>
  
  <entry>
    <title>ubuntu配置日志</title>
    <link href="https://blog.pwxcoo.com/2018/01/04/ubuntu%E9%85%8D%E7%BD%AE%E6%97%A5%E5%BF%97/"/>
    <id>https://blog.pwxcoo.com/2018/01/04/ubuntu配置日志/</id>
    <published>2018-01-04T10:22:00.000Z</published>
    <updated>2022-03-24T05:48:48.871Z</updated>
    
    <content type="html"><![CDATA[<h2 id="firefox"><a href="#firefox" class="headerlink" title="firefox"></a>firefox</h2><ul><li><code>sudo apt update</code></li><li><code>sudo apt install firefox</code></li></ul><h2 id="chrome"><a href="#chrome" class="headerlink" title="chrome"></a>chrome</h2><ul><li>添加软件源 <code>sudo wget http://www.linuxidc.com/files/repo/google-chrome.list -P /etc/apt/sources.list.d/</code></li><li>导入谷歌软件公钥 <code>wget -q -O - https://dl.google.com/linux/linux_signing_key.pub  | sudo apt-key add -</code></li><li><code>sudo apt update</code></li><li><code>sudo apt-get install google-chrome-stable</code></li><li><code>/usr/bin/google-chrome-stable</code></li></ul><h2 id="vim"><a href="#vim" class="headerlink" title="vim"></a>vim</h2><ul><li><code>sudo apt install vim</code></li><li><code>sudo vim /etc/vim/vimrc</code></li><li>取消<code>syntax on</code>的注释</li><li>最后一行配置:</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">set nu          //在左侧显示行号码</span><br><span class="line">set tabstop=4   //tab 长度设为4</span><br><span class="line">set nobackup    //覆盖文件不备份</span><br><span class="line">set cursorline  //突出显示当前行</span><br><span class="line">set ruler       //在右下角显示光标位置打状态行</span><br><span class="line">set autoindent  //自动缩进</span><br></pre></td></tr></table></figure><h2 id="git"><a href="#git" class="headerlink" title="git"></a>git</h2><ul><li><code>sudo apt install git</code></li><li><code>git config --global user.name &quot;your name&quot;</code></li><li><code>git config --globa user.email &quot;your email.com&quot;</code></li><li><code>ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;</code></li><li>复制公钥到github上</li></ul><h2 id="vscode"><a href="#vscode" class="headerlink" title="vscode"></a>vscode</h2><h3 id="用-ubuntu-make-安装"><a href="#用-ubuntu-make-安装" class="headerlink" title="用 ubuntu-make 安装"></a>用 ubuntu-make 安装</h3><ul><li><code>sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make</code></li><li><code>sudo apt update</code></li><li><code>sudo apt install ubuntu-make</code></li><li><code>sudo umake ide visual-studio-code</code></li></ul><h3 id="apt-安装"><a href="#apt-安装" class="headerlink" title="apt 安装"></a><a href="https://linuxize.com/post/how-to-install-visual-studio-code-on-ubuntu-18-04/">apt 安装</a></h3><ul><li><code>sudo apt update</code></li><li><code>sudo apt install software-properties-common apt-transport-https wget</code></li><li><code>wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -</code></li><li><code>sudo add-apt-repository &quot;deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main&quot;</code></li><li><code>sudo apt install code</code></li></ul><p><strong>20181123 update: <code>sudo umake web visual-studio-code</code>，在新版本中 vscode 移动到 ide 分类中</strong></p><p><strong>PS: ubuntu 下的推荐字体 `font-family=”‘Ubuntu Mono’, monospace”</strong></p><h2 id="anaconda"><a href="#anaconda" class="headerlink" title="anaconda"></a>anaconda</h2><ul><li>官网下载</li><li><code>sudo bash Anaconda3-5.0.1-Linux-x86_64.sh</code> 然后选yes</li><li>添加环境路径</li><li><code>sudo chmod 777 -R anaconda3/</code> 给权限</li><li>jupyter的皮肤配置: jupyterthemes配置 <code>jt -t gruvboxd -f ubuntu -fs 12 -tfs 12 -ofs 11</code>, 我发现ubuntu的字体已经比window好太多，所以就没配置。</li></ul><h2 id="java"><a href="#java" class="headerlink" title="java"></a>java</h2><p>一件很奇怪的事情，就是不知道为什么我用apt的ppa源下载，一直连不上oracle官网。。一直404失败。所以就徒手配置了。</p><ul><li><code>apt -u dist-upgrade</code>强制升级一下版本</li><li><code>sudo apt remove --purge openjdk*</code>删除openjdk</li><li><code>sudo mkdir /usr/local/java</code> 创建安装目录 <code>cd /usr/local/java</code> 进入安装目录</li><li>因为服务器连不上oracle官网。。所以我用了rz把本地的jdk传上去了。。</li><li><code>tar -zxvf jdk-8u161-linux-x64.tar.gz</code> 解压</li><li><code>vim ~/.bashrc</code> 环境变量配置<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">export JAVA_HOME=/usr/local/java/jdk1.8.0_161</span><br><span class="line">export JRE_HOME=&#123;JAVA_HOME&#125;/jre</span><br><span class="line">export CLASSPATH=.:&#123;JAVA_HOME&#125;/lib:$&#123;JRE_HOME&#125;/lib</span><br><span class="line">export PATH=$JAVA_HOME/bin:$PATH</span><br></pre></td></tr></table></figure></li><li><code>source ~/.bashrc</code> 重新加载环境变量生效</li></ul><h2 id="node"><a href="#node" class="headerlink" title="node"></a>node</h2><ul><li><code>sudo apt install nodejs-legacy nodejs</code> 直接下载</li><li><code>sudo apt install npm</code> 安装npm</li><li><code>sudo npm install -g n</code> 下载node版本控制库</li><li><code>sudo n stable</code> 升级到最新的LTS</li></ul><h2 id="codeblocks"><a href="#codeblocks" class="headerlink" title="codeblocks"></a>codeblocks</h2><ul><li><code>sudo add-apt-repository ppa:damien-moore/codeblocks-stable</code> 添加ppa源</li><li><code>sudo apt update</code> 更新</li><li><code>sudo apt install codeblocks</code> 下载安装</li></ul><h2 id="coursera"><a href="#coursera" class="headerlink" title="coursera"></a>coursera</h2><ul><li><code>ping coursera.org</code> 得到ip</li><li>浏览器里看network，未正常响应的都添加到hosts里。</li><li><code>vim /etc/hosts</code> 修改</li><li><code>sudo /etc/init.d/networking restart</code>重启网络服务</li></ul><h2 id="shutter"><a href="#shutter" class="headerlink" title="shutter"></a>shutter</h2><ul><li><code>apt install shutter</code></li></ul><h2 id="system-theme"><a href="#system-theme" class="headerlink" title="system theme"></a>system theme</h2><ul><li><code>sudo  apt  install unity-tweak-tool</code> 下载 unity-tweak-tool</li><li><code>sudo add-apt-repository ppa:noobslab/themes</code> 添加源</li><li><code>sudo apt update</code> 更新</li><li><code>sudo apt install flatabulous-theme</code> 下载 flatabulous-themes</li><li><code>sudo apt install ultra-flat-icons</code> 下载 ultra-flat的图标</li></ul><hr><p><strong>Ubuntu18.04 用了 gnome，更改主题 <a href="https://blog.csdn.net/lishanleilixin/article/details/80453565">Ubuntu18.04美化主题</a></strong></p><ul><li><a href="https://www.gnome-look.org/">Gnome-LOOK</a></li></ul><h2 id="zsh"><a href="#zsh" class="headerlink" title="zsh"></a>zsh</h2><ul><li><code>sudo apt install zsh</code> 下载</li><li><code>chsh -s $(which zsh)</code> 设置</li><li>重启计算机生效</li><li><code>sh -c &quot;$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&quot;</code></li><li><code>git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions</code></li><li>改动 <code>~/.zshrc</code> 的 <code>plugins=(git)</code> 为 <code>plugins=(git zsh-autosuggestions)</code></li></ul><h2 id="shadowsocks"><a href="#shadowsocks" class="headerlink" title="shadowsocks"></a>shadowsocks</h2><ul><li><code>sudo apt install python-pip</code> 下载 pip</li><li><code>sudo pip install shadowsocks</code> 下载 shadowsocks</li><li><code>sudo vim  /etc/shadowsocks.json</code> 修改配置文件<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;server&quot;</span><span class="punctuation">:</span><span class="string">&quot;ip地址&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;server_port&quot;</span><span class="punctuation">:</span>服务器端口<span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;local_address&quot;</span><span class="punctuation">:</span> <span class="string">&quot;127.0.0.1&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;local_port&quot;</span><span class="punctuation">:</span><span class="number">1080</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span><span class="string">&quot;密码&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;timeout&quot;</span><span class="punctuation">:</span><span class="number">300</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;method&quot;</span><span class="punctuation">:</span><span class="string">&quot;使用的方法&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;fast_open&quot;</span><span class="punctuation">:</span> <span class="keyword">false</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;workers&quot;</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></li><li><code>sudo sslocal -c /etc/shadowsocks.json</code> 开中断启动 ss</li><li>更改系统设置 -&gt; 网络 -&gt; 网络代理 -&gt; Socks主机:  127.0.0.1 -&gt; port: 1080</li><li>给 chrome 下一个插件 SwitchyOmega(<a href="https://www.sundabao.com/ubuntu%E4%BD%BF%E7%94%A8shadowsocks/">SwitchyOmega 配置</a>)，选择 <code>autoProxy</code>，规则列表网址:<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt</span><br></pre></td></tr></table></figure></li><li>别的浏览器 firefox 的代理都关了，不然不开 ss 上不了网，需要 FQ 的时候再用终端开个 ss 。</li></ul><h3 id="开机启动"><a href="#开机启动" class="headerlink" title="开机启动"></a>开机启动</h3><ul><li><code>sudo vim /etc/systemd/system/shadowsocks.service</code></li><li>把 <code>/home/xx/Software/ShadowsocksConfig/shadowsocks.json</code> 修改为自己的 <code>shadowsocks.json</code> 路径  <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=Shadowsocks Client Service</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=simple</span><br><span class="line">User=root</span><br><span class="line">ExecStart=/usr/bin/sslocal -c /home/xx/Software/ShadowsocksConfig/shadowsocks.json</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure></li><li><code>systemctl enable /etc/systemd/system/shadowsocks.service</code> 重启生效</li></ul>]]></content>
    
    <summary type="html">
    
      
      
        
        
          
        
      
    
    </summary>
    
      <category term="memo" scheme="https://blog.pwxcoo.com/categories/memo/"/>
    
    
      <category term="memo" scheme="https://blog.pwxcoo.com/tags/memo/"/>
    
      <category term="linux" scheme="https://blog.pwxcoo.com/tags/linux/"/>
    
  </entry>
  
</feed>
