<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Snow Memory | Andrew Liu</title>
  <subtitle>Snow Memory, Do one thing, Do it best.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://andrewliu.in/"/>
  <updated>2017-06-17T23:22:51.000Z</updated>
  <id>http://andrewliu.in/</id>
  
  <author>
    <name>Andrew Liu</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Mac重装小计</title>
    <link href="http://andrewliu.in/2017/06/18/Mac%E9%87%8D%E8%A3%85%E5%B0%8F%E8%AE%A1/"/>
    <id>http://andrewliu.in/2017/06/18/Mac重装小计/</id>
    <published>2017-06-17T22:52:41.000Z</published>
    <updated>2017-06-17T23:22:51.000Z</updated>
    
    <content type="html"><![CDATA[<div class="tip"><br>    本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.<br></div>

<blockquote>
<p>很久没有更新博客了, 前段时间迷上了王者农药, 戒了农药后又重新入了暴雪爸爸<code>暗黑3</code>的大坑, 呵呵.<br>除此之外, 公司离职回来一直准备毕业论文、毕业答辩、毕业相关材料, 真是焦头烂额.<br>不过想想十九年求学生涯就要结束了, 即将AFK了, 简直幸福.</p>
</blockquote>
<p>为什么要重装? Mac系统的乱七八糟的东西已经占据了90%的磁盘空间, 无法减少文件保持一定空闲磁盘空间, 这种情况已经严重影响了我的日常工作.</p>
<p>重装方案严格按照 Apple 官方文档 <a href="https://support.apple.com/zh-cn/HT204904" target="_blank" rel="external">如何重新安装 macOS</a> 执行.</p>
<h2 id="系统偏好设置"><a href="#系统偏好设置" class="headerlink" title="系统偏好设置"></a>系统偏好设置</h2><ul>
<li>允许安装任何来源的APP: <code>安全性与隐私-&gt;通用</code>. 若无该选项，请命令行执行<code>sudo spctl --master-disable</code></li>
<li>设置快捷键: <code>键盘-&gt;快捷键</code> 更改输入法切换快捷键</li>
<li>设置触摸板: 选取全部触摸板设置</li>
<li>设置触发角: <code>桌面与屏幕保护程序-&gt;触发角</code></li>
<li><p>设置密码验证: <code>系统偏好设置-&gt;安全性与隐私-&gt;选择 进入休眠或屏保后立即要求输入密码</code></p>
</li>
<li><p>编辑 /etc/paths(<code>sudo vim /etc/paths</code>)</p>
</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">/usr/local/bin</div><div class="line">/usr/local/sbin</div><div class="line">/usr/bin</div><div class="line">/usr/sbin</div><div class="line">/bin</div><div class="line">/sbin</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="软件安装"><a href="#软件安装" class="headerlink" title="软件安装"></a>软件安装</h2><blockquote>
<p>若不需要Xcode可直接跳过该步骤, 安装Homebrew时同样会自动安装<code>Command Line Tools</code></p>
</blockquote>
<ul>
<li>通过App Store 安装Xcode</li>
<li>安装Command Line Tools</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ xcode-select --install</div></pre></td></tr></table></figure>
<h3 id="安装Homebrew"><a href="#安装Homebrew" class="headerlink" title="安装Homebrew"></a>安装<a href="http://brew.sh/" target="_blank" rel="external">Homebrew</a></h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ ruby -e <span class="string">"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"</span></div></pre></td></tr></table></figure>
<h3 id="安装Git"><a href="#安装Git" class="headerlink" title="安装Git"></a>安装Git</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$$ brew install git</div><div class="line"></div><div class="line"># SSH-KeyGen, 设置SSH密钥</div><div class="line">$ ssh-keygen -t rsa -C <span class="string">"your_email@youremail.com"</span> </div><div class="line"></div><div class="line"># 在Github中添加新生成的公钥, 验证是否成功请执行以下命令</div><div class="line">$ ssh -T git@github.com</div></pre></td></tr></table></figure>
<ul>
<li>参考<a href="http://andrewliu.in/2014/09/09/2014-09-09-Install-SSH-Use-Github/">Install-SSH-Use-Github</a></li>
</ul>
<h3 id="安装nodejs"><a href="#安装nodejs" class="headerlink" title="安装nodejs"></a>安装nodejs</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ brew install nodejs</div><div class="line"></div><div class="line"># 安装nvm或者n作为nodejs版本控制工具</div><div class="line">$ (sudo) npm install -g n</div><div class="line"># 如果不行, 则使用nvm进行版本控制</div><div class="line">$ n <span class="number">4.2</span><span class="number">.4</span></div></pre></td></tr></table></figure>
<ul>
<li><a href="https://npm.taobao.org/" target="_blank" rel="external">设置淘宝npm镜像</a></li>
</ul>
<h3 id="安装-Ruby"><a href="#安装-Ruby" class="headerlink" title="安装 Ruby"></a>安装 Ruby</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ brew install ruby</div><div class="line"></div><div class="line">$ rbenv install -l     <span class="meta"># list all available versions</span></div><div class="line">$ rbenv install <span class="number">2.2</span><span class="number">.1</span>  <span class="meta"># install a Ruby version</span></div><div class="line">$ rbenv global <span class="number">2.2</span><span class="number">.1</span>   <span class="meta"># set the global version</span></div><div class="line">$ rbenv versions       <span class="meta"># list all installed Ruby versions</span></div></pre></td></tr></table></figure>
<ul>
<li><a href="https://gems.ruby-china.org/" target="_blank" rel="external">配置 gem 墙内源</a></li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ gem sources --add https:<span class="comment">//gems.ruby-china.org/ --remove https://rubygems.org/</span></div><div class="line">$ gem sources -l</div><div class="line">https:<span class="comment">//gems.ruby-china.org</span></div><div class="line"># 确保只有 gems.ruby-china.org</div></pre></td></tr></table></figure>
<h3 id="安装vim和MacVim"><a href="#安装vim和MacVim" class="headerlink" title="安装vim和MacVim"></a>安装vim和MacVim</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Step 1. Install homebrew from here: http://brew.sh</div><div class="line">Step 1.1. Run export PATH=/usr/local/bin:$PATH</div><div class="line">Step 2. Run brew update</div><div class="line">Step 3. Run brew install vim &amp;&amp; brew install macvim</div><div class="line">Step 4. Run brew link macvim</div></pre></td></tr></table></figure>
<ul>
<li><a href="https://github.com/humiaozuzu/dot-vimrc" target="_blank" rel="external">Vim配置</a></li>
<li><a href="https://github.com/VundleVim/Vundle.vim" target="_blank" rel="external">vim.bundle</a></li>
<li><a href="http://blog.csdn.net/eric_xjj/article/details/8932502" target="_blank" rel="external">Mac开发利器之程序员编辑器MacVim学习总结</a></li>
</ul>
<h3 id="安装zsh和Oh-My-Zsh"><a href="#安装zsh和Oh-My-Zsh" class="headerlink" title="安装zsh和Oh My Zsh"></a>安装zsh和<a href="https://github.com/robbyrussell/oh-my-zsh" target="_blank" rel="external">Oh My Zsh</a></h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ brew install zsh</div><div class="line"># 安装oh-my-zsh</div><div class="line">$ sh -c <span class="string">"$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"</span></div></pre></td></tr></table></figure>
<ul>
<li>更换oh my zsh主题在<code>~/.oh-my-zsh/themes/</code>路径下, 在<a href="http://zshthem.es/" target="_blank" rel="external">zshthem</a>网站进行主题预览</li>
<li>安装<a href="https://github.com/wting/autojump" target="_blank" rel="external">autojump</a>, 一键跳转到目的目录, 不再不停的cd</li>
<li>安装<a href="https://github.com/sindresorhus/trash" target="_blank" rel="external">trash</a>, 不再用rm命令.</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"># 安装autojump</div><div class="line">$ brew install autojump</div><div class="line"># 安装trash</div><div class="line">$ npm install --global trash</div></pre></td></tr></table></figure>
<p>持久化SSH连接, 安装<a href="https://mosh.mit.edu/#usage" target="_blank" rel="external">mosh</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ brew install mobile-shell</div></pre></td></tr></table></figure>
<h3 id="安装python"><a href="#安装python" class="headerlink" title="安装python"></a>安装python</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">$ brew install python</div><div class="line"></div><div class="line">Pip <span class="keyword">and</span> setuptools have been installed. To update them</div><div class="line">  pip install --upgrade pip setuptools</div><div class="line"></div><div class="line">You can install Python packages with</div><div class="line">  pip install &lt;package&gt;</div><div class="line"></div><div class="line">They will install into the site-package directory</div><div class="line">  /usr/local/lib/python2<span class="number">.7</span>/site-packages</div><div class="line"></div><div class="line">See: http:<span class="comment">//docs.brew.sh/Homebrew-and-Python.html</span></div></pre></td></tr></table></figure>
<h3 id="安装安装-MongoDB-MySQL"><a href="#安装安装-MongoDB-MySQL" class="headerlink" title="安装安装 MongoDB, MySQL"></a>安装安装 MongoDB, MySQL</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ brew install mongodb mysql</div></pre></td></tr></table></figure>
<p>设置开机自启动「可选」：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ mkdir -p ~/Library/LaunchAgents</div><div class="line">$ ln -sfv /usr/local/opt/mongodb<span class="comment">/*.plist ~/Library/LaunchAgents</span></div><div class="line">$ ln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents</div></pre></td></tr></table></figure>
<h3 id="zshrc文件配置"><a href="#zshrc文件配置" class="headerlink" title=".zshrc文件配置"></a>.zshrc文件配置</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">alias zshconfig=&quot;vim ~/.zshrc&quot;</div><div class="line">alias rezsh=&quot;source ~/.zshrc&quot;</div><div class="line">alias ohmyzsh=&quot;cd ~/.oh-my-zsh&quot;</div><div class="line"></div><div class="line"># The default command paramters</div><div class="line">alias vi=&apos;vim&apos;</div><div class="line">alias egrep=&apos;egrep --color=auto&apos;</div><div class="line">alias fgrep=&apos;fgrep --color=auto&apos;</div><div class="line">alias bc=&apos;bc -l&apos;</div><div class="line">alias wget=&apos;wget -c&apos;</div><div class="line">alias chown=&apos;chown --preserve-root&apos;</div><div class="line">alias chgrp=&apos;chgrp --preserve-root&apos;</div><div class="line">alias rm=&apos;rm -I --preserve-root&apos;</div><div class="line">alias ln=&apos;ln -i&apos;</div><div class="line"></div><div class="line"># Colorful grep output</div><div class="line">alias grep=&apos;grep --color=auto&apos;</div><div class="line">export GREP_COLOR=&apos;1;33&apos;</div><div class="line"></div><div class="line"># Colorful ls</div><div class="line">export LSCOLORS=&apos;Gxfxcxdxdxegedabagacad&apos;</div><div class="line">ls=&apos;ls --color=auto&apos;</div><div class="line"></div><div class="line"># autojump</div><div class="line">[[ -s ~/.autojump/etc/profile.d/autojump.sh ]] &amp;&amp; . ~/.autojump/etc/profile.d/autojump.sh</div></pre></td></tr></table></figure>
<h3 id="安装Sublime-Text"><a href="#安装Sublime-Text" class="headerlink" title="安装Sublime Text"></a>安装Sublime Text</h3><ol>
<li>安装<a href="http://www.sublimetext.com/" target="_blank" rel="external">Sublime Text 3</a></li>
<li>安装<a href="https://packagecontrol.io/" target="_blank" rel="external">Package Control</a></li>
<li>自定义配置<a href="https://gist.github.com/Andrew-liu/86223a7f34a17080b0c33e36a09e5da3" target="_blank" rel="external">Settings</a></li>
</ol>
<p>Sublime Text 3 安装Package Control的程序<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">import urllib.request,os;pf = &apos;Package Control.sublime-package&apos;;ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) );open(os.path.join(ipp, pf), &apos;wb&apos;).write(urllib.request.urlopen( &apos;http://sublime.wbond.net/&apos; + pf.replace(&apos; &apos;,&apos;%20&apos;)).read())</div></pre></td></tr></table></figure></p>
<h2 id="常用收费软件"><a href="#常用收费软件" class="headerlink" title="常用收费软件"></a>常用收费软件</h2><ul>
<li>Alfred 2(效率神器)</li>
<li>Dash 3(程序员专用-文档查询)</li>
<li>CleanMyMac 3(电脑清理软件)</li>
<li>PopClip(选中即复制)</li>
<li>Near Lock(靠近解锁软件)</li>
<li>Bartender 2(状态栏图标管理器)</li>
<li>Manico(更方便的软件切换软件)</li>
</ul>
<h2 id="常用免费软件"><a href="#常用免费软件" class="headerlink" title="常用免费软件"></a>常用免费软件</h2><ul>
<li>Caffeine(不息屏神器)</li>
<li>XtraFinder(增强Finder)</li>
<li>Manico(快速切换神器, 相当与window上Alt+Tab)</li>
<li>Clipy, 重装时发现ClipMenu不维护了, 找了个代替品(剪切板管理)</li>
<li>Bear，MacDown，Mou(Markdown编辑器)</li>
<li>Movist(播放器)</li>
<li>Snip(截图软件)</li>
<li>Chrome(神之浏览器)</li>
<li>iTerm2(Mac上遗失的Terminal)</li>
<li>搜狗拼音(比原生的好用)</li>
<li>Window Tidy(窗口大小切换)</li>
<li>SmoothMouse</li>
<li>Reeder(RSS订阅)</li>
<li><p><a href="http://pilotmoon.com/scrollreverser/" target="_blank" rel="external">SCROLL REVERSER</a> (修改鼠标移动方向, 但不改变触摸板)</p>
</li>
<li><p>百度网盘</p>
</li>
<li>微信</li>
<li>QQ</li>
<li>阿里旺旺</li>
<li>JetBrain全家桶</li>
<li>TinyCal</li>
<li>Shadowsocks</li>
<li>Spark</li>
<li>网易云，QQ音乐</li>
<li>TickTick</li>
<li>富途牛牛</li>
<li>EverNote</li>
<li>TeamViewer</li>
<li>MacTex</li>
</ul>
<h2 id="命令行管理Wifi"><a href="#命令行管理Wifi" class="headerlink" title="命令行管理Wifi"></a>命令行管理Wifi</h2><p><a href="http://blog.mattcrampton.com/post/64144666914/managing-wifi-connections-using-the-mac-osx" target="_blank" rel="external">Managing WIFI connections using the Mac OSX terminal command line</a></p>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="http://www.jianshu.com/p/77a4349bf67b" target="_blank" rel="external">Mac开发配置</a></li>
<li><a href="http://blog.jobbole.com/85702/" target="_blank" rel="external">shell入门</a></li>
<li><a href="http://stackoverflow.com/questions/18144660/what-is-path-of-jdk-on-mac" target="_blank" rel="external">what is path of jdk on mac</a></li>
<li><a href="http://alfredtips.com/s/popular/1/" target="_blank" rel="external">Alfred web search custom</a></li>
<li><a href="https://kapeli.com/dash_guide" target="_blank" rel="external">Dash官方文档</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;div class=&quot;tip&quot;&gt;&lt;br&gt;    本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;br&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;很久没有更新博客了, 前段时间迷上了王者农药, 戒了农药后又重新入了暴雪爸爸&lt;code&gt;暗黑3&lt;/code&gt;的大坑, 呵呵.&lt;br&gt;除此之外, 公司离职回来一直准备毕业论文、毕业答辩、毕业相关材料, 真是焦头烂额.&lt;br&gt;不过想想十九年求学生涯就要结束了, 即将AFK了, 简直幸福.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为什么要重装? Mac系统的乱七八糟的东西已经占据了90%的磁盘空间, 无法减少文件保持一定空闲磁盘空间, 这种情况已经严重影响了我的日常工作.&lt;/p&gt;
&lt;p&gt;重装方案严格按照 Apple 官方文档 &lt;a href=&quot;https://support.apple.com/zh-cn/HT204904&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;如何重新安装 macOS&lt;/a&gt; 执行.&lt;/p&gt;
&lt;h2 id=&quot;系统偏好设置&quot;&gt;&lt;a href=&quot;#系统偏好设置&quot; class=&quot;headerlink&quot; title=&quot;系统偏好设置&quot;&gt;&lt;/a&gt;系统偏好设置&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;允许安装任何来源的APP: &lt;code&gt;安全性与隐私-&amp;gt;通用&lt;/code&gt;. 若无该选项，请命令行执行&lt;code&gt;sudo spctl --master-disable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;设置快捷键: &lt;code&gt;键盘-&amp;gt;快捷键&lt;/code&gt; 更改输入法切换快捷键&lt;/li&gt;
&lt;li&gt;设置触摸板: 选取全部触摸板设置&lt;/li&gt;
&lt;li&gt;设置触发角: &lt;code&gt;桌面与屏幕保护程序-&amp;gt;触发角&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;设置密码验证: &lt;code&gt;系统偏好设置-&amp;gt;安全性与隐私-&amp;gt;选择 进入休眠或屏保后立即要求输入密码&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;编辑 /etc/paths(&lt;code&gt;sudo vim /etc/paths&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight c&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;/usr/local/bin&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;/usr/local/sbin&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;/usr/bin&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;/usr/sbin&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;/bin&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;/sbin&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
    
      <category term="Mac" scheme="http://andrewliu.in/tags/Mac/"/>
    
  </entry>
  
  <entry>
    <title>2016成就墙完成情况总结</title>
    <link href="http://andrewliu.in/2017/01/17/2016%E6%88%90%E5%B0%B1%E5%A2%99%E5%AE%8C%E6%88%90%E6%83%85%E5%86%B5%E6%80%BB%E7%BB%93/"/>
    <id>http://andrewliu.in/2017/01/17/2016成就墙完成情况总结/</id>
    <published>2017-01-17T14:03:46.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<p>一转眼，2016年又结束了，我的本命年就这样不知不觉溜走了，回头一看，很多年初许下的目标因为各种拖延症没有完成，心情还是有些小抑郁的。2016年算是奔波的一年吧，往返南京、北京、深圳三座城市也是挺心酸了，还不是为了生活的苟且。</p>
<blockquote>
<p>本文想到哪里写到哪里，毕竟我没有过目不忘的记忆</p>
</blockquote>
<p>2016年解锁成就：</p>
<ul>
<li>Zhihu.inc全体验成就达成，拿到Offer后跑路</li>
<li>Baidu.inc全体验成就达成，拿到Offer后又干了一个月因其他原因不得不跑路</li>
<li>Tencent.inc实习成就达成，并期待解锁终生成就奖（然而已经错过了一波企鹅18岁的全员股票）</li>
<li>NJU最浪/最不务正业研二学生成就达成，一整年没回几次学校，没睡几次宿舍的床，感觉宿舍的床已经长毛了，不能再睡了。</li>
<li>BAT实习成就解锁2/3，预计剩余1/3是没有机会解锁了，毕竟企业文化是很重要的，选择一个适合的企业文化才能让自己开心的浪</li>
<li>iPhone 7成就达成，喊了这么多年想拥有一个iPhone，终于这一次iPhone发布会剁手，虽然没有太多的外形变化，但是总是比我的辣鸡魅族不知道高到哪里去了，辣鸡魅族，毁我青春，败我钱财</li>
<li>Apple三件套成就达成，还记得第一件iPad是去南大读书的时候，妈妈送我的礼物，还记得第二件Macbook是送给自己的开发礼物，用Apple的产品总是不会错的，虽然我一直鼓吹自己是个谷歌脑残粉，嗯，谷歌大法好！</li>
</ul>
<a id="more"></a>
<ul>
<li>PS4成就达成，并完全解锁火影4，待解锁巫师3，神海四件套，三人一狗，还是索尼大法好</li>
<li>自如最佳送钱租客成就达成，两次自如强制节约，累计送出几个亿的违约金，住的房子也是越来越贵了，从不到2000租金的次卧到住进2000+租金的自如主卧，感觉好像就空间大了点，阳光多了点的样子。</li>
<li>看书最少的一年成就解锁，累计看完三本书，剩下的时间都被狗吃了吗</li>
<li>Github凹凸commit记录成就解锁，当你看到最近一个月在频繁commit的时候，说明我实习结束了，当你看到我最近一个月几乎没有commit的时候，说明我又开始新的实习了</li>
<li>校招成就解锁，经历了一次校招，还没什么感觉就结束了，我想去的offer收到了，就没心情继续做一名收offer狂魔了</li>
<li><p>负心员工成就解锁，有了两度收到offer跑路的经历了，也是没谁了，第一次跑路是因为开发遇到大坑，不跑路也要被迫跑路，还不如主动点，第二次跑路是因为我收到理想中的offer并且签了三方，都把公司的脸打了，再不跑路难道当商业间谍吗？</p>
</li>
<li><p>北京全景点解锁，南锣鼓巷，长城，圆明园，后海，故宫…全解锁，不想再留在北京作为一个吸霾卫士了</p>
</li>
<li>深圳二次游成就解锁，时隔2年，又回到了个这个梦开始的地方，上一次我以一个参观者的身份到腾讯科兴科学园，这一次我以一个实习生的身份再一次走进科兴科学园，期待我下次以一名正式员工的身份归来。</li>
<li><p>香港游成就解锁，第一次看到了资本主义社会的发展形态，听到只能在听歌的时候才能听到的粤语，发现中国香港人好像没大陆仔没什么区别，2333</p>
</li>
<li><p>人生第一次爱情三周年成就解锁，不过貌似这两天又遇到感觉危机了，工作忙（借口？），沟通不畅，为什么别人家的女朋友总是善解人意，小鸟依然呢？这是一个值得思考的问题</p>
</li>
<li><p>博客更新和维护成就解锁，累计写出不知道多少篇博客，至少没有中断过太久的更新。</p>
</li>
</ul>
<p>2017年待解锁成就：</p>
<ul>
<li>浪漫情人节成就，准备发一波狗粮</li>
<li>Jaybird X3成就，写这篇文章的当天，该成就已成功解锁，非常赞的耳机，值得拥有</li>
<li>粗粮加的手环和移动电源割草成就</li>
<li>Tencent.inc实习全体验成就</li>
<li>南京大学成功毕业及毕业典礼成就</li>
<li>毕业旅行成就</li>
<li>正式入职成就</li>
<li>读完十本书成就，包括但不限于开发相关书籍</li>
<li>PS4 三人一狗，神海四合集，巫师3成就</li>
<li>王者农药黄金段位成就，如果玩农药没有梦想和咸鱼有什么两样，毕竟非洲血统，排位必遇挂机党，大神带我上排位</li>
<li>订婚成就</li>
<li>博客更新与维护成就</li>
<li>参与开源项目开发成就</li>
<li>更多成就待补充…</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;p&gt;一转眼，2016年又结束了，我的本命年就这样不知不觉溜走了，回头一看，很多年初许下的目标因为各种拖延症没有完成，心情还是有些小抑郁的。2016年算是奔波的一年吧，往返南京、北京、深圳三座城市也是挺心酸了，还不是为了生活的苟且。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文想到哪里写到哪里，毕竟我没有过目不忘的记忆&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2016年解锁成就：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zhihu.inc全体验成就达成，拿到Offer后跑路&lt;/li&gt;
&lt;li&gt;Baidu.inc全体验成就达成，拿到Offer后又干了一个月因其他原因不得不跑路&lt;/li&gt;
&lt;li&gt;Tencent.inc实习成就达成，并期待解锁终生成就奖（然而已经错过了一波企鹅18岁的全员股票）&lt;/li&gt;
&lt;li&gt;NJU最浪/最不务正业研二学生成就达成，一整年没回几次学校，没睡几次宿舍的床，感觉宿舍的床已经长毛了，不能再睡了。&lt;/li&gt;
&lt;li&gt;BAT实习成就解锁2/3，预计剩余1/3是没有机会解锁了，毕竟企业文化是很重要的，选择一个适合的企业文化才能让自己开心的浪&lt;/li&gt;
&lt;li&gt;iPhone 7成就达成，喊了这么多年想拥有一个iPhone，终于这一次iPhone发布会剁手，虽然没有太多的外形变化，但是总是比我的辣鸡魅族不知道高到哪里去了，辣鸡魅族，毁我青春，败我钱财&lt;/li&gt;
&lt;li&gt;Apple三件套成就达成，还记得第一件iPad是去南大读书的时候，妈妈送我的礼物，还记得第二件Macbook是送给自己的开发礼物，用Apple的产品总是不会错的，虽然我一直鼓吹自己是个谷歌脑残粉，嗯，谷歌大法好！&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
      <category term="心路札记" scheme="http://andrewliu.in/tags/%E5%BF%83%E8%B7%AF%E6%9C%AD%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>开发机安装配置golang和使用CGI</title>
    <link href="http://andrewliu.in/2016/12/13/%E5%BC%80%E5%8F%91%E6%9C%BA%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AEgolang%E5%92%8C%E4%BD%BF%E7%94%A8CGI/"/>
    <id>http://andrewliu.in/2016/12/13/开发机安装配置golang和使用CGI/</id>
    <published>2016-12-13T06:46:08.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<h2 id="安装golang"><a href="#安装golang" class="headerlink" title="安装golang"></a>安装golang</h2><ul>
<li><a href="https://golang.org/dl/" target="_blank" rel="external">Golang download</a>下载合适版本的<code>golang</code>二进制发布包.</li>
</ul>
<a id="more"></a>
<ol>
<li>Xshell5 登录跳板机-&gt;开发机</li>
<li>执行<code>rz -bey</code>选中本地电脑中下载的golang压缩包</li>
<li>执行以下命令解压并安装二进制文件到<code>/usr/local</code>中</li>
</ol>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ tar -C /usr/local -xzf <span class="keyword">go</span>$VERSION.$OS-$ARCH.tar.gz</div></pre></td></tr></table></figure>
<ol>
<li>配置golang对应的环境变量</li>
</ol>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"># 使用vim修改对应的配置文件</div><div class="line">$ vim /etc/profile</div><div class="line"># 配置环境变量</div><div class="line">export PATH=$PATH:/usr/local/<span class="keyword">go</span>/bin</div></pre></td></tr></table></figure>
<h2 id="什么是cgi"><a href="#什么是cgi" class="headerlink" title="什么是cgi?"></a>什么是cgi?</h2><ul>
<li>Web服务器负责管理连接， 数据传输，网络交互</li>
<li>CGI脚本负责管理具体的业务逻辑， <strong>CGI是一种网页和程序通讯的协议</strong></li>
</ul>
<blockquote>
<p><code>CGI</code>程序负责生成动态内容， 然后返回给服务器， 由服务器转交给客户端。CGI是一个独立的程序可以独立运行</p>
</blockquote>
<h2 id="使用golang和cgi包"><a href="#使用golang和cgi包" class="headerlink" title="使用golang和cgi包"></a>使用golang和cgi包</h2><blockquote>
<p>golang开发时, 需要对当前工作目录配置 <code>GOPATH</code>.</p>
</blockquote>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># 到工作目录下执行以下目录, 将当前目录设置为GOPATH</div><div class="line">$ export <span class="string">"GOPATH=$PWD"</span></div></pre></td></tr></table></figure>
<p>简单的机遇HTTP Server的cgi处理程序</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line">	<span class="string">"net/http"</span></div><div class="line">	<span class="string">"net/http/cgi"</span></div><div class="line">	<span class="string">"log"</span></div><div class="line">	<span class="string">"fmt"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">CgiTestHandler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</div><div class="line">	fmt.Println(<span class="string">"get request..."</span>, r.URL.Path)</div><div class="line">	handler := <span class="built_in">new</span>(cgi.Handler)</div><div class="line">	handler.Path = <span class="string">"/usr/local/go/bin/go"</span></div><div class="line">	<span class="comment">// 运行脚本位置</span></div><div class="line">	script := <span class="string">"/root/AsyncLiu/Golang/CgiTest/script/"</span> + r.URL.Path</div><div class="line">	log.Println(handler.Path)</div><div class="line"></div><div class="line">	handler.Dir = <span class="string">"/root/AsyncLiu/Golang/CgiTest/script/"</span></div><div class="line">	args := []<span class="keyword">string</span>&#123;<span class="string">"run"</span>, script&#125;</div><div class="line">	handler.Args = <span class="built_in">append</span>(handler.Args, args...)</div><div class="line">	handler.Env = <span class="built_in">append</span>(handler.Env, <span class="string">"GOPATH=/root/AsyncLiu/Golang/CgiTest"</span>)</div><div class="line">	log.Println(handler.Args)</div><div class="line"></div><div class="line">	handler.ServeHTTP(w, r)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">	http.HandleFunc(<span class="string">"/"</span>, CgiTestHandler)</div><div class="line">	fmt.Println(<span class="string">"start web server...."</span>)</div><div class="line">	log.Fatal(http.ListenAndServe(<span class="string">":8888"</span>, <span class="literal">nil</span>))</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>对应的被执行的脚本</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</div><div class="line">	fmt.Println(<span class="string">"Content-Type: test/plain;charset=utf-8\n\n"</span>)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">	fmt.Println(<span class="string">"This is executing go script"</span>)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;h2 id=&quot;安装golang&quot;&gt;&lt;a href=&quot;#安装golang&quot; class=&quot;headerlink&quot; title=&quot;安装golang&quot;&gt;&lt;/a&gt;安装golang&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://golang.org/dl/&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Golang download&lt;/a&gt;下载合适版本的&lt;code&gt;golang&lt;/code&gt;二进制发布包.&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
      <category term="Golang" scheme="http://andrewliu.in/tags/Golang/"/>
    
  </entry>
  
  <entry>
    <title>传统软件公司/创业公司/大公司的工作机会如何选择？</title>
    <link href="http://andrewliu.in/2016/11/21/%E4%BC%A0%E7%BB%9F%E8%BD%AF%E4%BB%B6%E5%85%AC%E5%8F%B8-%E5%88%9B%E4%B8%9A%E5%85%AC%E5%8F%B8-%E5%A4%A7%E5%85%AC%E5%8F%B8%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E4%BC%9A%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%EF%BC%9F/"/>
    <id>http://andrewliu.in/2016/11/21/传统软件公司-创业公司-大公司的工作机会如何选择？/</id>
    <published>2016-11-21T03:30:55.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>背景介绍: 第一家工作的公司是一家跨国外企安全公司, 骄傲的称自己不是互联网公司而是传统软件公司, 第二家公司是当下最热的知识分享社区, 创业公司. 第三家公司是挤走谷歌, 曾一度称霸中国的搜索引擎公司, 体量很大的著名三大互联网公司之一.</p>
</blockquote>
<p><strong>每一家公司我都接触的不是很久, 没能用一生的时间来体验一个公司, 只能说一下我在短时间看到的优势和缺陷</strong></p>
<a id="more"></a>
<h2 id="传统软件公司"><a href="#传统软件公司" class="headerlink" title="传统软件公司"></a>传统软件公司</h2><p><strong>优势:</strong></p>
<ol>
<li>最大优势, 几乎从不加班, 其中包括很多外企传统软件公司, 养老的最好去处, 五点老大带你一起下班回家</li>
<li>次要优势, 工作压力小, 当然核心项目除外 (因为要抢占市场之类的), 我当时所在的部门, 工作目标不要太轻松, 两三天完成本周工作目标, 然后可以自由看书(当然也可能是老板看我太菜, 想闲置我)</li>
</ol>
<p><strong>缺陷:</strong></p>
<ul>
<li>技术栈不够丰富, 很难学到高深的姿势(大神除外), 很难接触到或者用到当下最流程的框架或者技术</li>
<li>福利少, 思想僵化, 体制化严重</li>
</ul>
<blockquote>
<p>建议: 想在互联网行业发展, 不要去传统软件行业和外企, 长久呆在这些地方, 很难适应国内互联网公司的工作压力和工作节奏</p>
</blockquote>
<h2 id="创业公司"><a href="#创业公司" class="headerlink" title="创业公司"></a>创业公司</h2><p>如果想去创业公司, 我觉得首先应该调查一下以下问题:</p>
<ol>
<li>老板是否有互联网背景? 是否有创业成功经历? 是否曾连续创业</li>
<li>公司是否有足够的创业资金, 公司处于第几轮融资, 是否有广阔的前景(这个比较难判断), 公司当前的估值如果</li>
<li>公司的创业团队如何? 带队领导的业界风评怎么样</li>
</ol>
<p>针对第一个问题, 问题比较大的是, 有些创业CEO本身毫无互联网行业背景, 总是想着<code>只差一个程序员</code>, 而又喜欢对技术实现指手画脚, 我个人不太喜欢这种创业公司. 针对第二个问题, 无论如何, 工作的本质都是为了挣钱, 最终实现财富自由, 所有公司有充足的资金比较重要, 我听过身边很多朋友说创业公司老板跑路或者发不起/拖欠工资. 针对第三个问题, 进入互联网行业的程序员很多人都有一个技术专家的梦想, 所以跟着一个技术大佬指引人生方向是很重要的(菊苣们不需要).</p>
<p><strong>优势</strong></p>
<ul>
<li>工作氛围年轻, 充满活力, 一起工作的人年龄都差不多, 简直不要太轻松愉悦</li>
<li>福利比较好, 创业公司为了吸引人才是舍得下本钱的, 标配Mac, Dell U系列显示器, 人体工程椅, 大量零食, 定期<code>Team building</code>等等</li>
<li>技术栈自由, 没有历史包袱, 可以任意使用新颖的框架和技术(但是会给以后埋坑)</li>
</ul>
<p><strong>缺陷</strong></p>
<ul>
<li>很多创业公司缺少同一的标准, 代码混乱缺乏review, 技术栈混乱造轮子严重(这需要有一个重视这方面的leader来引导)</li>
<li>技术积累比较弱, 大量使用外部开源项目, 很多时候都是业务堆积</li>
<li>没有升职空间和与之对应的职业路线, 大多创业公司是简单的三层管理方式, 普通员工, leader(一般员工到这里就到顶了), CXO</li>
<li>除非技术栈和技能点过硬, 否则跳槽比较困难, 创业公司因为没有固定的晋升体系, 并且职位和工作比较难被其他公司认可</li>
</ul>
<h2 id="大公司"><a href="#大公司" class="headerlink" title="大公司"></a>大公司</h2><p>互联网大公司有很多创业公司和传统软件公司所没有的优势, 而且业界容易被业界认可</p>
<p><strong>优势:</strong></p>
<ul>
<li>会有机会和一群名校毕业, 智商很高, 头脑清晰的人一起工作, 有时间压力就是动力(比如猪厂, 可能随便一个写脚本的都是清华毕业的)</li>
<li>技术基本雄厚, 外界知名的开源项目可能很快就能在内部造出合适的轮子, 并且有专人维护跟进, 代码提交, 风格和审核有一定的标准, 不容易导致代码库紊乱</li>
<li>跳槽相对容易, 俗称大公司镀金, 很多大公司晋升体系是被认可的, 如百度/腾讯的T序列, 阿里的P序列.</li>
<li>大量的内部或者外部知识分享或培训, 完整的入职培训</li>
</ul>
<p><strong>缺陷:</strong></p>
<ul>
<li>历史包袱比较严重, 可能一次简单的版本升级会引出大量的bug和不兼容, 写代码总是要小心翼翼.</li>
<li>流程/规范/会议较多, 大量的时间被浪费在其中, 然而我们总想着让我静静</li>
<li>不同团队间协作比较困难</li>
<li>内部轮子过多, 每个小部门都喜欢自己造论子, 正如技术的特点: <code>总是短期被重视，长期被忽视</code>.</li>
</ul>
<blockquote>
<p>总的来说, 我更喜欢大公司, 完整的工作体验和晋升流程 浓厚的技术积累适合刚毕业的同学们快速成长又能有一份不菲的薪资. </p>
</blockquote>
<p>工作的选择并不是很容易做出, 要考虑的地方很多, 比如:</p>
<ol>
<li>个人喜欢, 对公司的爱好, 对岗位的爱好</li>
<li>工作是否能够让你获得成就感</li>
<li>喜欢离家近一些还是离家远一些, 喜欢城市环境好一些还是城市节奏快一些</li>
</ol>
<p>最后, 希望每个人都能拿到自己梦想的offer, 进行自己心仪的公司</p>
<h2 id="TED心得"><a href="#TED心得" class="headerlink" title="TED心得"></a>TED心得</h2><p><strong>最近看了个TED, 感触颇深, 来干了这碗毒鸡汤</strong></p>
<ul>
<li>大多数人的梦想是实现不了的, 然而也并不重要, 但是人总要有梦想吗, 万一实现了呢</li>
<li>有些事情是可以自然而然的发生的, 如果你选择了这样的事情, 多停留一段时间一定会有收获的</li>
<li>无论是什么年龄段的人, 攒人品是没必要的, 当前的痛苦并不一定带来未来的快乐, 未来的快乐也代替不了现在的快乐, 所以要活在当下, 珍惜身边人</li>
<li>心情愉悦很重要</li>
</ul>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a href="http://v.qq.com/x/cover/2mk64hchaksns1z.html?vid=e0143xi01p9" target="_blank" rel="external">TEDxDUFE：于宙 我们这一代的困惑</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;背景介绍: 第一家工作的公司是一家跨国外企安全公司, 骄傲的称自己不是互联网公司而是传统软件公司, 第二家公司是当下最热的知识分享社区, 创业公司. 第三家公司是挤走谷歌, 曾一度称霸中国的搜索引擎公司, 体量很大的著名三大互联网公司之一.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;每一家公司我都接触的不是很久, 没能用一生的时间来体验一个公司, 只能说一下我在短时间看到的优势和缺陷&lt;/strong&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="Blog" scheme="http://andrewliu.in/tags/Blog/"/>
    
  </entry>
  
  <entry>
    <title>Linux内核设计与实现读书笔记</title>
    <link href="http://andrewliu.in/2016/11/15/Linux%E5%86%85%E6%A0%B8%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    <id>http://andrewliu.in/2016/11/15/Linux内核设计与实现读书笔记/</id>
    <published>2016-11-15T02:13:27.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<p><strong>Unix强大的根本原因:</strong></p>
<ol>
<li>Unix简洁, 提供几百个系统调用, 设计目的明确</li>
<li>Unix中<code>所有东西都被当做文件对待</code></li>
<li>Unix内核和相关系统工具是用C语言开发的, 移植能力强大</li>
<li>Unix进程创建迅速, 有独特的fork机制</li>
<li>Unix提供简单稳定的进程间通信元语</li>
</ol>
<blockquote>
<p>Linux是类Unix系统, 借鉴了Unix设计并实现了Unix的API.<br>应用程序通常调用库函数(如C库函数)再由库函数通过系统调用界面, 让内核代其完成各种任务.</p>
</blockquote>
<a id="more"></a>
<ul>
<li>Linux支持动态加载内核模块</li>
<li>Linux支持对称多处理(SMP)机制</li>
<li>Linux为<code>抢占式内核</code></li>
<li>Linux并不区分线程和其他的一般进程</li>
<li>Linux提供具有设备类的面向对象的设备模型, 热插拔事件, 以及用户控件的设备文件系统</li>
</ul>
<h2 id="中断和中断处理"><a href="#中断和中断处理" class="headerlink" title="中断和中断处理"></a>中断和中断处理</h2><blockquote>
<p>中断是一种解决处理器和速度差异的方案, 只有在硬件需要的时候再向内核发出信号. 中断本质上是一种特殊的电信号.</p>
</blockquote>
<ul>
<li>内核响应特定中断, 然后<code>内核</code>调用特定的<code>中断处理程序</code>, 终端处理程序是设备驱动程序的一部分</li>
<li>Linux中的终端处理程序是不可重入的, 同一个中断处理程序不会被同时调用</li>
<li>中断上下文不可以睡眠(我理解当前被中断的程序再中断处理结束后需要继续执行)</li>
<li>中断处理程序不在进程上下文中进行, 他们不能阻塞</li>
<li>中断处理分为两部分, 上半部为中断处理程序, 要求尽可能快的执行, 下半部(<code>用于减少中断处理程序的工作量</code>)执行与中断处理密切相关但中断处理程序本身不执行的工作</li>
<li>下半部的实现方法 <code>软中断、tasklet、工作队列</code>,</li>
</ul>
<blockquote>
<p><strong>中断机制的实现:</strong> 设置产生中断, 通过电信号给处理器的特定管脚发送一个信号, 处理器听着当前处理工作, <code>关闭中断系统</code>, 然后调到内存中预定义的位置(中断处理程序的入口点)开始执行.计算终端号, <code>do_IRQ()</code>对接收的中断进行应答, 禁止这条线上的中断传递.</p>
</blockquote>
<h2 id="内核同步"><a href="#内核同步" class="headerlink" title="内核同步"></a>内核同步</h2><blockquote>
<p>对于共享资源, 如果同时被多个线程访问和操作, 就可能发生各线程之间相互覆盖共享数据, 造成访问数据不一致.</p>
</blockquote>
<p>同步实现通过主要<code>锁机制</code>对共享资源进行加锁, 只有持有锁的线程才能操作共享资源, 其他线程睡眠(或者轮询). 资源操作完成后, 持有锁的线程释放锁, 由等待线程抢锁.</p>
<p><strong>内核同步方法:</strong></p>
<ol>
<li><code>原子操作</code></li>
<li><code>自旋锁</code>, 特性是当线程无法获取锁, 会一直忙循环(<code>忙等</code>)等待锁重新可以, 适用于短期轻量级加锁</li>
<li><code>读/写自旋锁</code>(共享/排它锁), 一个或多个任务可以并发的持有读者锁, 写者锁只能被一个写任务持有.</li>
<li><code>信号量</code>(睡眠锁), 如果一个任务试图获得一个被占用的信用量时, 信号量会将其推进一个等待队列, 然后让其睡眠. 当信号量可用后, 等待队列中的任务会被唤醒. 适用于锁被长期占用的时候.</li>
<li>mutex(计数为1的信号量), 这个是编程中最常见的.</li>
<li><code>顺序锁</code></li>
<li><code>屏障</code>(barriers), 用于确保指令序列和读写的执行顺序</li>
</ol>
<p>内核中造成并发的原因:</p>
<ul>
<li>中断, 几乎可以再任何时刻异步发生, 可能随时打断当前正在执行的代码</li>
<li>软中断和tasklet, 内核能在任何时刻唤醒或调度软中断或tasklet, 打断当前正在执行的代码</li>
<li>内核抢占</li>
<li>睡眠及与用户空间的同步</li>
<li>对称多处理, 多个处理器同时执行代码</li>
</ul>
<h2 id="内存管理"><a href="#内存管理" class="headerlink" title="内存管理"></a>内存管理</h2><p>内核把物理页作为内存管理的基本单位, <code>内存管理单元(MMU, 管理内存并将虚拟地址转换为物理地址)</code>通常以页为单位来管理系统中的页表.</p>
<p>内核把也划分为不同的区(<code>zone</code>), 使用区对具有相似特性的页进行分组</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// &lt;linux/gfp.h&gt; 该函数分配2的order次方个连续`物理页`, 返回指针指向第一个页的page结构体</span></div><div class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">inline</span> struct page *</span></div><div class="line"><span class="title">alloc_pages</span><span class="params">(<span class="keyword">gfp_t</span> gfp_mask, <span class="keyword">unsigned</span> <span class="keyword">int</span> order)</span></div><div class="line"></div><div class="line"><span class="comment">// 释放物理页</span></div><div class="line"><span class="keyword">extern</span> <span class="keyword">void</span> <span class="title">free_pages</span><span class="params">(<span class="keyword">unsigned</span> <span class="keyword">long</span> addr, <span class="keyword">unsigned</span> <span class="keyword">int</span> order)</span>;</div><div class="line"></div><div class="line"><span class="comment">//&lt;linux/slab.h&gt;以字节为单位分配一块内核内存(物理上连续)</span></div><div class="line"><span class="keyword">static</span> __<span class="function">always_inline <span class="keyword">void</span> *<span class="title">kmalloc</span><span class="params">(<span class="keyword">size_t</span> size, <span class="keyword">gfp_t</span> flags)</span></span></div><div class="line"><span class="comment">//释放kmalloc分配的内存块</span></div><div class="line"><span class="keyword">void</span> <span class="title">kfree</span><span class="params">(<span class="keyword">const</span> <span class="keyword">void</span> *)</span>;</div></pre></td></tr></table></figure>
<h2 id="虚拟文件系统"><a href="#虚拟文件系统" class="headerlink" title="虚拟文件系统"></a>虚拟文件系统</h2><p>虚拟文件系统为用户控件程序提供了文件和文件系统相关接口.</p>
<p>文件的元数据, 被存储在一个单独的数据结构中, 被称为<code>inode</code>(索引节点)</p>
<p>虚拟文件系统(VFS)有四个主要的对象模型:</p>
<ul>
<li>超级块对象, 代表一个具体的已安装文件系统, 存储特定文件系统的信息</li>
<li>索引节点对象, 代表一个具体文件, 包含内核在操作文件或目录时需要的全部信息, 一个索引节点代表文件系统中的一个文件, </li>
<li>目录项对象, 代表一个目录项, 是路径的一个组成部分, <code>VFS把目录当做文件处理</code>, 目录项对象没有对应的磁盘数据结构</li>
<li>文件对象, 代表进程打开的文件, <code>进程直接处理的是文件</code></li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// &lt;linux/fs.h&gt; 文件对象的数据结构</span></div><div class="line"><span class="class"><span class="keyword">struct</span> <span class="title">file</span> &#123;</span></div><div class="line">	<span class="keyword">union</span> &#123;</div><div class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">llist_node</span>	<span class="title">fu_llist</span>;</span></div><div class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">rcu_head</span> 	<span class="title">fu_rcuhead</span>;</span></div><div class="line">	&#125; f_u;</div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">path</span>		<span class="title">f_path</span>;</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">inode</span>		*<span class="title">f_inode</span>;</span>	<span class="comment">/* cached value */</span></div><div class="line">	<span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">file_operations</span>	*<span class="title">f_op</span>;</span></div><div class="line"></div><div class="line">	<span class="comment">/*</span></div><div class="line">	 * Protects f_ep_links, f_flags.</div><div class="line">	 * Must not be taken from IRQ context.</div><div class="line">	 */</div><div class="line">	<span class="keyword">spinlock_t</span>		f_lock;</div><div class="line">	<span class="keyword">atomic_long_t</span>		f_count;</div><div class="line">	<span class="keyword">unsigned</span> <span class="keyword">int</span> 		f_flags;</div><div class="line">	<span class="keyword">fmode_t</span>			f_mode;</div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">mutex</span>		<span class="title">f_pos_lock</span>;</span></div><div class="line">	<span class="keyword">loff_t</span>			f_pos;</div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">fown_struct</span>	<span class="title">f_owner</span>;</span></div><div class="line">	<span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">cred</span>	*<span class="title">f_cred</span>;</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">file_ra_state</span>	<span class="title">f_ra</span>;</span></div><div class="line"></div><div class="line">	u64			f_version;</div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> CONFIG_SECURITY</span></div><div class="line">	<span class="keyword">void</span>			*f_security;</div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line">	<span class="comment">/* needed for tty driver, and maybe others */</span></div><div class="line">	<span class="keyword">void</span>			*private_data;</div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> CONFIG_EPOLL</span></div><div class="line">	<span class="comment">/* Used by fs/eventpoll.c to link all the hooks to this file */</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">f_ep_links</span>;</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">f_tfile_llink</span>;</span></div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">/* #ifdef CONFIG_EPOLL */</span></span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">address_space</span>	*<span class="title">f_mapping</span>;</span></div><div class="line">&#125; __attribute__((aligned(<span class="number">4</span>)));	<span class="comment">/* lest something weird decides that 2 is OK */</span></div></pre></td></tr></table></figure>
<h2 id="块I-O层"><a href="#块I-O层" class="headerlink" title="块I/O层"></a>块I/O层</h2><blockquote>
<p>系统中能够<code>随机访问</code>固定大小数据片(chunks)的硬件设备称作块设备, 如硬盘. 按照字符流的方式被<code>有序访问</code>的硬件设备称为字符设备, 如键盘</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"># &lt;linux/bio.h&gt;I/O设备基本容器由bio结构体表示</div></pre></td></tr></table></figure>
<ul>
<li><code>I/O调度程序</code>用于管理块设备的请求队列, 决定队列中的请求排列顺序以及什么时刻派发请求到挂设备. 这样有利于减少磁盘的寻址时间, 从而提高全局的吞吐量</li>
<li>linux实际使用的I/O调度程序有<code>linux电梯, 最终期限I/O调度, 预测I/O调度程序, 空操作的I/O调度程序</code></li>
</ul>
<h2 id="进程地址空间"><a href="#进程地址空间" class="headerlink" title="进程地址空间"></a>进程地址空间</h2><p>内核需要管理用户空间中进程的内存, 这个内存称为<code>进程地址空间</code>, 系统中所有进程之间以虚拟方式共享内存.</p>
<p>进程地址空间由进程可寻址的虚拟内存组成, 每个进程有32位或64位地址空间.</p>
<p>虚拟地址空间, 可被访问的合法地址空间称为<code>内存区域</code>:</p>
<ul>
<li>可执行文件代码的内存映射, 称为代码段</li>
<li>可执行文件的已初始化全局变量的内存映射, 称为数据段</li>
<li>包含未初始化全局变量,bss(block started by symbol)段的零页的内存映射</li>
<li>用于进程用户空间栈的零页内存映射</li>
<li>每一个如C库或动态链接程序等共享库的代码段、数据段和bss会被载入进程的地址空间</li>
<li>任何内存映射文件</li>
<li>任何共享内存段</li>
<li>任何匿名的内存映射, 如malloc分配的内存</li>
</ul>
<p>内核使用内存描述符结构体表示进程的地址空间, 内存描述符由mm_struct(<code>&lt;linux/sched.h&gt;</code>)结构体表示. <code>内核线程没有进程地址空间, 也没有相关的内存描述符, 所有内核线程没有用户上下文</code></p>
<blockquote>
<p>应用程序操作的对象是<code>映射到物理内存上的虚拟内存</code>, 而处理器操作的是物理内存, Linux使用三级页表完成地址转换, 每个虚拟地址作为索引指向页表, 页表项则指向下一级的页表. 在多级页表中通过TLB(translate lookaside buffer)作为一个虚拟地址映射到物理地址的缓存</p>
</blockquote>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><code>&lt;内核设计与实现&gt;</code></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unix强大的根本原因:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Unix简洁, 提供几百个系统调用, 设计目的明确&lt;/li&gt;
&lt;li&gt;Unix中&lt;code&gt;所有东西都被当做文件对待&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Unix内核和相关系统工具是用C语言开发的, 移植能力强大&lt;/li&gt;
&lt;li&gt;Unix进程创建迅速, 有独特的fork机制&lt;/li&gt;
&lt;li&gt;Unix提供简单稳定的进程间通信元语&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux是类Unix系统, 借鉴了Unix设计并实现了Unix的API.&lt;br&gt;应用程序通常调用库函数(如C库函数)再由库函数通过系统调用界面, 让内核代其完成各种任务.&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="Linux" scheme="http://andrewliu.in/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Google Protobuf源码剖析(一)</title>
    <link href="http://andrewliu.in/2016/11/07/Google-Protobuf%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90-%E4%B8%80/"/>
    <id>http://andrewliu.in/2016/11/07/Google-Protobuf源码剖析-一/</id>
    <published>2016-11-07T13:52:00.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>很久之前写过一篇<a href="http://andrewliu.in/2016/06/05/Google-protobuf-C-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">Google protobuf(C++) 学习笔记</a>. <code>google protobuf</code>被大量用于公司的RPC通信中作为序列化和序列化工具, 高于JSON和XML的性能值得拥有. 刚好最近有时间, 准备强读一发<code>google protobuf源码</code></p>
</blockquote>
<h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>本文所有所有示例均基于官方示例<code>addressbook.proto</code>:</p>
<a id="more"></a>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">package tutorial;</div><div class="line"></div><div class="line">message Person &#123;</div><div class="line">  required <span class="built_in">string</span> name = <span class="number">1</span>;</div><div class="line">  required int32 id = <span class="number">2</span>;</div><div class="line">  optional <span class="built_in">string</span> email = <span class="number">3</span>;</div><div class="line"></div><div class="line">  <span class="keyword">enum</span> PhoneType &#123;</div><div class="line">    MOBILE = <span class="number">0</span>;</div><div class="line">    HOME = <span class="number">1</span>;</div><div class="line">    WORK = <span class="number">2</span>;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  message PhoneNumber &#123;</div><div class="line">    required <span class="built_in">string</span> number = <span class="number">1</span>;</div><div class="line">    optional PhoneType type = <span class="number">2</span> [<span class="keyword">default</span> = HOME];</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  repeated PhoneNumber phone = <span class="number">4</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line">message AddressBook &#123;</div><div class="line">  repeated Person person = <span class="number">1</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li><code>required</code>字段初值是必须要提供的, 否则字段的便是未初始化的, 序列化的时候必须对<code>required</code>初始化</li>
<li><code>optional</code>字段如果未进行初始化，那么一个默认值将赋予该字段</li>
<li><code>repeated</code>字段可以理解为<code>数组</code>, </li>
</ul>
<blockquote>
<p>每个变量后的数字为标签, 用于标示了字段在二进制流中存放的位置</p>
</blockquote>
<p>运行<code>protoc -I=./ --cpp_out=./ ./addressbook.proto</code>通过<code>protoc</code>来生成 <code>.h和.cpp</code>文件.</p>
<p>那么<code>.h文件</code>中到底生成了什么呢?</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"># Person类的基类为::google::protobuf::Message类</div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> :</span> <span class="keyword">public</span> ::google::protobuf::Message</div><div class="line"></div><div class="line"><span class="meta"># enum类型的数据, 可以通过Person::MOBILE来访问</span></div><div class="line"><span class="keyword">typedef</span> Person_PhoneType PhoneType;</div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> PhoneType MOBILE =</div><div class="line">    Person_PhoneType_MOBILE;</div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> PhoneType HOME =</div><div class="line">    Person_PhoneType_HOME;</div><div class="line"><span class="keyword">static</span> <span class="keyword">const</span> PhoneType WORK =</div><div class="line">    Person_PhoneType_WORK;</div><div class="line">    </div><div class="line"><span class="meta"># required和optional的普通类型, 产生的函数族都是差不多的. 对于每个字段会生成一个has函数、clear清除函数、set函数、get函数</span></div><div class="line"><span class="comment">// required string name = 1;</span></div><div class="line"><span class="function"><span class="keyword">bool</span> <span class="title">has_name</span><span class="params">()</span> <span class="keyword">const</span></span>;  # <span class="function">has_xxx</span></div><div class="line"><span class="keyword">void</span> <span class="title">clear_name</span><span class="params">()</span>;  # clear_xxx</div><div class="line"><span class="keyword">const</span> ::<span class="built_in">std</span>::<span class="function"><span class="built_in">string</span>&amp; <span class="title">name</span><span class="params">()</span> <span class="keyword">const</span></span>;</div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">set_name</span><span class="params">(<span class="keyword">const</span> ::<span class="built_in">std</span>::<span class="built_in">string</span>&amp; value)</span></span>;</div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">set_name</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* value)</span></span>;</div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">set_name</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* value, <span class="keyword">size_t</span> size)</span></span>;</div><div class="line">::<span class="built_in">std</span>::<span class="function"><span class="built_in">string</span>* <span class="title">mutable_name</span><span class="params">()</span></span>;</div><div class="line">::<span class="built_in">std</span>::<span class="function"><span class="built_in">string</span>* <span class="title">release_name</span><span class="params">()</span></span>;</div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">set_allocated_name</span><span class="params">(::<span class="built_in">std</span>::<span class="built_in">string</span>* name)</span></span>;</div><div class="line"></div><div class="line"><span class="meta"># repeated类型有些不同, 没有has_xxx函数族</span></div><div class="line"><span class="comment">// repeated .tutorial.Person.PhoneNumber phone = 4;</span></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">phone_size</span><span class="params">()</span> <span class="keyword">const</span></span>;</div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">clear_phone</span><span class="params">()</span></span>;</div><div class="line"><span class="keyword">const</span> ::tutorial::<span class="function">Person_PhoneNumber&amp; <span class="title">phone</span><span class="params">(<span class="keyword">int</span> index)</span> <span class="keyword">const</span></span>;</div><div class="line">::tutorial::<span class="function">Person_PhoneNumber* <span class="title">mutable_phone</span><span class="params">(<span class="keyword">int</span> index)</span></span>;</div><div class="line">::tutorial::<span class="function">Person_PhoneNumber* <span class="title">add_phone</span><span class="params">()</span></span>;</div><div class="line">::google::protobuf::RepeatedPtrField&lt;::tutorial::Person_PhoneNumber &gt;*</div><div class="line">      mutable_phone();</div><div class="line"><span class="keyword">const</span> ::google::protobuf::RepeatedPtrField&lt; ::tutorial::Person_PhoneNumber &gt;&amp;</div><div class="line">      phone() <span class="keyword">const</span>;</div></pre></td></tr></table></figure>
<h2 id="Protobuf主要类"><a href="#Protobuf主要类" class="headerlink" title="Protobuf主要类"></a>Protobuf主要类</h2><ul>
<li><code>Message</code>类, 是一个抽象层, 记录了一个proto文件里的所有内容. <code>Message</code>继承自<code>MessageLite</code></li>
<li><code>MessageLite</code>类, 是一个轻量级的接口协议, 这个接口由所有协议的消息对象来实现, 这个类中包含大量定义的虚函数和纯虚函数. 使用<code>MessageLite</code>来生成代码, 需要在.proto中加入下面这行</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">option optimize_for = LITE_RUNTIME;</div></pre></td></tr></table></figure>
<ul>
<li><code>Arena</code>类主要用于协议消息的内存分配和释放, 并且分配内存是线程安全的.</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 内存块的链表组织结构</span></div><div class="line">  <span class="comment">// Blocks are variable length malloc-ed objects.  The following structure</span></div><div class="line">  <span class="comment">// describes the common header for all blocks.</span></div><div class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">Block</span> &#123;</span></div><div class="line">    <span class="keyword">void</span>* owner;   <span class="comment">// &amp;ThreadCache of thread that owns this block, or</span></div><div class="line">                   <span class="comment">// &amp;this-&gt;owner if not yet owned by a thread.</span></div><div class="line">    Block* next;   <span class="comment">// Next block in arena (may have different owner)</span></div><div class="line">    <span class="comment">// ((char*) &amp;block) + pos is next available byte. It is always</span></div><div class="line">    <span class="comment">// aligned at a multiple of 8 bytes.</span></div><div class="line">    <span class="keyword">size_t</span> pos;</div><div class="line">    <span class="keyword">size_t</span> size;  <span class="comment">// total size of the block.</span></div><div class="line">    <span class="function">GOOGLE_ATTRIBUTE_ALWAYS_INLINE size_t <span class="title">avail</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123; <span class="keyword">return</span> size - pos; &#125;</div><div class="line">    <span class="comment">// data follows</span></div><div class="line">  &#125;;</div><div class="line"></div><div class="line"><span class="comment">// Arena核心的初始化函数</span></div><div class="line"><span class="keyword">void</span> Arena::Init() &#123;</div><div class="line">  lifecycle_id_ = lifecycle_id_generator_.GetNext();</div><div class="line">  blocks_ = <span class="number">0</span>;</div><div class="line">  hint_ = <span class="number">0</span>;</div><div class="line">  owns_first_block_ = <span class="literal">true</span>;</div><div class="line">  cleanup_list_ = <span class="number">0</span>;</div><div class="line">  <span class="comment">// options为构造函数的参数, 一个配置结构体ArenaOptions</span></div><div class="line">  <span class="keyword">if</span> (options_.initial_block != <span class="literal">NULL</span> &amp;&amp; options_.initial_block_size &gt; <span class="number">0</span>) &#123;</div><div class="line">    GOOGLE_CHECK_GE(options_.initial_block_size, <span class="keyword">sizeof</span>(Block))</div><div class="line">        &lt;&lt; <span class="string">": Initial block size too small for header."</span>;</div><div class="line"></div><div class="line">    <span class="comment">// Add first unowned block to list.</span></div><div class="line">    Block* first_block = <span class="keyword">reinterpret_cast</span>&lt;Block*&gt;(options_.initial_block);</div><div class="line">    first_block-&gt;size = options_.initial_block_size;</div><div class="line">    first_block-&gt;pos = kHeaderSize;</div><div class="line">    first_block-&gt;next = <span class="literal">NULL</span>;</div><div class="line">    <span class="comment">// Thread which calls Init() owns the first block. This allows the</span></div><div class="line">    <span class="comment">// single-threaded case to allocate on the first block without taking any</span></div><div class="line">    <span class="comment">// locks.</span></div><div class="line">    first_block-&gt;owner = &amp;thread_cache();</div><div class="line">    SetThreadCacheBlock(first_block);</div><div class="line">    AddBlockInternal(first_block);</div><div class="line">    owns_first_block_ = <span class="literal">false</span>;</div><div class="line">  &#125;</div></pre></td></tr></table></figure>
<ul>
<li><code>Reflection</code>类, 是一个用于动态访问和修改协议消息各种变量域的类(也就是我们常说的反射机制), 该类只在Message中实现(<code>MessageLite中没有</code>)</li>
<li><code>Descriptor</code>类, 用于描述协议消息,通过<code>Message::GetDescriptor()</code>来获取Message对应的</li>
</ul>
<p>待续…</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;很久之前写过一篇&lt;a href=&quot;http://andrewliu.in/2016/06/05/Google-protobuf-C-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/&quot;&gt;Google protobuf(C++) 学习笔记&lt;/a&gt;. &lt;code&gt;google protobuf&lt;/code&gt;被大量用于公司的RPC通信中作为序列化和序列化工具, 高于JSON和XML的性能值得拥有. 刚好最近有时间, 准备强读一发&lt;code&gt;google protobuf源码&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;前提&quot;&gt;&lt;a href=&quot;#前提&quot; class=&quot;headerlink&quot; title=&quot;前提&quot;&gt;&lt;/a&gt;前提&lt;/h2&gt;&lt;p&gt;本文所有所有示例均基于官方示例&lt;code&gt;addressbook.proto&lt;/code&gt;:&lt;/p&gt;
    
    </summary>
    
    
      <category term="C++" scheme="http://andrewliu.in/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>某度实习总结</title>
    <link href="http://andrewliu.in/2016/10/29/%E6%9F%90%E5%BA%A6%E5%AE%9E%E4%B9%A0%E6%80%BB%E7%BB%93/"/>
    <id>http://andrewliu.in/2016/10/29/某度实习总结/</id>
    <published>2016-10-28T22:56:00.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>人呀，有时候也要考虑一下历史的行程，也要总结一下人生的经验。算算一个人来北京实习一年了，上半年在某乎，下半年在某度，而北京这个地方却没有给我留下太深的印象，喜欢的地方不多。马上要离开某度了，简单的总结一下自己吧，同时发泄一下最近消极的心情。过段时间可能会再写一篇，对传统互联网/创业公司/大体量公司的择业思考（又挖新坑）。</p>
</blockquote>
<p>文章大概几个部分吧，<strong>北京印象，人文关怀，工作感受，个人心得</strong>。文章中可能会出现一些某乎和某度的对比，仅个人见解。<strong>本文一切均个人视角请勿对号入座，想到哪里写到哪里。</strong></p>
<a id="more"></a>
<h2 id="北京印象"><a href="#北京印象" class="headerlink" title="北京印象"></a>北京印象</h2><blockquote>
<p>2015年10月，带着对未来的憧憬踏入祖国帝都。</p>
</blockquote>
<ul>
<li>去坐地铁的时候，让我印象深刻的是2号线地铁（好像是2号线）竟然没有护栏，简直恐怖。</li>
<li>北京的生活从自如租的的带阳台的出租屋开始。</li>
</ul>
<blockquote>
<p>2015年10月，正式进入互联网行业实习。</p>
</blockquote>
<ul>
<li>初入某乎实习，迎接我的是北京的雾霾天，我从来想到个一个地方的雾霾会那么严重，严重到大街上的所有人都不得不带着口罩，空气都被雾霾充斥，直到春夏雾霾渐歇。</li>
<li>某乎的冬天，在北京完成了人生第一次滑雪，只记得滑完雪，屁股好疼，内裤都是冰水。某乎的冬天，也完成了人生的第一次射箭，然而印象了了。</li>
<li>北京的冬天，会下雪，和我的老家一毛一样的。</li>
<li>某乎的团建和年会，给我的北京之旅留下了浓重的一笔，大家一起写剧本，一起演舞台剧，一起拍摄某乎五周年的视频频段，然而最后不得不和某乎的朋友说再见。</li>
</ul>
<blockquote>
<p>2016年4月，进入互联网三巨头之一实习。</p>
</blockquote>
<ul>
<li>初入某度，身边陌生，周围的同事也并不像某乎那样和蔼融洽。</li>
<li><p>某度的北京印象，平平淡淡，后文详述。</p>
</li>
<li><p>北京的人们，总是匆匆忙忙赶着地铁或者公交，快节奏的生活是在远方的小城中体验不到的。</p>
</li>
<li>北京的地上，总是到处拥堵，尤其是在互联网创造奇迹的<code>西二旗</code>，听说小米新发布的<code>小米Mix</code>意思是<strong>Made in 西二旗</strong></li>
<li>北京的景点，多是众众众众，天安门，故宫，长城，后海，圆明园，清华北京，都有去过，体会这个城市过去时代的繁华。</li>
</ul>
<h2 id="人文关怀"><a href="#人文关怀" class="headerlink" title="人文关怀"></a>人文关怀</h2><blockquote>
<p>叨叨了这么多才进入正题，年纪大了还是静不下来。</p>
</blockquote>
<p>在某度，没有感受到太多的人文关怀，毕竟人那么多，哪有那么周全的照顾到你的情绪，还是要自己动手丰衣足食才行。</p>
<ol>
<li>每天一人一份的水果或者酸奶</li>
<li>人均配置hp（i5/ssd/8G）加samsuang的19寸显示器，真心对眼睛不好，每天都拖着酸痛的眼睛回家，还是喜欢用自己的retina显示屏和大屏幕1080p的显示器，由奢入俭难呀。</li>
<li>某度半年也没有获得一个某熊吉祥物，这是我深深的怨念，给每个人入职的时候发一个不好吗，这样不是还能增加公司的归属感吗。</li>
<li>一周一次全公司抢票式报名的公开课，而我并未体验过，工作太忙？不感兴趣？我也不知道。</li>
<li>对新实习生的培训体系不健全，刚入职的时候全靠自己搜索，问身边的老司机，总算度过了当年痛苦的时光。</li>
<li>无限量供应的热水。还有咖啡，各种速溶冲泡饮料，然而喝了几天就再也不想碰了。</li>
<li>吃饭非免费，但可以报销，伙食并没有某乎的免费午餐那么好（不知道现在还有没有），毕竟我在某乎胖了十斤，而在某度又把这十斤瘦回去了。</li>
<li>晚上加班晚回去可以打车报销，然而我会说：我住的不是特别远（四十分钟的路）根本打不到车，女朋友经常嫌弃我回家太晚，其实心里挺内疚的，在这里很多时候，因为需求太多太急不得不加班，<code>但是生活不仅仅有工作</code>。</li>
</ol>
<blockquote>
<p>一到秋冬季节，北京的雾霾就非常严重，前段时间又是头疼，又是感冒发烧，我只能把这个锅丢给北京雾霾了（毕竟我是经常去健身房锻炼，还会经常和妹纸打羽毛球好吗），听说一次北京雾霾能让医院增收10%，能让口罩产业增收300%（逃</p>
</blockquote>
<h2 id="工作收获"><a href="#工作收获" class="headerlink" title="工作收获"></a>工作收获</h2><p>周围的同事都比较闷，或许是程序猿的共性？大家基本不聊天，吃饭的时候偶尔聊天也是和代码架构相关，但这些都掩盖不了周围的人们实力很强，和一群优秀的人在一起工作真是一种享受。</p>
<p>在某乎，最大的工作收获是：写代码要覆盖测试，虽然有时候测试写的不全会导致bug。然而即使有大量测试，也依然不能拯救某乎主站大姨妈式的服务器向你提了一个问题。</p>
<p>在某度，最大的工作收获是：学会了设计思维，<strong>写程序只是工作中很小很小的一部门</strong>，大部分时候应该是去全面思考当前工作的如何实现比较优秀。整理好自己的架构设计思路，拉上几个人一番头脑风暴，会有更多优化的地点，然后再开始写代码，一般效率更高。</p>
<p>在某乎，很多时候没有所谓的规范，没有日志规范，没有强制的代码规范，没有注释要求，带来的是各部门有自己规范或者说传统(代码段也有口口相传的)，最后很难相互协作，出现问题只能看源码。</p>
<p>在某度，有强制的代码审查，有大量的注释（当然也有一些老坑需要自己看源码），注释写起来虽然浪费时间，但是有助于梳理函数功能和福泽后人。在某度所有的东西都规范化流程化了，虽然很好，但很多时间有大量的时间浪费在权限申请了，而且经常你不得不push别人帮你开通权限申请。</p>
<p>在某乎，这个年轻的公司，同事即朋友，代码库不大，跨部门开发总能找到合适的人结对编程，一起完成当前项目的推进，某乎的编程经历还是很开心的。</p>
<p>在某度，我最讨厌的就是跨部门合作，不是沟通的问题，而是很多跨部门合作是因为一方弱需求一方强需求，导致总有一方不配合，或者推出来一个新来的实习生背锅作为对接人，然而这有什么卵用，一问三不知？最后还不如自己好好看源码好，甩开膀子开干。</p>
<p>在某乎，我收获了好几个真诚的工友，现在依然可以偶然联系，微信吹吹流弊，ins互刷赞，一起膜蛤续命，还有我敬爱的郭教授（手动点赞）。</p>
<p>在某度，我没有收到几个朋友，大家都各自为政，每个人都有自己的家庭，也失去了年轻时候的朝气。<br>在某度，有些流程很拖沓，我不喜欢，做人做事干净利落，今天拖明天，明天拖后天，有什么意思？</p>
<p>在某乎，一个知识性社区，也是我现在依然每天都在刷，获取知识和时事的来源之一。记得当时，偶尔会有知友来公司送吃的以表感谢，感觉真是很好。</p>
<p>在某度，我来实习的时候，刚好是各种坏消息接踵而来的时候，我见到了很多big news，血友病卖吧，魏则西事件，某度外卖事件，PPT总监事件，各种无底线推广等等，虽然我也赞同技术无罪论，但我不喜欢这种为了追求kpi，为了追求盈利而无底线的做法，既然在国内属于这个技术顶尖的公司，就应该有这样公司的担当，勇于承认错误并改正，就像google那样选择不作恶。</p>
<p>在某度，我要强力吐槽一下，标配开发机，号称4G，双核CPU的开发机成天卡成狗，随便编译个代码就不能动了，你家开发机这么多人吐槽也不知道优化一下吗？（黑人问号，手动滑稽）。</p>
<p>不管怎么样，某乎和某度都是很赞的公司。</p>
<h2 id="个人小结"><a href="#个人小结" class="headerlink" title="个人小结"></a>个人小结</h2><p>说一些自己工作中的经验或者教训吧。</p>
<ol>
<li>工作中，需要别人协作的时候，要多push，自己的事情自己不推，还有谁在乎呢？你不管，别人也不管，然而锅还是在你身上甩不掉。</li>
<li>工作中，有问题就问，不要怕被嘲笑，被鄙视。你不问清楚，最后吃亏的一定是你。我听说很多不那么正面的话，<code>python的这个用法你都不会？</code>，<code>这个工作随便一个实习生都能做</code>，<code>这个算法你都不会，是怎么通过面试的？</code>。虽然明白这个道理，但依然没有做太好。</li>
<li>工作中，要勇于争取自己的利益，你不争取，你的那份就是别人或者充公了，人非圣贤，孰能无钱，我很缺钱，毕竟北京生活成本那么高，实习工资那么少？</li>
<li>不要人云亦云，做好自己的本职工作，无中生有的东西，你再帮他说一遍，等于你也有责任吧</li>
<li>有一个严格要求对你的人是很好的，这是对你负责，如果一个mentor对你不管不问，那他不是一个好的mentor。认真严格也是一种品质，这是mentor教给我的。</li>
<li>把当前手头的工作写入邮件发给别人review，这样一是可以给未来留下工作梳理依据，二是让别人知道你当前在做什么，<strong>这很重要</strong></li>
<li>离职的时候，尽量把自己的工作收尾，不要给人留下不可依赖的印象。</li>
</ol>
<blockquote>
<p>有时候很庆幸自己能够走进某度实习，感谢自己mentor和manager, 祝各自安好。</p>
</blockquote>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人呀，有时候也要考虑一下历史的行程，也要总结一下人生的经验。算算一个人来北京实习一年了，上半年在某乎，下半年在某度，而北京这个地方却没有给我留下太深的印象，喜欢的地方不多。马上要离开某度了，简单的总结一下自己吧，同时发泄一下最近消极的心情。过段时间可能会再写一篇，对传统互联网/创业公司/大体量公司的择业思考（又挖新坑）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;文章大概几个部分吧，&lt;strong&gt;北京印象，人文关怀，工作感受，个人心得&lt;/strong&gt;。文章中可能会出现一些某乎和某度的对比，仅个人见解。&lt;strong&gt;本文一切均个人视角请勿对号入座，想到哪里写到哪里。&lt;/strong&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="Blog" scheme="http://andrewliu.in/tags/Blog/"/>
    
  </entry>
  
  <entry>
    <title>Linux进程创建和调度学习笔记</title>
    <link href="http://andrewliu.in/2016/10/08/Linux%E8%BF%9B%E7%A8%8B%E5%88%9B%E5%BB%BA%E5%92%8C%E8%B0%83%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>http://andrewliu.in/2016/10/08/Linux进程创建和调度学习笔记/</id>
    <published>2016-10-08T12:45:56.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>读书笔记</p>
</blockquote>
<h2 id="进程管理"><a href="#进程管理" class="headerlink" title="进程管理"></a>进程管理</h2><blockquote>
<p>进程是处于执行期的程序, 包含代码段, 打开描述符, 挂起信号, 内核内部数据, 处理器状态, 一个或多个具有内存映射的内存地址空间及一个或多个执行线程.<br>线程是进程中活动对象, 包含<code>独立</code>的程序计数器, 栈和一组进程寄存器. <code>线程间可共享虚拟内存, 但每个都拥有各自的虚拟处理器</code></p>
</blockquote>
<ul>
<li>内核将进程的列表放在一个双向循环链表中, 每项为<code>task_struct</code></li>
<li>进程执行系统调用或者异常处理才会陷入内核空间(内核态)</li>
<li>Linux所有进程都是PID为1的init进程的后代</li>
</ul>
<a id="more"></a>
<p>每个进程或线程都有三个数据结构，分别是 <code>struct thread_info</code>, <code>struct task_struct</code> 和 <code>内核栈</code></p>
<p><strong>task_struct 结构体中的主要元素</strong>:</p>
<ul>
<li>struct thread_info *thread_info。thread_info 指向该进程/线程的基本信息。</li>
<li>struct mm_struct *mm。mm_struct 对象用来管理该进程/线程的页表以及虚拟内存区。</li>
<li>struct mm_struct *active_mm。主要用于内核线程访问主内核页全局目录。</li>
<li>struct fs_struct *fs。fs_struct 是关于文件系统的对象。</li>
<li>struct files_struct *files。files_struct 是关于打开的文件的对象。</li>
<li>struct signal_struct *signal。signal_struct 是关于信号的对象。</li>
</ul>
<h3 id="进程创建"><a href="#进程创建" class="headerlink" title="进程创建"></a>进程创建</h3><p>通过<code>fork和exec</code>来实现, fork() 拷贝当前进程创建一个子进程, exec() 负责读取可执行文件并将其载入地址空间开始执行.</p>
<ul>
<li>Linux的 fork() 使用写时复制(copy-on-write)页实现, 资源的复制只有在需要写入的时候才进行. <code>fork() 的实际开销是复制父进程的页表以及给子进程创建唯一的进程描述符</code></li>
<li>Linux通过 <code>Clone()</code> 实现 <code>fork()</code>. <code>fork() -&gt; clone(SIGCHLD) -&gt; do_fork() -&gt; copy_process()</code></li>
</ul>
<p>copy_process()的工作:</p>
<ol>
<li>调用dup_task_struct为新进程创建一个内核栈, thread_info结果和task_struct, 值保持与当前进程相同</li>
<li>检查当前用户进程数未超出给定分配资源的限制</li>
<li>子进程部分信息被清0或重设为初始值</li>
<li>子进程状态被设置为 TASK_UNINTERRUPTIBLE, 保证他不会投入运行</li>
<li>更新 task_struct的flags成员</li>
<li>调用 alloc_pid为新进程分配有效的<code>PID</code>(此时才分配新pid)</li>
<li>根据传递给clone的参数, 设置拷贝或共享的资源.</li>
<li>返回一个指向子进程的指针.</li>
</ol>
<blockquote>
<p>vfork 与 fork 的区别是, vfork不拷贝父进程的页表项</p>
</blockquote>
<h3 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h3><ul>
<li>线程在Linux被视为一个与其他进程共享某些资源的进程</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># 线程同样通过clone创建, 只是指定共享的资源不同</div><div class="line">clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, <span class="number">0</span>);</div></pre></td></tr></table></figure>
<h3 id="内核线程"><a href="#内核线程" class="headerlink" title="内核线程"></a>内核线程</h3><ul>
<li>内核线程用于完成内核需要在后台执行的一些操作</li>
<li>内核线程和普通进程的区别在于内核线程<code>没有独立的地址空间</code>, 只在内核空间运行, <strong>所有的内核线程共享内核地址空间</strong></li>
<li>内核线程可以被调度, 可以被抢占</li>
<li>内核线程只能由其他内核线程创建</li>
</ul>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"># &lt;linux/kthread.h&gt;, 内核线程创建</div><div class="line">struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),</div><div class="line">					   void *data,</div><div class="line">					   int node,</div><div class="line">					   const char namefmt[], ...);</div><div class="line"></div><div class="line">#define kthread_create(threadfn, data, namefmt, arg...) \</div><div class="line">	kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)</div><div class="line"></div><div class="line"># 创建并运行, kthread_create创建, wake_up_process唤醒</div><div class="line">#define kthread_run(threadfn, data, namefmt, ...)			   \</div><div class="line">(&#123;									   \</div><div class="line">	struct task_struct *__k						   \</div><div class="line">		= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \</div><div class="line">	if (!IS_ERR(__k))						   \</div><div class="line">		wake_up_process(__k);					   \</div><div class="line">	__k;								   \</div><div class="line">&#125;)</div></pre></td></tr></table></figure>
<h3 id="进程终止"><a href="#进程终止" class="headerlink" title="进程终止"></a>进程终止</h3><p>进程终止一般是调用 <code>exit()</code>, 最终基本是通过 <code>do_exit()</code>完成.</p>
<ol>
<li>task_struct的标志成员设置为 PF_EXITING</li>
<li>调用del_timer_sync()删除任一内核定时器</li>
<li>调用exit_mm()函数释放进程占用的 mm_struct</li>
<li>调用sem_exit()函数</li>
<li>调用exit_file()和exit_fs() 分别递减文件描述符, 文件系统数据的引用计数</li>
<li>执行一些其他的退出动作</li>
<li>调用exit_notify()向父进程发送信号, 为其子进程找继父(线程组其他线程或init进程). 进程状态设置为<code>EXIT_ZOMBIE</code></li>
<li>调度schedule()切换到新的进程. do_exit() 永不返回.</li>
</ol>
<blockquote>
<p>进程终止的清理工作和进程描述符的删除是分开执行的. 进程描述符的删除由<code>wait4()系统调用</code>完成. 两步完成所有资源才释放完成.</p>
</blockquote>
<h2 id="进程调度"><a href="#进程调度" class="headerlink" title="进程调度"></a>进程调度</h2><blockquote>
<p>进程调度程序可以看做在可运行态进程之间分配有限的处理器时间资源的内核子系统. Linux在2.6.23内核版本中使用了<code>完全公平调度算法(CFS)</code></p>
</blockquote>
<ul>
<li>进程分为I/O消耗型和处理器消耗型</li>
<li>Linux调度器以模块方式提供, 允许多种不同的可动态添加的调度算法并存.</li>
<li>Linux使用<code>完全公平调度(CFS)</code>, 允许每个进程运行一段时间, 循环轮转, 选择运行最少的进程作为下一个运行进程(不采用通过nice计算并分配给进程时间片做法), 分配给进程的是一个处理的使用比重. nice值在CFS中被作为进程获得处理器运行比的权重. 高nice值获得更低的处理器权重. 为防止可运行进程过趋于无限时,  进程各自获得处理器使用比趋于0, CFS为每个进程设置一个<code>最小粒度</code></li>
<li>CFS使用红黑树组织可运行进程队列, 选择可运行进程中最小<code>vruntime(进程虚拟运行时间)</code>的任务. 进程可运行或被fork()后被加入红黑树</li>
<li>Linux还提供两种实时调度策略(均为静态优先级) <code>SCHED_FIFO和SCHED_RR</code>由特殊的实时调度器管理. FIFO就是先入先出的调度策略. RR是一种带时间片的先入先出的调度策略</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div></pre></td><td class="code"><pre><div class="line"># &lt;linux/sched.h&gt; <span class="class"><span class="keyword">struct</span> <span class="title">sched_entity</span>对进程运行时间做记录</span></div><div class="line"><span class="title">struct</span> <span class="title">sched_entity</span> &#123;</div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">load_weight</span>	<span class="title">load</span>;</span>		<span class="comment">/* for load-balancing */</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">rb_node</span>		<span class="title">run_node</span>;</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">list_head</span>	<span class="title">group_node</span>;</span></div><div class="line">	<span class="keyword">unsigned</span> <span class="keyword">int</span>		on_rq;</div><div class="line"></div><div class="line">	u64			exec_start;</div><div class="line">	u64			sum_exec_runtime;</div><div class="line">	u64			vruntime;</div><div class="line">	u64			prev_sum_exec_runtime;</div><div class="line"></div><div class="line">	u64			nr_migrations;</div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> CONFIG_SCHEDSTATS</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">sched_statistics</span> <span class="title">statistics</span>;</span></div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> CONFIG_FAIR_GROUP_SCHED</span></div><div class="line">	<span class="keyword">int</span>			depth;</div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">sched_entity</span>	*<span class="title">parent</span>;</span></div><div class="line">	<span class="comment">/* rq on which this entity is (to be) queued: */</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">cfs_rq</span>		*<span class="title">cfs_rq</span>;</span></div><div class="line">	<span class="comment">/* rq "owned" by this entity/group: */</span></div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">cfs_rq</span>		*<span class="title">my_q</span>;</span></div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line"></div><div class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> CONFIG_SMP</span></div><div class="line">	<span class="comment">/*</span></div><div class="line">	 * Per entity load average tracking.</div><div class="line">	 *</div><div class="line">	 * Put into separate cache line so it does not</div><div class="line">	 * collide with read-mostly values above.</div><div class="line">	 */</div><div class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">sched_avg</span>	<span class="title">avg</span> ____<span class="title">cacheline_aligned_in_smp</span>;</span></div><div class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<blockquote>
<p>Linux优先级分两种. nice值(取值-20到+19, nice值最大优先级越低); 实时优先级(取值0到99, 实时优先级数值月大优先级越高)</p>
</blockquote>
<h3 id="上下文切换"><a href="#上下文切换" class="headerlink" title="上下文切换"></a>上下文切换</h3><p><code>schedule()</code>执行进程调度时, 调用kernel/sched.c中的<code>context_switch()</code>函数执行:</p>
<ul>
<li>调用声明<code>asm/mmu_context.h</code>中的switch_mm(), 该函数把虚拟内存从上一个进程切换到新的进程中.</li>
<li>调用<code>&lt;asm/system.h&gt;</code>中的<code>switch_to()</code>, 该函数负责从上一个进程的处理器状态切换到新进程的处理器状态. 包括保存、回复栈信息和寄存器信息</li>
</ul>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li>Linux内核设计与实现 - 原书第3版</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;读书笔记&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;进程管理&quot;&gt;&lt;a href=&quot;#进程管理&quot; class=&quot;headerlink&quot; title=&quot;进程管理&quot;&gt;&lt;/a&gt;进程管理&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;进程是处于执行期的程序, 包含代码段, 打开描述符, 挂起信号, 内核内部数据, 处理器状态, 一个或多个具有内存映射的内存地址空间及一个或多个执行线程.&lt;br&gt;线程是进程中活动对象, 包含&lt;code&gt;独立&lt;/code&gt;的程序计数器, 栈和一组进程寄存器. &lt;code&gt;线程间可共享虚拟内存, 但每个都拥有各自的虚拟处理器&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;内核将进程的列表放在一个双向循环链表中, 每项为&lt;code&gt;task_struct&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;进程执行系统调用或者异常处理才会陷入内核空间(内核态)&lt;/li&gt;
&lt;li&gt;Linux所有进程都是PID为1的init进程的后代&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
      <category term="Linux" scheme="http://andrewliu.in/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>深入探索C++对象模式读书笔记</title>
    <link href="http://andrewliu.in/2016/09/30/%E6%B7%B1%E5%85%A5%E6%8E%A2%E7%B4%A2C-%E5%AF%B9%E8%B1%A1%E6%A8%A1%E5%BC%8F%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    <id>http://andrewliu.in/2016/09/30/深入探索C-对象模式读书笔记/</id>
    <published>2016-09-30T06:49:01.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>什么是<code>C++对象模型</code>?</p>
<ol>
<li>语言中直接支持面向对象程序设计的部分.</li>
<li>对于各种支持的底层实现机制.</li>
</ol>
</blockquote>
<h2 id="Object-Lessons"><a href="#Object-Lessons" class="headerlink" title="Object Lessons"></a>Object Lessons</h2><ol>
<li>C++封装并未增加布局成本, 数据成员内含在class object中(像struct), <code>成员函数不会出现在object中</code>, <strong>非内敛函数只会诞生一个实例, 内联函数对每个使用者产生一个实例</strong></li>
<li>C++在布局及存取时间上主要的额外负担是由<code>virtual</code>引起的<ul>
<li>virtual function支持动态绑定</li>
<li>virtual base class实现单一而被共享的基类实例, 多用于多继承中</li>
</ul>
</li>
</ol>
<a id="more"></a>
<h3 id="C-Object-Model"><a href="#C-Object-Model" class="headerlink" title="C++ Object Model"></a>C++ Object Model</h3><ol>
<li>简单对象模式, object由很多slot组成, 每个slot(<code>指向member的指针</code>)指向一个memeber(包含数据成员和成员函数).</li>
<li>表格驱动对象模型, 将members相关信息分别放到data member table(持有data member)和member function table(member function的指针)中, class object本身包含指向两个表格的指针</li>
<li>C++对象模型, 非静态数据成员配置在class object内, 静态数据成员存放在个别class object外.</li>
</ol>
<p>C++对象模型之<code>虚函数</code>:</p>
<ol>
<li>每个<code>class(类)</code>产生一堆指向虚函数的指针, 放在virtual table(<code>vtbl</code>)中</li>
<li>每个<code>object(实例)</code>包含一个指针(<code>vptr</code>), 指向相关的vitual table. <code>vptr的设置和重置由class的构造/析构/拷贝赋值操作符完成</code>. 每个class的<code>type_info object</code>(支持RTTI)也经由virtual table被指出来.</li>
</ol>
<p><img src="http://ww4.sinaimg.cn/large/ab508d3dgw1f7we2123j2j21ce0waq7v.jpg" alt=""></p>
<blockquote>
<p>书中给出了一个struct和class结合使用的建议: 当要传递一个复杂的class object的全部或部分到C函数中, struct声明可以将数据封装起来, 并保证拥有与C兼容的空间布局.</p>
</blockquote>
<p>C++程序设计模型支持三种programming paradigms(编程范式):</p>
<ul>
<li>程序模型, 像C一样</li>
<li>抽象数据类型模型</li>
<li>面向对象模型</li>
</ul>
<blockquote>
<p>多态的主要用途是经由一个共同的接口来影响类型的封装, 这个接口通常被定义在一个抽象的base class中</p>
</blockquote>
<h2 id="The-Semantics-of-Constructors"><a href="#The-Semantics-of-Constructors" class="headerlink" title="The Semantics of Constructors"></a>The Semantics of Constructors</h2><h3 id="Default-Constructor"><a href="#Default-Constructor" class="headerlink" title="Default Constructor"></a>Default Constructor</h3><p>四种情况会造成编译器必须为未声明构造函数的 classes 合成一个 default constructor</p>
<ol>
<li><code>带有Default Constructor的Member Class</code>. 如果class没有任何构造函数, 但包含一个<code>member object</code>, 而后者有默认构造函数, 编译器会为该clas合成一个default constructor(只是为了满足编译器的需求). 如果class有默认构造函数, 且包含<code>member object</code>, 则<strong>编译器会扩张已存在的构造函数</strong>.</li>
<li><code>带有Default Constructor的Base Class</code>. 一个没有任何构造函数的class派生自一个带有default constructor的 base class, 这个派生类中的default constructor将会被合成.</li>
<li><code>带有一个Virtual Function的class</code>. <strong>默认构造函数正确的初始化每个class的vptr</strong>. class声明一个虚函数会合成默认构造函数</li>
<li><code>带有一个Virtual Base class的Class</code>, class派生自一个继承链, 其中有一个或者更多virtual base classes</li>
</ol>
<p>带有虚函数类在编译器:</p>
<ol>
<li>编译期一个virtual function table会被编译器产生, 内放class 的virtual functions地址</li>
<li>每个<code>class object</code>中, 一个额外的 pointer member(vptr) 被编译器合成出来, 内含相关与class vtbl的地址.</li>
</ol>
<h3 id="Copy-Constructor"><a href="#Copy-Constructor" class="headerlink" title="Copy Constructor"></a>Copy Constructor</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"># 三个场景使用拷贝构造函数</div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Base</span>;</span></div><div class="line">Base a;</div><div class="line">Base b = a;  <span class="comment">//1. 显示拷贝</span></div><div class="line"><span class="keyword">void</span> (Base x);  <span class="comment">//2. object参数值传递</span></div><div class="line"><span class="keyword">return</span> b;  <span class="comment">//3. 值传递的函数返回object</span></div></pre></td></tr></table></figure>
<ol>
<li>class未提供显式copy constructor时, 默认拷贝构造函数会把<code>内建或派生</code>的 data member 的值, 从某个object拷贝到另一个object上, 但不会拷贝其中的<code>member class object</code></li>
</ol>
<h3 id="Member-Initialization-List"><a href="#Member-Initialization-List" class="headerlink" title="Member Initialization List"></a>Member Initialization List</h3><p>以下情况必须使用成员初始化列表:</p>
<ol>
<li>当初始化一个 reference member 时</li>
<li>当初始化一个 const member 时</li>
<li>当调用一个 base class 的 constructor, 而它拥有一组参数时</li>
<li>当调用一个 member class 的 constructor, 而它拥有一组参数时</li>
</ol>
<ul>
<li><strong>初始化成员列表的顺序是由class中的members声明顺序决定的</strong></li>
<li>调用<code>member function</code>设定一个member的初值可行, 因为与此object 相关的 this指针已经被建构妥当</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">X::X(<span class="keyword">int</span> val) :</div><div class="line">    i(xfoo(val)),  <span class="comment">// xfoo必须是 member function</span></div><div class="line">    j(val) &#123;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="The-Semantics-of-Data"><a href="#The-Semantics-of-Data" class="headerlink" title="The Semantics of Data"></a>The Semantics of Data</h2><h3 id="Data-Member-Layout"><a href="#Data-Member-Layout" class="headerlink" title="Data Member Layout"></a>Data Member Layout</h3><ul>
<li>非静态数据成员在class object的排列顺序和其被声明的顺序一样</li>
<li><code>静态成员函数</code>不会放入对象布局中, 会被放入<strong>data segment</strong></li>
</ul>
<blockquote>
<p>C++标准对布局持放任态度.</p>
</blockquote>
<h3 id="Data-Member-get-set"><a href="#Data-Member-get-set" class="headerlink" title="Data Member get/set"></a>Data Member get/set</h3><ul>
<li><code>static data member</code>在class object中存取不会招致任何空间或执行时间上的额外负担(静态数据成员不在class object内部)</li>
<li><code>nonstatic data member</code>存放在 class object 内, 存取效率和 c struct member 或 nonderived class member 一样.</li>
</ul>
<blockquote>
<p>继承(非虚继承)不会不会增加空间和存取时间上的额外负担</p>
</blockquote>
<p>菱形继承的解决方案是导入虚拟继承</p>
<p><img src="http://ww3.sinaimg.cn/large/ab508d3djw1f80297j7bzj20ja0bqjs4.jpg" alt=""></p>
<p>虚继承的实现: class 如果内包含一个或多个 <code>virtual base class subobjects</code>, 将被分割成两部分: 一个不变区域和一个共享区域, 不变区域中的数据, 不管后继如何演化, 总有固定的offset用于直接存取, 共享区域只能被间接存取(在虚表中放置 virtual base class 的 offset)</p>
<h2 id="The-Semantics-of-Function"><a href="#The-Semantics-of-Function" class="headerlink" title="The Semantics of Function"></a>The Semantics of Function</h2><h3 id="Nonstatic-Member-Functions"><a href="#Nonstatic-Member-Functions" class="headerlink" title="Nonstatic Member Functions"></a>Nonstatic Member Functions</h3><p>C++设计准则之一: 非静态成员函数至少必须和一般的非成员函数有相同的效率.</p>
<blockquote>
<p>编译器将类的成员函数转换为非成员函数</p>
</blockquote>
<p>转换步骤:</p>
<ol>
<li>改写函数signature, 增加一个额外的参数(this)</li>
<li>将每一个<code>对 nonstatic data member 的存取操作</code>改为经过this指针存取</li>
<li>将 member function 重新谢伟一个外部函数(对函数名特殊处理. <code>name mangling</code>)</li>
</ol>
<h3 id="Virtual-Member-Functions"><a href="#Virtual-Member-Functions" class="headerlink" title="Virtual Member Functions"></a>Virtual Member Functions</h3><p>多态: 以 public base class的指针(或引用) 寻址出一个 derived class object.</p>
<ul>
<li>一个类(不是对象)只有一个virtual table, 每个object被安插一个由编译器产生的指针(vptr).</li>
<li>派生的子类,会 overriding 一个可能存在的 base class virtual function实例</li>
</ul>
<p><img src="http://ww2.sinaimg.cn/large/ab508d3djw1f806zpmvegj21520y047e.jpg" alt=""></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Derived</span> :</span> <span class="keyword">public</span> Base1, <span class="keyword">public</span> Base2 &#123;&#125;;</div><div class="line"># 两张虚表</div><div class="line">vtbl_Derived;  <span class="comment">// 主要虚表</span></div><div class="line">vtbl_Base2_Derived;  <span class="comment">// 次要虚表</span></div></pre></td></tr></table></figure>
<p><strong>多重继承</strong>情况下, 维护有多张虚表. 当Deriveed对象指定给Base1指针或Derived指针时, 被处理的virtual table 是主要表格<code>vtbl_Derived</code>, 当Deriveed对象指定给Base2时, 被处理的虚表为<code>vtbl_Base2_Derived</code></p>
<p><img src="http://ww2.sinaimg.cn/large/ab508d3djw1f807sjmgdfj214a108n6p.jpg" alt=""></p>
<h3 id="Static-Member-Function"><a href="#Static-Member-Function" class="headerlink" title="Static Member Function"></a>Static Member Function</h3><blockquote>
<p><strong>没有this指针</strong></p>
</blockquote>
<ul>
<li>静态成员函数将被转换为一般的非静态成员函数调用</li>
<li>不能被声明为 const, volatile, virtual</li>
<li>不需要经由class object 调用</li>
</ul>
<h3 id="Inline-Functions"><a href="#Inline-Functions" class="headerlink" title="Inline Functions"></a>Inline Functions</h3><p>处理 inline函数的两个阶段:</p>
<ol>
<li>分析函数定义, 以决定函数的 <code>intrinsic inline ability</code>(我理解为天生的内联能力).</li>
<li>真正的inline函数扩展操作是在调用的那一点上.</li>
</ol>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li>深入探索C++对象模型</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;什么是&lt;code&gt;C++对象模型&lt;/code&gt;?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;语言中直接支持面向对象程序设计的部分.&lt;/li&gt;
&lt;li&gt;对于各种支持的底层实现机制.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Object-Lessons&quot;&gt;&lt;a href=&quot;#Object-Lessons&quot; class=&quot;headerlink&quot; title=&quot;Object Lessons&quot;&gt;&lt;/a&gt;Object Lessons&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;C++封装并未增加布局成本, 数据成员内含在class object中(像struct), &lt;code&gt;成员函数不会出现在object中&lt;/code&gt;, &lt;strong&gt;非内敛函数只会诞生一个实例, 内联函数对每个使用者产生一个实例&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;C++在布局及存取时间上主要的额外负担是由&lt;code&gt;virtual&lt;/code&gt;引起的&lt;ul&gt;
&lt;li&gt;virtual function支持动态绑定&lt;/li&gt;
&lt;li&gt;virtual base class实现单一而被共享的基类实例, 多用于多继承中&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
    
      <category term="C++" scheme="http://andrewliu.in/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>macOS Sierra 惊险升级</title>
    <link href="http://andrewliu.in/2016/09/24/macOS-Sierra-%E6%83%8A%E9%99%A9%E5%8D%87%E7%BA%A7/"/>
    <id>http://andrewliu.in/2016/09/24/macOS-Sierra-惊险升级/</id>
    <published>2016-09-24T10:26:27.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="惊现问题"><a href="#惊现问题" class="headerlink" title="惊现问题"></a>惊现问题</h2><p><code>2016年9月21</code> Apple开始推送 <code>macOS Sierra(10.12)</code>.</p>
<p><strong>此处升级的亮点:</strong></p>
<ul>
<li><strong>亮点就是没有亮点!!!</strong></li>
<li>最大的升级是<code>Mac OS X</code> 改名为 <code>macOS&#39;</code>, 很大的改变有木有</li>
<li>Mac增加了Siri支持, 我知道我Mac多了个天气预报小助手</li>
<li>可以使用Apple Watch自动近距离解锁Mac, 听说<a href="">Near Lock</a>已哭晕在厕所? 然而首先你要买一部 <code>Apple Watch</code></li>
<li>跨设备复制粘贴, 可以使用云端剪切板, iPhone上复制的东西可以在Mac上直接黏贴. 然而首先你要买一部 <code>iPhone</code></li>
<li>Safari我就不喷了, 反正用了Chrome的我实在受不了龟速的Safari. 听说Safari很省电, 这个卖点不错!</li>
<li>还有啥? 这次升级只有很少的App闪退阵亡.(呵呵</li>
</ul>
<blockquote>
<p>然后开始作死升级之路… 怎么升级就不说了, 正常人都知道….</p>
</blockquote>
<a id="more"></a>
<p><img src="http://ww3.sinaimg.cn/large/ab508d3dgw1f81hwo4xcwj20qo0zk0t7.jpg" alt=""></p>
<p>告诉我macOS未能安装在电脑上, 磁盘没有足够的空间来安装..</p>
<p>我当时就哔了狗了, 你安装前不先检查磁盘剩余空间就敢给我安装?<br>重启后, 自动又开始安装, 然后再一次错误, 我就知道出事了…</p>
<p>都是我最近下片无数, 看完不删还想有空再回味一下, 这下子出事了… 磁盘不够了…</p>
<h2 id="尝试解决"><a href="#尝试解决" class="headerlink" title="尝试解决"></a>尝试解决</h2><p>方法一. <code>Failed</code>. 安全模式方法, 重启后, 按住<code>Shift</code>等待出现苹果标志, 进入Mac模式, 听说此模式可以进入电脑, 这样我就能够删除我的大片了, 然后就有足够空间了, 然而我天真了, 真的再也进不去系统了, 一到80%或者100%就卡住不动了,太坑爹了…</p>
<p><img src="http://ww3.sinaimg.cn/large/ab508d3dgw1f81i4hw4xaj20qo0zk3yx.jpg" alt=""></p>
<p>方法二. <code>Failed</code>. 恢复模式方法, 重启后, 按住<code>Command + R</code>进入恢复模式, 在菜单栏的工具中, 打开Terminal, 满心以为可以通过Terminal中删除home文件下的文件. 结果我发现里面全是一些系统文件, 没有任何个人文件的影响, 此方法猝..</p>
<p>方法三. <code>Failed</code>. Ubuntu大法. 插入制作了Ubuntu的U盘, 按住<code>Option</code>开机, 开机后选址<code>Efi</code>(大概是这个名字)模式, 然后选择<code>Try Ubuntu but not installing</code>方式试用Ubuntu. 通过Ubuntu挂载Mac的硬盘, 然后对Mac硬盘的文件进行删除. 结果我又天真了, 由于硬盘未能安装成功更新, 导致挂载失败, 只能挂载恢复盘的文件, 我要这玩意有何用…</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"># 安装hfsprogs用于支持hfsplus</div><div class="line">$ sudo apt-get install hfsprogs</div><div class="line"></div><div class="line"># 测试要挂载盘的状态, sdXX表示要挂载的磁盘, 如sda1, sda2</div><div class="line">$ sudo fsck.hfsplus -f /dev/sdXX</div><div class="line"># 输出结果为 The volume #### appears to be OK表示可以挂载</div><div class="line"></div><div class="line"># 挂载磁盘到本地的路径下, 此处我挂载sda2到本地/home/ubuntu路径下</div><div class="line">sudo mount -t hfsplus -o fore,rw /dev/sda2 /home/ubuntu</div><div class="line"># 若挂载成功可进行读写操作</div></pre></td></tr></table></figure>
<p><img src="http://ww3.sinaimg.cn/large/ab508d3dgw1f81ihpzf4cj20qo0zkta2.jpg" alt=""></p>
<ol>
<li><code>Success</code>. 最后实在没办法了, 我又让他重启自动安装了, 结果发现这次成功了, 我还能说什么????</li>
</ol>
<p><img src="http://ww4.sinaimg.cn/large/ab508d3dgw1f81ij42nrpj20js08adgg.jpg" alt=""></p>
<h2 id="自我反省"><a href="#自我反省" class="headerlink" title="自我反省"></a>自我反省</h2><ol>
<li>以后再升级, 千万不能硬上了, 记得备份呀. 说的我好像买的起<code>Time Machine</code>一样</li>
<li>升级前一定要看看剩余的磁盘空间呀, 千万别装备呀, 千万别信Apple粑粑的升级包呀</li>
</ol>
<blockquote>
<p>最后我只想大声说一句: 重启大法好!!!!</p>
</blockquote>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;惊现问题&quot;&gt;&lt;a href=&quot;#惊现问题&quot; class=&quot;headerlink&quot; title=&quot;惊现问题&quot;&gt;&lt;/a&gt;惊现问题&lt;/h2&gt;&lt;p&gt;&lt;code&gt;2016年9月21&lt;/code&gt; Apple开始推送 &lt;code&gt;macOS Sierra(10.12)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;此处升级的亮点:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;亮点就是没有亮点!!!&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;最大的升级是&lt;code&gt;Mac OS X&lt;/code&gt; 改名为 &lt;code&gt;macOS&amp;#39;&lt;/code&gt;, 很大的改变有木有&lt;/li&gt;
&lt;li&gt;Mac增加了Siri支持, 我知道我Mac多了个天气预报小助手&lt;/li&gt;
&lt;li&gt;可以使用Apple Watch自动近距离解锁Mac, 听说&lt;a href=&quot;&quot;&gt;Near Lock&lt;/a&gt;已哭晕在厕所? 然而首先你要买一部 &lt;code&gt;Apple Watch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;跨设备复制粘贴, 可以使用云端剪切板, iPhone上复制的东西可以在Mac上直接黏贴. 然而首先你要买一部 &lt;code&gt;iPhone&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Safari我就不喷了, 反正用了Chrome的我实在受不了龟速的Safari. 听说Safari很省电, 这个卖点不错!&lt;/li&gt;
&lt;li&gt;还有啥? 这次升级只有很少的App闪退阵亡.(呵呵&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;然后开始作死升级之路… 怎么升级就不说了, 正常人都知道….&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="Blog" scheme="http://andrewliu.in/tags/Blog/"/>
    
  </entry>
  
  <entry>
    <title>Lua热更新</title>
    <link href="http://andrewliu.in/2016/09/17/Lua%E7%83%AD%E6%9B%B4%E6%96%B0/"/>
    <id>http://andrewliu.in/2016/09/17/Lua热更新/</id>
    <published>2016-09-17T08:53:51.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是热更新"><a href="#什么是热更新" class="headerlink" title="什么是热更新"></a>什么是热更新</h2><blockquote>
<p>Hot swapping is ability to alter the running code of a program without needing to interrupt its execution.<br>                                    –<a href="https://en.wikipedia.org/wiki/Hot_swapping" target="_blank" rel="external">Wikipedia</a></p>
</blockquote>
<p>热更新: lua虚拟机运行时, 修改出现bug或者想要增加新feature的代码, 不需要去重启整个服务.</p>
<a id="more"></a>
<h2 id="require热更新方案"><a href="#require热更新方案" class="headerlink" title="require热更新方案"></a>require热更新方案</h2><p>require的原理:</p>
<ol>
<li>在registry[“_LOADED”]表中判断该模块是否已经加载过了, 如果是则直接换回, 避免重复加载某个模块代码</li>
<li>一次调用注册的loader来加载模块</li>
<li>将加载过的模块赋值给registry[“LOADER”]表</li>
</ol>
<p>require实现的缓存机器会阻止重复加载相同模块. 所以使用require可以把<code>package.loaded</code>中对应的模块设置为nil, 然后重新进行require加载</p>
<ul>
<li>缺点: 数据会被同时更新(全局变量), 这并不是我们想要的</li>
</ul>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">-- 1. 清除require缓存, 来实现热加载</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">require_ex</span><span class="params">(module_name)</span></span></div><div class="line">    <span class="built_in">print</span>(<span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"require_ex = %s"</span>, module_name))</div><div class="line">    <span class="keyword">if</span> <span class="built_in">package</span>.<span class="built_in">loaded</span>[module_name] <span class="keyword">then</span></div><div class="line">        <span class="built_in">print</span>(<span class="built_in">string</span>.<span class="built_in">format</span>(<span class="string">"require_ex module[%s] reload."</span>, module_name))</div><div class="line">    <span class="keyword">end</span></div><div class="line">    <span class="built_in">package</span>.<span class="built_in">loaded</span>[module_name] = <span class="literal">nil</span>  <span class="comment">-- 清除历史, 重新load</span></div><div class="line">    <span class="keyword">return</span> <span class="built_in">require</span>(module_name)</div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<h2 id="优化的热更新方案"><a href="#优化的热更新方案" class="headerlink" title="优化的热更新方案"></a>优化的热更新方案</h2><p>upvalue被保留在热更新中非常重要, <code>upvalue</code>为<strong>函数里用到的定义在该函数之前的local变量</strong></p>
<p>优化的热更新方案主要是将upvalue保存下来, 重新加载时, 把upvalue的值更新进去, 使之整体看起来与原来一样.</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div></pre></td><td class="code"><pre><div class="line"><span class="comment">-- 2. 优化后的热加载</span></div><div class="line"><span class="comment">-- 利用_ENV环境，在加载的时候把数据加载到_ENV下，然后再通过对比的方式修改_G底下的值，从而实现热更新</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">hot_swap</span><span class="params">(chunk, check_name)</span></span></div><div class="line">    <span class="keyword">local</span> env = &#123;&#125;</div><div class="line">    <span class="built_in">setmetatable</span>(env, &#123; <span class="built_in">__index</span> = <span class="built_in">_G</span> &#125;)</div><div class="line">    <span class="keyword">local</span> <span class="built_in">_ENV</span> = env</div><div class="line">    <span class="keyword">local</span> f, err = <span class="built_in">load</span>(chunk, check_name,  <span class="string">'t'</span>, env)</div><div class="line">    <span class="built_in">assert</span>(f, err)</div><div class="line">    <span class="keyword">local</span> ok, err = <span class="built_in">pcall</span>(f)</div><div class="line">    <span class="built_in">assert</span>(ok, err)</div><div class="line">    <span class="keyword">for</span> name, value <span class="keyword">in</span> <span class="built_in">pairs</span>(env) <span class="keyword">do</span></div><div class="line">        <span class="keyword">local</span> g_value = <span class="built_in">_G</span>[name]</div><div class="line">        <span class="comment">-- 原type和新类型不同则做覆盖, 否则保持原值</span></div><div class="line">        <span class="keyword">if</span> <span class="built_in">type</span>(g_value) ~= <span class="built_in">type</span>(value) <span class="keyword">then</span></div><div class="line">            <span class="built_in">_G</span>[name] = value</div><div class="line">        <span class="keyword">elseif</span> <span class="built_in">type</span>(value) == <span class="string">'function'</span> <span class="keyword">then</span></div><div class="line">            update_func(value, g_value, name, <span class="string">'G'</span>..<span class="string">'  '</span>)</div><div class="line">            <span class="built_in">_G</span>[name] = value</div><div class="line">        <span class="keyword">elseif</span> <span class="built_in">type</span>(value) == <span class="string">'table'</span> <span class="keyword">then</span></div><div class="line">            update_table(value, g_value, name, <span class="string">'G'</span>..<span class="string">'  '</span>)</div><div class="line">        <span class="keyword">end</span></div><div class="line">    <span class="keyword">end</span></div><div class="line"><span class="keyword">end</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">update_func</span><span class="params">(env_f, g_f, name, deep)</span></span></div><div class="line">    <span class="comment">-- 取得原值所有的upvalue，保存起来</span></div><div class="line">    <span class="keyword">local</span> old_upvalue_map = &#123;&#125;</div><div class="line">    <span class="keyword">for</span> i = <span class="number">1</span>, <span class="built_in">math</span>.<span class="built_in">huge</span> <span class="keyword">do</span></div><div class="line">        <span class="keyword">local</span> name, value = <span class="built_in">debug</span>.<span class="built_in">getupvalue</span>(g_f, i)</div><div class="line">        <span class="keyword">if</span> <span class="keyword">not</span> name <span class="keyword">then</span> <span class="keyword">break</span> <span class="keyword">end</span></div><div class="line">        old_upvalue_map[name] = value</div><div class="line">    <span class="keyword">end</span></div><div class="line">    <span class="comment">-- 遍历所有新的upvalue，根据名字和原值对比，如果原值不存在则进行跳过，如果为其它值则进行遍历env类似的步骤</span></div><div class="line">    <span class="keyword">for</span> i = <span class="number">1</span>, <span class="built_in">math</span>.<span class="built_in">huge</span> <span class="keyword">do</span></div><div class="line">        <span class="keyword">local</span> name, value = <span class="built_in">debug</span>.<span class="built_in">getupvalue</span>(env_f, i)</div><div class="line">        <span class="keyword">if</span> <span class="keyword">not</span> name <span class="keyword">then</span> <span class="keyword">break</span> <span class="keyword">end</span></div><div class="line">        <span class="keyword">local</span> old_value = old_upvalue_map[name]</div><div class="line">        <span class="keyword">if</span> old_value <span class="keyword">then</span></div><div class="line">            <span class="keyword">if</span> <span class="built_in">type</span>(old_value) ~= <span class="built_in">type</span>(value) <span class="keyword">then</span></div><div class="line">                <span class="built_in">debug</span>.<span class="built_in">setupvalue</span>(env_f, i, old_value)</div><div class="line">            <span class="keyword">elseif</span> <span class="built_in">type</span>(old_value) == <span class="string">'function'</span> <span class="keyword">then</span></div><div class="line">                update_func(value, old_value, name, deep..<span class="string">'  '</span>..name..<span class="string">'  '</span>)</div><div class="line">            <span class="keyword">elseif</span> <span class="built_in">type</span>(old_value) == <span class="string">'table'</span> <span class="keyword">then</span></div><div class="line">                update_table(value, old_value, name, deep..<span class="string">'  '</span>..name..<span class="string">'  '</span>)</div><div class="line">                <span class="built_in">debug</span>.<span class="built_in">setupvalue</span>(env_f, i, old_value)</div><div class="line">            <span class="keyword">else</span></div><div class="line">                <span class="built_in">debug</span>.<span class="built_in">setupvalue</span>(env_f, i, old_value)</div><div class="line">            <span class="keyword">end</span></div><div class="line">        <span class="keyword">end</span></div><div class="line">    <span class="keyword">end</span></div><div class="line"><span class="keyword">end</span></div><div class="line"></div><div class="line"><span class="keyword">local</span> protection = &#123;</div><div class="line">    <span class="built_in">setmetatable</span> = <span class="literal">true</span>,</div><div class="line">    <span class="built_in">pairs</span> = <span class="literal">true</span>,</div><div class="line">    <span class="built_in">ipairs</span> = <span class="literal">true</span>,</div><div class="line">    <span class="built_in">next</span> = <span class="literal">true</span>,</div><div class="line">    <span class="built_in">require</span> = <span class="literal">true</span>,</div><div class="line">    <span class="built_in">_ENV</span> = <span class="literal">true</span>,</div><div class="line">&#125;</div><div class="line"><span class="comment">-- 防止重复的table替换，造成死循环</span></div><div class="line"><span class="keyword">local</span> visited_sig = &#123;&#125;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">update_table</span><span class="params">(env_t, g_t, name, deep)</span></span></div><div class="line">    <span class="comment">-- 对某些关键函数不进行比对</span></div><div class="line">    <span class="keyword">if</span> protection[env_t] <span class="keyword">or</span> protection[g_t] <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">end</span></div><div class="line">    <span class="comment">--如果原值与当前值内存一致，值一样不进行对比</span></div><div class="line">    <span class="keyword">if</span> env_t == g_t <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">end</span></div><div class="line">    <span class="keyword">local</span> signature = <span class="built_in">tostring</span>(g_t)..<span class="built_in">tostring</span>(env_t)</div><div class="line">    <span class="keyword">if</span> visited_sig[signature] <span class="keyword">then</span> <span class="keyword">return</span> <span class="keyword">end</span></div><div class="line">    visited_sig[signature] = <span class="literal">true</span></div><div class="line">    <span class="comment">-- 遍历对比值，如进行遍历env类似的步骤</span></div><div class="line">    <span class="keyword">for</span> name, value <span class="keyword">in</span> <span class="built_in">pairs</span>(env_t) <span class="keyword">do</span></div><div class="line">        <span class="keyword">local</span> old_value = g_t[name]</div><div class="line">        <span class="keyword">if</span> <span class="built_in">type</span>(value) == <span class="built_in">type</span>(old_value) <span class="keyword">then</span></div><div class="line">            <span class="keyword">if</span> <span class="built_in">type</span>(value) == <span class="string">'function'</span> <span class="keyword">then</span></div><div class="line">                update_func(value, old_value, name, deep..<span class="string">'  '</span>..name..<span class="string">'  '</span>)</div><div class="line">                g_t[name] = value</div><div class="line">            <span class="keyword">elseif</span> <span class="built_in">type</span>(value) == <span class="string">'table'</span> <span class="keyword">then</span></div><div class="line">                update_table(value, old_value, name, deep..<span class="string">'  '</span>..name..<span class="string">'  '</span>)</div><div class="line">            <span class="keyword">end</span></div><div class="line">        <span class="keyword">else</span></div><div class="line">            g_t[name] = value</div><div class="line">        <span class="keyword">end</span></div><div class="line">    <span class="keyword">end</span></div><div class="line">    <span class="comment">--遍历table的元表，进行对比</span></div><div class="line">    <span class="keyword">local</span> old_meta = <span class="built_in">debug</span>.<span class="built_in">getmetatable</span>(g_t)</div><div class="line">    <span class="keyword">local</span> new_meta = <span class="built_in">debug</span>.<span class="built_in">getmetatable</span>(env_t)</div><div class="line">    <span class="keyword">if</span> <span class="built_in">type</span>(old_meta) == <span class="string">'table'</span> <span class="keyword">and</span> <span class="built_in">type</span>(new_meta) == <span class="string">'table'</span> <span class="keyword">then</span></div><div class="line">        update_table(new_meta, old_meta, name..<span class="string">'s Meta'</span>, deep..<span class="string">'  '</span>..name..<span class="string">'s Meta'</span>..<span class="string">'  '</span> )</div><div class="line">    <span class="keyword">end</span></div><div class="line"><span class="keyword">end</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">hot_reload</span><span class="params">(name)</span></span></div><div class="line">    <span class="keyword">local</span> file_str</div><div class="line">    <span class="keyword">local</span> fp = <span class="built_in">io</span>.<span class="built_in">open</span>(name)</div><div class="line">    <span class="keyword">if</span> fp <span class="keyword">then</span></div><div class="line">        <span class="built_in">io</span>.<span class="built_in">input</span>(name)</div><div class="line">        file_str = <span class="built_in">io</span>.<span class="built_in">read</span>(<span class="string">'*all'</span>)</div><div class="line">        <span class="built_in">io</span>.<span class="built_in">close</span>(fp)</div><div class="line">    <span class="keyword">end</span></div><div class="line"></div><div class="line">    <span class="keyword">if</span> <span class="keyword">not</span> file_str <span class="keyword">then</span></div><div class="line">        <span class="keyword">return</span> <span class="number">-1</span></div><div class="line">    <span class="keyword">end</span></div><div class="line">    <span class="keyword">return</span> hot_swap(file_str, name)</div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="http://www.jianshu.com/p/4fca228240c0" target="_blank" rel="external">Lua 5.2/5.3 热更新小结</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;什么是热更新&quot;&gt;&lt;a href=&quot;#什么是热更新&quot; class=&quot;headerlink&quot; title=&quot;什么是热更新&quot;&gt;&lt;/a&gt;什么是热更新&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Hot swapping is ability to alter the running code of a program without needing to interrupt its execution.&lt;br&gt;                                    –&lt;a href=&quot;https://en.wikipedia.org/wiki/Hot_swapping&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;热更新: lua虚拟机运行时, 修改出现bug或者想要增加新feature的代码, 不需要去重启整个服务.&lt;/p&gt;
    
    </summary>
    
    
      <category term="Lua" scheme="http://andrewliu.in/tags/Lua/"/>
    
  </entry>
  
  <entry>
    <title>Lua编码规范</title>
    <link href="http://andrewliu.in/2016/09/04/Lua%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83/"/>
    <id>http://andrewliu.in/2016/09/04/Lua编码规范/</id>
    <published>2016-09-04T13:08:18.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>写代码是为了给别人读的, 所以总需要一些建议性的规范, 所以简单的翻译了一下国外的这篇<a href="http://lua-users.org/wiki/LuaStyleGuide" target="_blank" rel="external">Lua编码规范</a>, 并做了一些适合自己的改动.</p>
</blockquote>
<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>代码写出来是给人读的, 保持<code>一致性</code>能够增加改善代码的可读性. <code>一致性(Consistency)</code>在项目之间, 项目内部, 单个模块或单个函数中非常重要. 值得注意的是, 编码规范只是<code>一种建议</code>, 有时可能并不需要去遵守编码规范.</p>
<a id="more"></a>
<p>Lua包含自己的语法和常用方式, 本文的编码规范受到其他语言的编码规范的启发:</p>
<ul>
<li><a href="https://www.python.org/dev/peps/pep-0008/" target="_blank" rel="external">Python Style Guide</a></li>
<li><a href="http://search.cpan.org/dist/perl/pod/perlstyle.pod" target="_blank" rel="external">Perl Style Guide</a></li>
<li><a href="https://google.github.io/styleguide/cppguide.html" target="_blank" rel="external">Various Primarily C/C++ Style Guides</a></li>
</ul>
<blockquote>
<p><strong>编码规范是一种艺术</strong>. </p>
</blockquote>
<p>在定义<code>Lua</code>的风格建议时, 从一些<code>Lua</code>的源码中获得灵感(这些源码来自官方文档, Lua的作者们, 其他有名的源码) :</p>
<ul>
<li><a href="http://www.lua.org/pil/" target="_blank" rel="external">Programming in Lua</a> by Roberto Ierusalimschy</li>
<li><a href="http://www.lua.org/manual/5.1/" target="_blank" rel="external">Lua 5.1 Reference Manual</a> by R. Ierusalimschy, L. H. de Figueiredo, W. Celes</li>
<li>Other <a href="http://lua-users.org/wiki/LuaBookshttp://www.keplerproject.org/" target="_blank" rel="external">LuaBooks</a> such as Beginning Lua Programming</li>
<li>Examples Lua programs in the “test” folder of the Lua distribution.</li>
<li>Well known modules such as in <a href="http://www.keplerproject.org/" target="_blank" rel="external">Kepler</a> or on <a href="http://lua-users.org/wiki/LuaForge" target="_blank" rel="external">LuaForge</a>.</li>
</ul>
<h2 id="Formatting"><a href="#Formatting" class="headerlink" title="Formatting"></a>Formatting</h2><p><strong>缩进(Indentation)</strong> 缩进常常使用<code>四个空格缩进</code>(原文建议为两个空格). 使用四个空格缩进原因在于, 公司中C/C++编码风格和Python编码风格都是遵循四个缩进的.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">t = &#123;4, 5, 6, &quot;hello&quot;&#125;</div><div class="line">for i, v in ipairs(t) do</div><div class="line">    if type(v) == &quot;string&quot; then</div><div class="line">        print(v)</div><div class="line">    else</div><div class="line">        print(&quot;index = &quot; .. i .. &quot;, num = &quot; .. v)</div><div class="line">    end</div><div class="line">end</div></pre></td></tr></table></figure>
<h2 id="Naming"><a href="#Naming" class="headerlink" title="Naming"></a>Naming</h2><p><strong>变量命名长度(Variable name length)</strong>: 这个一个一般的规则, 范围大的变量名应该比范围小的变量有更多的描述词. 比如, 在一个大型程序中<code>i</code>作为全局变量的命名是非常糟糕的, 但作为一个小范围的计数器是最吼得.</p>
<p><strong>值和对象命名(Value and object variable naming)</strong>: 保存值和对象的变量的命名应该一般是<code>小写</code>并且比较短.</p>
<ul>
<li>布尔变量(Booleans): 布尔值或者函数的前缀应该对整体的意义是有帮助的. 例如<code>is_directory</code>而不是<code>directory</code></li>
</ul>
<p><strong>函数命名(Function naming)</strong>: 函数的命令规则一般类似于值和对象命名. 应该使用下划线和小写单词组合(原文使用getmetatable这种多个字母写在一起的方式). 例如<code>print_table</code></p>
<p><strong>Lua内部变量命名(Lua internal variable naming)</strong>: 使用名字以下滑线开头, 后跟大写字母作为传统, 如(<code>_VERSION</code>). 这种命名常用于<code>常量</code>, 但不是必须的.</p>
<p><strong>常量命名(Constants naming)</strong>: 常量, 尤其是值比较简单的, 使用全大写的组合(<code>ALL_CAPS</code>).</p>
<p><strong>模块/包命名(Mudule/Package naming)</strong>: 使用小写字母加下划线组合的形式, 如: <code>lua_module.lua</code></p>
<blockquote>
<p>变量名只有一个<code>_</code>的变量, 表示希望忽略这个变量, 这只是一个<code>传统(convention)</code></p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">-- 忽略数组下标</div><div class="line">for _, v in ipairs(t) do print(v) end</div></pre></td></tr></table></figure>
<p><code>i, k, v, t</code>常用于一下场景</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">for k,v in pairs(t) ... end</div><div class="line">for i,v in ipairs(t) ... end</div><div class="line">mt.__newindex = function(t, k, v) ... end</div></pre></td></tr></table></figure>
<p><code>M</code>常用于表示<code>当前模块的table(current module table)</code></p>
<p><strong>类名(Class names)</strong>: 以大驼峰形式命名(<code>ClassName</code>).</p>
<p><strong>匈牙利命名法?(Hungarian notations, 是这么翻译吗)</strong>. <del>将语义信息编码到变量中能够帮助增加代码可读性, 尤其是当编码的含义不容易被理解时, 但是过度的命名可能会显示冗余并且降低复杂度(在黑OC呢?). 在一些静态语言中(如, C), 变量类型已知, 此时将变量类型加入命名是很冗余的.</del></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">local function paint(canvas, n_times)</div><div class="line">    for i = 1, n_times do</div><div class="line">        local hello_str_asc_lc_en_const = &quot;hello world&quot;</div><div class="line">        canvas:draw(hello_str_asc_lc_en_const:to_upper())</div><div class="line">    end</div><div class="line">end</div></pre></td></tr></table></figure>
<h2 id="Libraries-and-Features"><a href="#Libraries-and-Features" class="headerlink" title="Libraries and Features"></a>Libraries and Features</h2><ul>
<li>如非必要, 不要使用<code>debug library</code>, 尤其是运行可信代码(使用debug library有时是一种<code>hack</code>: <a href="http://lua-users.org/wiki/StringInterpolation" target="_blank" rel="external">String Interpolation</a>)</li>
<li>避免使用过时(deprecated)的特性. 参考 <a href="http://www.lua.org/manual/5.1/manual.html#7" target="_blank" rel="external">Lua 5.1 Reference Manual - 7 - Incompatibilities with the Previous Version</a></li>
</ul>
<h2 id="Scope"><a href="#Scope" class="headerlink" title="Scope"></a>Scope</h2><p>无论何时, 尽可能的使用<code>local</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">local x = 0</div><div class="line">local function count()</div><div class="line">    x = x + 1</div><div class="line">    print(x)</div><div class="line">end</div></pre></td></tr></table></figure>
<p>全局(global) 有更大的范围和生命周期, 并且会增加<a href="https://en.wikipedia.org/wiki/Coupling_(computer_programming" target="_blank" rel="external">coupling</a>)和复杂度. <strong>不要污染环境</strong>. 在Lua中, 由于全局(global)需要运行时查找table, 所以访问<code>local</code>比<code>global</code>更快, <code>local</code>保存在寄存器中. 参考<a href="http://lua-users.org/wiki/ScopeTutorial" target="_blank" rel="external">Scope Tutorial</a></p>
<p>检测误用global的有效方法在<a href="http://lua-users.org/wiki/DetectingUndefinedVariables" target="_blank" rel="external">DetectingUndefinedVariables</a>给出. 在Lua中, <code>globals</code>有时可能导致错误拼写和潜在的错误.</p>
<p>有时通过<code>do-blocks</code>进一步限制<code>local变量</code>的范围非常有用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">local v</div><div class="line">do</div><div class="line">    local x = u2 * v3 - u3 * v2</div><div class="line">    local y = u3 * v1 - u1 * v3</div><div class="line">    local z = u1 * v2 - u2 * v1</div><div class="line">    v = &#123;x, y, z&#125;</div><div class="line">end</div><div class="line"></div><div class="line">local count</div><div class="line">do</div><div class="line">    local x = 0</div><div class="line">    count = function() x = x + 1; return x end</div><div class="line">end</div></pre></td></tr></table></figure>
<h2 id="Mudules"><a href="#Mudules" class="headerlink" title="Mudules"></a>Mudules</h2><p>在Lua 5.1版本中, 模块系统常被推荐, 但也有一些需要批评的地方. 更详细的描述可以看<a href="http://lua-users.org/wiki/LuaModuleFunctionCritiqued" target="_blank" rel="external">Lua Module Function Critiqued</a>. 你可能写出一下代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">-- hello/mytest.lua</div><div class="line">module(..., package.seeall)</div><div class="line">local function test() print(123) end</div><div class="line">function test1() test() end</div><div class="line">function test2() test1(); test1() end</div></pre></td></tr></table></figure>
<p>并且像下方一样使用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">require &quot;hello.mytest&quot;</div><div class="line">hello.mytest.test2()</div></pre></td></tr></table></figure>
<p>这里需要批判的地方是: 在所有模块中创建了一个全局变量<code>hello</code>(包含副作用), 通过hello table全局环境曝光. 如: <code>hello.mytest.print = _G.print</code>.</p>
<p>这些问题可以通过不适用<code>module</code>函数, 而是通过简单的定义模块来解决: </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">-- hello/mytest.lua</div><div class="line">local M = &#123;&#125;</div><div class="line"></div><div class="line">local function test() print(123) end</div><div class="line">function M.test1() test() end</div><div class="line">function M.test2() M.test1(); M.test1() end</div><div class="line"></div><div class="line">return M</div></pre></td></tr></table></figure>
<p>然后通过如下方式import</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">local MT = require &quot;hello.mytest&quot;</div><div class="line">MT.test2()</div></pre></td></tr></table></figure>
<p>一个模块包含带构造函数的类可以通过多种方法封装. 此处提供一个好方法:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">-- file: finance/BankAccount.lua</div><div class="line"></div><div class="line">local M = &#123;&#125;; M.__index = M</div><div class="line"></div><div class="line">local function construct()</div><div class="line">    local self = setmetatable(&#123;balance = 0&#125;, M)</div><div class="line">    return self</div><div class="line">end</div><div class="line">setmetatable(M, &#123;__call = construct&#125;)</div><div class="line"></div><div class="line">function M:add(value) self.balance = self.balance + 1 end</div><div class="line"></div><div class="line">return M</div></pre></td></tr></table></figure>
<p>一个模块通过以上方式定义一般只有<code>一个类</code>, 这个类就是模块本身.</p>
<p>使用方式如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">local BankAccount = require &quot;finance.BankAccount&quot;</div><div class="line">local account = BankAccount()</div></pre></td></tr></table></figure>
<p>或者是</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">local new = require</div><div class="line">local account = new &quot;finance.BankAccount&quot; ()</div></pre></td></tr></table></figure>
<h2 id="Commenting"><a href="#Commenting" class="headerlink" title="Commenting"></a>Commenting</h2><p>单行注释在<code>--</code>后加<code>一个空格</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">return nil  -- not found    (suggested)</div><div class="line">return nil  --not found     (discouraged)</div></pre></td></tr></table></figure>
<p>文档注释有多种写法. 可以使用下方的装饰器模式</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">local docstrings = setmetatable(&#123;&#125;, &#123;__mode = &quot;kv&quot;&#125;)</div><div class="line"></div><div class="line">function document(str)</div><div class="line">    return function(obj) docstrings[obj] = str; return obj end</div><div class="line">end</div><div class="line"></div><div class="line">function help(obj)</div><div class="line">    print(docstrings[obj])</div><div class="line">end</div><div class="line"></div><div class="line">document[[Print the documentation for a given object]](help)</div><div class="line">document[[Add a string as documentation for an object]](document)</div><div class="line"></div><div class="line">f = document[[Print a hello message]](</div><div class="line">    function()</div><div class="line">        print(&quot;hello&quot;)</div><div class="line">    end</div><div class="line">)</div><div class="line">f()</div><div class="line">help(f)</div></pre></td></tr></table></figure>
<p><strong>End终止符(End Terminator)</strong></p>
<p>在end后增加一些语句结束的注释发会帮助增加可读性</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">for i, v in ipairs(t) do</div><div class="line">    if type(v) == &quot;string&quot; then</div><div class="line">        ...lots of code here...</div><div class="line">    end -- if string</div><div class="line">end -- for each t</div></pre></td></tr></table></figure>
<h2 id="Lua-Idioms"><a href="#Lua-Idioms" class="headerlink" title="Lua Idioms"></a>Lua Idioms</h2><ul>
<li>测试一个变量不等于nil, 下方的写法更简洁. </li>
</ul>
<blockquote>
<p>Lua中nil和false做为<code>false</code>, 其他所有值为<code>true</code></p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">local line = io.read()</div><div class="line">if line then  -- instead of line ~= nil</div><div class="line">...</div><div class="line">end</div><div class="line">...</div><div class="line">if not line then  -- instead of line == nil</div><div class="line">...</div><div class="line">end</div></pre></td></tr></table></figure>
<p>但是当值即可能等于<code>nil</code>也可能等于<code>false</code>时, 条件需要明确.</p>
<ul>
<li><code>and</code>和<code>or</code>的短路求值特性也可以简化代码</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">local function test(x)</div><div class="line">    x = x or &quot;idunno&quot;</div><div class="line">    -- rather than if x == false or x == nil then x = &quot;idunno&quot; end</div><div class="line">    print(x == &quot;yes&quot; and &quot;YES!&quot; or x)</div><div class="line">    -- rather than if x == &quot;yes&quot; then print(&quot;YES!&quot;) else print(x) end</div><div class="line">end</div></pre></td></tr></table></figure>
<ul>
<li>克隆(clone)一个table(对表的大小有系统依赖限制)</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">u = &#123;unpack(t)&#125;</div></pre></td></tr></table></figure>
<ul>
<li>判断一个table是否为空</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">if next(t) == nil then ...</div></pre></td></tr></table></figure>
<ul>
<li>向数组中增加一个值</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">-- #t表示数组当前长度</div><div class="line">t[#t + 1] = 1  -- 代替 table.insert(t, 1)</div></pre></td></tr></table></figure>
<h2 id="Design-Patterns"><a href="#Design-Patterns" class="headerlink" title="Design Patterns"></a>Design Patterns</h2><p>Lua是一个轻量级语言, 通过少量的模块支持可以提供大量的强大的方式. 这种自由的设计模式需要自我组织. </p>
<blockquote>
<p>Lua is small language with a small number of simple building blocks that can be combined in a vast number of powerful ways. With this freedom comes the need for self-discipline in the form of design patterns. Often there is an idiomatic way or design pattern to achieving a certain effect in Lua that can be reused frequently (e.g. ObjectOrientationTutorial or ReadOnlyTables). See the LuaTutorial and SampleCode for common solutions to such problems.(这部分翻译不好, 放上原文)</p>
</blockquote>
<h2 id="Coding-Standards"><a href="#Coding-Standards" class="headerlink" title="Coding Standards"></a>Coding Standards</h2><p>在各种Lua项目中的编码标准:</p>
<ul>
<li><a href="http://sputnik.freewisdom.org/en/Coding_Standard" target="_blank" rel="external">Sputnik coding standard</a></li>
</ul>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="http://lua-users.org/wiki/LuaStyleGuide" target="_blank" rel="external">Lua编码规范</a></li>
<li><a href="https://segmentfault.com/a/1190000004372649" target="_blank" rel="external">高性能 Lua 技巧（译）</a></li>
<li><a href="http://www.lua.org/gems/sample.pdf" target="_blank" rel="external">Lua Performance Tips</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;写代码是为了给别人读的, 所以总需要一些建议性的规范, 所以简单的翻译了一下国外的这篇&lt;a href=&quot;http://lua-users.org/wiki/LuaStyleGuide&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;Lua编码规范&lt;/a&gt;, 并做了一些适合自己的改动.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Introduction&quot;&gt;&lt;a href=&quot;#Introduction&quot; class=&quot;headerlink&quot; title=&quot;Introduction&quot;&gt;&lt;/a&gt;Introduction&lt;/h2&gt;&lt;p&gt;代码写出来是给人读的, 保持&lt;code&gt;一致性&lt;/code&gt;能够增加改善代码的可读性. &lt;code&gt;一致性(Consistency)&lt;/code&gt;在项目之间, 项目内部, 单个模块或单个函数中非常重要. 值得注意的是, 编码规范只是&lt;code&gt;一种建议&lt;/code&gt;, 有时可能并不需要去遵守编码规范.&lt;/p&gt;
    
    </summary>
    
    
      <category term="Lua" scheme="http://andrewliu.in/tags/Lua/"/>
    
  </entry>
  
  <entry>
    <title>快学Lua</title>
    <link href="http://andrewliu.in/2016/08/29/%E5%BF%AB%E5%AD%A6Lua/"/>
    <id>http://andrewliu.in/2016/08/29/快学Lua/</id>
    <published>2016-08-29T08:01:26.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>工作需要用到Lua做一些脚本, 所以学习一下这个在游戏开发应用广泛的语言, 当然, 我更喜欢称之为撸啊撸语言…</p>
</blockquote>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><ul>
<li><code>Lua</code>是一种轻量语言，它的官方版本只包括一个精简的核心和最基本的库, </li>
<li>性能方面, Lua比Python快(这也是脚本语言选型中不用python的原因)</li>
<li>Lua与C/C++交互方便, 易于扩展</li>
<li><code>Lua</code>是一个动态弱类型语言, 支持增量式垃圾收集策略, 支持<code>协程</code></li>
<li>支持REPL(Read-Eval-Print Loop, 交互式解释器)</li>
</ul>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">-- 命令行中输入lua, 使用REPL</div><div class="line">andrew_liu:Lua/ $ lua  </div><div class="line">Lua <span class="number">5.2</span><span class="number">.4</span>  Copyright (C) <span class="number">1994</span><span class="number">-2015</span> Lua.org, PUC-Rio</div><div class="line">&gt; <span class="keyword">print</span> <span class="string">"hello world"</span>  -- 国际编程学习惯例</div><div class="line">hello world</div><div class="line">&gt; print(<span class="string">"hello world"</span>)</div><div class="line">hello world</div><div class="line">&gt;</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>已经懒得再安利<code>brew</code>了(不好用你干我), 一行命令完成安装</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># 安装路径为/usr/local/Cellar/lua</div><div class="line">$ brew install lua</div></pre></td></tr></table></figure>
<h2 id="基础语法"><a href="#基础语法" class="headerlink" title="基础语法"></a>基础语法</h2><h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><blockquote>
<p>单行注释使用<code>--</code>, 多行注释使用<code>--[[</code>和<code>--]]</code></p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">-- 这种为单行注释</div><div class="line">-- Date: 16/8/27</div><div class="line">-- Time: 22:33</div><div class="line"></div><div class="line">--[[</div><div class="line">    这里是多行注释</div><div class="line">--]]</div></pre></td></tr></table></figure>
<h3 id="变量定义"><a href="#变量定义" class="headerlink" title="变量定义"></a>变量定义</h3><ul>
<li>lua中的所有变量默认为全局变量, 定义局部变量需要使用<code>local关键字</code>(是不是很变态)</li>
<li><strong>所有的数字</strong>都是双精度浮点型(double)</li>
<li>字符串可以使用单引号和双引号, 类似于<code>python</code>中的字符串表示形式</li>
<li>使用一个<code>未定义的变量</code>不会报错, 而是返回nil(类似于C/C++中的NULL)</li>
<li>一行语句可以同时定义多个变量</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">-- variable</div><div class="line">age = 0 -- 全局变量</div><div class="line">sum = 2  -- 所有数字都是double型</div><div class="line">str = &apos;蛤丝&apos;  -- 字符串可以是单引号和双引号</div><div class="line">str1 = &quot;还是蛤丝&quot;</div><div class="line">-- 使用`[[]]`定义多行字符串时, 其空格都会被保留</div><div class="line">str2 = [[多行的字符串</div><div class="line">    以两个方括号</div><div class="line">    开始和结尾。]]</div><div class="line">sum = nil  -- 撤销sum的定义</div><div class="line"></div><div class="line">test = not_define</div><div class="line">print(test)</div><div class="line">name, age, sex = &quot;andrew&quot;, 18, &quot;male&quot;, &quot;蛤丝&quot;  -- 最后一个变量会被丢弃, 而不会报错(闷死发大财才是最吼得)</div></pre></td></tr></table></figure>
<h3 id="if-else"><a href="#if-else" class="headerlink" title="if-else"></a>if-else</h3><blockquote>
<p>if-else与python有些像, 不过需要注意if和elseif最后的<code>then</code>和末尾的<code>end</code></p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">-- if-else</div><div class="line">age = 100</div><div class="line">if age &lt;= 40 and  age &gt;= 20 then</div><div class="line">    print(&quot;young man!&quot;)</div><div class="line">elseif age &gt; 40 and age &lt; 100 then</div><div class="line">    print(&quot;old man&quot;)</div><div class="line">elseif age &lt; 20 then</div><div class="line">    print(&quot;too young&quot;)</div><div class="line">else</div><div class="line">    print(&quot;monster&quot;)</div><div class="line">end</div></pre></td></tr></table></figure>
<h3 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h3><ul>
<li><code>Lua</code>同样支持三种循环, while循环, for循环, until循环(可以理解为C/C++中的do-while循环)</li>
</ul>
<p><strong>while循环</strong></p>
<p>当条件满足, 会一直执行循环体<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">-- while</div><div class="line">num = 0</div><div class="line">while num &lt; 10 do</div><div class="line">    print(num)</div><div class="line">    num = num + 1</div><div class="line">end</div></pre></td></tr></table></figure></p>
<p><strong>for循环</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">-- for</div><div class="line">sum = 0</div><div class="line">--  1表示从1开始, 10表示最大为10(包含10)</div><div class="line">-- C++: for (int i = 1, i &lt;= 10; ++i)</div><div class="line">for i = 1, 100 do  -- 包含1和100</div><div class="line">    sum = sum + i</div><div class="line">end</div><div class="line">-- 类似于Python中的range操作, 2表示, 每次循环后i执行+2操作</div><div class="line">-- C++: for (int i = 1, i &lt;= 10; i = i + 2)</div><div class="line">for i = 1, 10, 2 do  -- 1,3,5,7,9</div><div class="line">    print(i)</div><div class="line">    sum = sum + i</div><div class="line">end</div><div class="line">-- ..操作符用于连接字符串, 可以理解为python中字符串的+</div><div class="line">操作</div><div class="line">print(&quot;sum = &quot; .. sum)</div></pre></td></tr></table></figure>
<p><strong>until循环</strong>, 类似于C/C++中的do-while语句</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">-- until(do-while)</div><div class="line">sum = 2</div><div class="line">-- 中文: 直到sum的值大于1000时, 才终止循环</div><div class="line">repeat</div><div class="line">    sum = sum ^ 2  -- 幂操作</div><div class="line">until sum &gt; 1000  </div><div class="line">print(&quot;do-while, sum = &quot;..sum)</div></pre></td></tr></table></figure>
<h3 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h3><ul>
<li>函数在Lua中是一等公民</li>
<li>支持有名和匿名函数</li>
<li>函数最后有个end, 写C/C++/Python的可能不太适应</li>
<li>函数支持一次`返回多个变量</li>
<li>局部函数的定义只需要在函数前加个<code>local</code>, 和局部变量类似</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">-- function(return支持返回多个值), 函数是一等公民</div><div class="line">function add(a, b)</div><div class="line">    return a + b</div><div class="line">end</div><div class="line">print(&quot;1 + 2 = &quot;..add(1, 2))</div><div class="line">-- 匿名函数</div><div class="line">f = function (a, b)</div><div class="line">    return a * b</div><div class="line">end</div><div class="line">print (&quot;匿名函数: &quot; .. f(2, 3))</div></pre></td></tr></table></figure>
<p><strong>递归(经典: 斐波那契)</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">function fib(n)</div><div class="line">  if n &lt; 2 then return n end</div><div class="line">  return fib(n - 2) + fib(n - 1)</div><div class="line">end</div></pre></td></tr></table></figure>
<h2 id="Table"><a href="#Table" class="headerlink" title="Table"></a>Table</h2><p><code>Table</code>是Lua中核心数据结构, 我理解为其他语言中的<code>Dict/Map</code>数据结构(key-value的数据结构), 而数组等在Lua中都是通过<code>Table</code>来表示的</p>
<blockquote>
<p>Table可以使用任何非nil的值作为key</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">-- Lua唯一的组合数据结构</div><div class="line">-- 可以使用任何非nil值作为key</div><div class="line">-- Table(key-value数据结构)</div><div class="line">map = &#123;</div><div class="line">    name = &quot;andrew&quot;,  -- 此处name默认使用字符串作为key</div><div class="line">    age = 18,</div><div class="line">    sex = &quot;male&quot;</div><div class="line">&#125;</div><div class="line">-- 上下两种写法等价</div><div class="line">--map = &#123;[&apos;name&apos;] = &apos;andrew&apos;, [&apos;age&apos;] = 18, [&apos;sex&apos;] = &apos;male&apos;&#125;</div></pre></td></tr></table></figure>
<p>对Table创建, 读取, 更新和删除操作, key-value的读取可以通过<code>.</code>符号</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">-- Table的CRUD</div><div class="line">map[&apos;hobby&apos;] = &quot;膜蛤&quot;  -- map.hobby = &quot;膜蛤&quot;</div><div class="line">print(map.name)</div><div class="line">map.age = 19</div><div class="line">map.sex = nil  -- 删除key</div></pre></td></tr></table></figure>
<p>遍历一个Table</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">-- 遍历一个Table</div><div class="line">for k, v in pairs(map) do</div><div class="line">    print(k, v)</div><div class="line">end</div></pre></td></tr></table></figure>
<h3 id="数组-列表"><a href="#数组-列表" class="headerlink" title="数组/列表"></a>数组/列表</h3><ul>
<li>数组是通过Table来实现的</li>
</ul>
<blockquote>
<p><code>Lua</code>中数组的下标是从1开始, 从1开始, 从1开始. 重要的事情说三遍.</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">-- array, 数组下标从1开始</div><div class="line">-- 数组中可以是不同类型的数据</div><div class="line">arr = &#123;&quot;string&quot;, 100, &quot;andrew&quot;, function() print(&quot;hello world&quot;) end &#125;</div><div class="line">-- 等价于</div><div class="line">-- arr = &#123;[1]=&quot;string&quot;, [2]=100, [3]=&quot;andrew&quot;, [4]=40, [5]=function() print(&quot;hello world&quot;) end&#125;</div><div class="line"></div><div class="line">for i = 1, #arr do  -- #arr表示数组的长度</div><div class="line">    print(arr[i])</div><div class="line">end</div></pre></td></tr></table></figure>
<h3 id="元表-metatable"><a href="#元表-metatable" class="headerlink" title="元表(metatable)"></a>元表(metatable)</h3><p>Table的<code>metatable</code>提供一种类似于操作符重载的机制, 写个python的可能重写个<code>__add__</code>方法, 感觉有点类似</p>
<ul>
<li>通过<code>setmetatable</code>将自己实现的metamethod绑定到对应的Table对象中</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">-- table的元表提供一种机制,支持操作符重载</div><div class="line">data_one = &#123;a = 1, b = 2&#125;  -- data_one和data_two 可以看做表示1/2和3/4</div><div class="line">data_two = &#123;a = 3, b = 4 &#125;</div><div class="line">meta_fraction = &#123;&#125;  -- 新定义一个元表</div><div class="line">function meta_fraction.__add(f1, f2) -- __add是build-in的原方法</div><div class="line">    local sum = &#123;&#125;</div><div class="line">    sum.b = f1.b * f2.b</div><div class="line">    sum.a = f1.a * f2.b + f2.a * f1.b</div><div class="line">    return sum</div><div class="line">end</div><div class="line">setmetatable(data_one, meta_fraction)  -- 为之前定义的两个table设置metatable</div><div class="line">setmetatable(data_two, meta_fraction)</div><div class="line">s = data_one + data_two  -- 执行 + 操作</div></pre></td></tr></table></figure>
<ul>
<li>元表的<code>__index</code> 可以重载用于查找的点操作符, 我理解有点类似于python中的<code>__getattr__</code>, 对类中的属性做查找</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">-- 当表要索引一个值时如table[key], Lua会首先在table本身中查找key的值,</div><div class="line">-- 如果没有并且这个table存在一个带有__index属性的Metatable, 则Lua会按照__index所定义的函数逻辑查找</div><div class="line">default_map = &#123;name = &apos;andrew&apos;, sex = &apos;male&apos; &#125;</div><div class="line">new_map = &#123;age = 18 &#125;</div><div class="line">setmetatable(default_map, &#123;__index = new_map&#125;)</div><div class="line">print(default_map.name, default_map.age)  -- 继承了new_map的属性</div></pre></td></tr></table></figure>
<h3 id="元方法-metamethod"><a href="#元方法-metamethod" class="headerlink" title="元方法(metamethod)"></a>元方法(metamethod)</h3><p><code>__add</code>这种方法在Lua中被称为元方法, </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">-- __add(a, b)                     for a + b </div><div class="line">-- __sub(a, b)                     for a - b </div><div class="line">-- __mul(a, b)                     for a * b </div><div class="line">-- __div(a, b)                     for a / b </div><div class="line">-- __mod(a, b)                     for a % b </div><div class="line">-- __pow(a, b)                     for a ^ b </div><div class="line">-- __unm(a)                        for -a </div><div class="line">-- __concat(a, b)                  for a .. b </div><div class="line">-- __len(a)                        for #a </div><div class="line">-- __eq(a, b)                      for a == b </div><div class="line">-- __lt(a, b)                      for a &lt; b </div><div class="line">-- __le(a, b)                      for a &lt;= b </div><div class="line">-- __index(a, b)  &lt;fn or a table&gt;  for a.b </div><div class="line">-- __newindex(a, b, c)             for a.b = c </div><div class="line">-- __call(a, ...)                  for a(...)</div></pre></td></tr></table></figure>
<h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><p><code>继承</code>同样是通过元表和元方法来实现的.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">-- table实现继承</div><div class="line">Dog = &#123;&#125;</div><div class="line">-- 这个new函数我理解为继承类的构造函数, 冒号(:)函数会使参数默认增加一个self作为参数</div><div class="line">function Dog:new()</div><div class="line">    -- 下面这句相当于继承的类增加的新的成员变量</div><div class="line">    local new_obj = &#123;sound = &apos;woof&apos;&#125;</div><div class="line">    self.__index = self</div><div class="line">    return setmetatable(new_obj, self)</div><div class="line">end</div><div class="line">function Dog:make_sound()  -- 等价于Dog.make_sound(Dog)</div><div class="line">    print(&quot;say &quot; .. self.sound)</div><div class="line">end</div><div class="line">-- 使用</div><div class="line">new_dog = Dog:new()  -- new_dog获得Dog的方法和变量列表</div><div class="line">new_dog:make_sound()</div></pre></td></tr></table></figure>
<ol>
<li><code>self.__index = self</code>是防止self被扩展后改写，所以，让其保持原样</li>
<li><code>setmetatable(new_obj, self)</code>会返回第一个参数, new_obj会得到self的函数</li>
<li><code>Dog:new()</code>调用方式会增加一个隐式参数<code>self</code></li>
</ol>
<h2 id="模块"><a href="#模块" class="headerlink" title="模块"></a>模块</h2><ol>
<li>require函数, 第一次执行后有缓存, 所以载入同样的lua文件时, 只有第一次的时候会去执行, 多次执行同一句require, 只有第一次生效</li>
<li>dofile函数, 类似于require, 但无缓存, 每次语句都会重新执行一次</li>
<li>loadfile加载一个lua文件, 但是并不运行它, 只要在需要的时候再像函数一样执行它</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">-- module</div><div class="line">-- 1. require</div><div class="line">local test = require(&apos;module&apos;)  -- require文件会被缓存</div><div class="line">-- test.say_my_name()  -- 不能调用local</div><div class="line">test.say_hello()</div><div class="line">-- 2. dofile 类似require 但不缓存</div><div class="line">-- 3. loadfile加载一个lua文件, 但是并不运行它</div></pre></td></tr></table></figure>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="http://book.luaer.cn/" target="_blank" rel="external">lua程序设计</a></li>
<li><a href="http://www.codingnow.com/2000/download/lua_manual.html" target="_blank" rel="external">Lua 5.1 参考手册(云风译)</a></li>
<li><a href="https://learnxinyminutes.com/docs/zh-cn/lua-cn/" target="_blank" rel="external">Learn Lua in Y Minutes</a></li>
<li><a href="http://coolshell.cn/articles/10739.html" target="_blank" rel="external">Lua简明教程</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;工作需要用到Lua做一些脚本, 所以学习一下这个在游戏开发应用广泛的语言, 当然, 我更喜欢称之为撸啊撸语言…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Lua&lt;/code&gt;是一种轻量语言，它的官方版本只包括一个精简的核心和最基本的库, &lt;/li&gt;
&lt;li&gt;性能方面, Lua比Python快(这也是脚本语言选型中不用python的原因)&lt;/li&gt;
&lt;li&gt;Lua与C/C++交互方便, 易于扩展&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Lua&lt;/code&gt;是一个动态弱类型语言, 支持增量式垃圾收集策略, 支持&lt;code&gt;协程&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;支持REPL(Read-Eval-Print Loop, 交互式解释器)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;highlight python&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;7&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;8&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;-- 命令行中输入lua, 使用REPL&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;andrew_liu:Lua/ $ lua  &lt;/div&gt;&lt;div class=&quot;line&quot;&gt;Lua &lt;span class=&quot;number&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;.4&lt;/span&gt;  Copyright (C) &lt;span class=&quot;number&quot;&gt;1994&lt;/span&gt;&lt;span class=&quot;number&quot;&gt;-2015&lt;/span&gt; Lua.org, PUC-Rio&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;gt; &lt;span class=&quot;keyword&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;hello world&quot;&lt;/span&gt;  -- 国际编程学习惯例&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;hello world&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;gt; print(&lt;span class=&quot;string&quot;&gt;&quot;hello world&quot;&lt;/span&gt;)&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;hello world&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;gt;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
    
      <category term="Lua" scheme="http://andrewliu.in/tags/Lua/"/>
    
  </entry>
  
  <entry>
    <title>Kqueue学习笔记</title>
    <link href="http://andrewliu.in/2016/08/14/Kqueue%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>http://andrewliu.in/2016/08/14/Kqueue学习笔记/</id>
    <published>2016-08-14T02:33:53.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>想在Mac上造点小轮子, 然而epoll是在Linux平台独有的, 所以想到了用kqueue来替代. 记录一下自己的学习过程.</p>
</blockquote>
<h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><ul>
<li><code>kqueue</code>是在UNIX上高效的IO复用技术, 类比于linux平台中的<code>epoll</code>.</li>
<li>IO复用原理大概为: 网卡设备对应一个中断号, 当网卡收到网络端的消息的时候会向CPU发起中断请求, 然后CPU处理该请求. 通过驱动程序 进而操作系统得到通知, 系统然后通知epoll/kqueue, epoll/kqueue通知用户代码. </li>
</ul>
<a id="more"></a>
<h2 id="API"><a href="#API" class="headerlink" title="API"></a>API</h2><p>kqueue使用的头文件和api可以通过<code>man kqueue</code>来看到</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">#include &lt;sys/types.h&gt;</div><div class="line">#include &lt;sys/event.h&gt;</div><div class="line">#include &lt;sys/time.h&gt;</div></pre></td></tr></table></figure>
<ul>
<li><code>kqueue()</code>系统调用会创建一个新的内核消息队列并返回描述符.</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># 创建失败返回-1, 否则返回描述符</div><div class="line">int kqueue(void);</div></pre></td></tr></table></figure>
<ul>
<li>kqueue的数据结构</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">struct kevent &#123;</div><div class="line">    uintptr_t       ident;          /* identifier for this event, 该事件关联的文件描述符 */</div><div class="line">    int16_t         filter;         /* filter for event */</div><div class="line">    uint16_t        flags;          /* general flags, 用于指定事件操作类型, 比如EV_ADD, EV_ENABLE, EV_DELETE等, 通过|可以同时设置多个事件 */</div><div class="line">    uint32_t        fflags;         /* filter-specific flags */</div><div class="line">    intptr_t        data;           /* filter-specific data */</div><div class="line">    void            *udata;         /* opaque user data identifier */</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<ul>
<li><code>EV_SET()</code>在官方文档描述是一个宏,  用于初始化kevent数据结构</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">EV_SET(&amp;kev, ident, filter, flags, fflags, data, udata);</div><div class="line">// 使用范例, 将监听stdin描述符的可读事件初始化到ev数据结构</div><div class="line">kevent ev;</div><div class="line">EV_SET(&amp;ev, STDIN_FILENO, EVFILT_READ, EV_ADD, 0, 0, 0);</div></pre></td></tr></table></figure>
<ul>
<li><code>kevent</code>为核心函数, 初始时<code>kqueue</code>内核消息队列为空, 使用kevent进行事件填充, 在不设置超时参数时, 只有当收到某监听事件才会返回. 该函数返回接收到事件个数, 并将事件写入eventlist</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"># changelist为要注册的事件列表, eventlist用于返回已经就绪的事件列表</div><div class="line">int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout);</div></pre></td></tr></table></figure>
<h2 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div></pre></td><td class="code"><pre><div class="line">#include &lt;stdio.h&gt;          // fprintf</div><div class="line">#include &lt;sys/event.h&gt;      // kqueue</div><div class="line">#include &lt;netdb.h&gt;          // addrinfo</div><div class="line">#include &lt;arpa/inet.h&gt;      // AF_INET</div><div class="line">#include &lt;sys/socket.h&gt;     // socket</div><div class="line">#include &lt;assert.h&gt;         // assert</div><div class="line">#include &lt;string.h&gt;         // bzero</div><div class="line">#include &lt;stdbool.h&gt;        // bool</div><div class="line">#include &lt;unistd.h&gt;         // close</div><div class="line"></div><div class="line">const size_t BUF_SIZE = 1024;</div><div class="line">static bool s_stop = true;</div><div class="line">// 信号处理函数</div><div class="line">static void handle_signal(int sig) &#123;</div><div class="line">    s_stop = false;</div><div class="line">&#125;</div><div class="line"></div><div class="line">int learn_kqueue(const char* ip, int32_t port) &#123;</div><div class="line">    std::cout &lt;&lt; &quot;ip: &quot; &lt;&lt; ip &lt;&lt; &quot; port: &quot; &lt;&lt; port &lt;&lt; std::endl;</div><div class="line">    signal(SIGTERM, handle_signal);</div><div class="line">    int sock = socket(PF_INET, SOCK_STREAM, 0);</div><div class="line">    assert(sock &gt; 0);</div><div class="line"></div><div class="line">    // 强制使用TIME_WAIT状态的socket地址</div><div class="line">    int reuse = 1;</div><div class="line">    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &amp;reuse, sizeof(reuse));</div><div class="line"></div><div class="line">    struct sockaddr_in address;</div><div class="line">    bzero(&amp;address, sizeof(address));</div><div class="line">    address.sin_family = AF_INET;</div><div class="line">    inet_pton(AF_INET, ip, &amp;address.sin_addr); //主机序转网络序ip</div><div class="line">    address.sin_port = htons(port); //主机序转网络序</div><div class="line"></div><div class="line">    int ret = bind(sock, (struct sockaddr*)&amp;address, sizeof(address));</div><div class="line">    assert(ret != -1);</div><div class="line">    ret = listen(sock, BACK_LOG);</div><div class="line">    assert(ret != -1);</div><div class="line"></div><div class="line">    //创建一个消息队列并返回kqueue描述符</div><div class="line">    int kq =  kqueue();</div><div class="line">    assert(kq != -1);</div><div class="line"></div><div class="line">    struct kevent change_list[10];  //想要监控的事件</div><div class="line">    struct kevent event_list[10];  //用于kevent返回</div><div class="line">    char buf[BUF_SIZE];</div><div class="line">    // 监听sock的读事件</div><div class="line">    EV_SET(&amp;change_list[0], sock, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);</div><div class="line">    // 监听stdin的读事件</div><div class="line">    EV_SET(&amp;change_list[1], fileno(stdin), EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);</div><div class="line">    int nevents;</div><div class="line">    while (s_stop) &#123;</div><div class="line">        printf(&quot;new loop...\n&quot;);</div><div class="line">        // 等待监听事件的发生</div><div class="line">        nevents = kevent(kq, change_list, 2, event_list, 2, NULL);</div><div class="line">        if (nevents &lt; 0) &#123;</div><div class="line">            printf(&quot;kevent error.\n&quot;);  // 监听出错</div><div class="line">        &#125; else if (nevents &gt; 0) &#123;</div><div class="line">            printf(&quot;get events number: %d\n&quot;, nevents);</div><div class="line">            for (int i = 0; i &lt; nevents; ++i) &#123;</div><div class="line">                printf(&quot;loop index: %d\n&quot;, i);</div><div class="line">                struct kevent event = event_list[i]; //监听事件的event数据结构</div><div class="line">                int clientfd = (int) event.ident;  // 监听描述符</div><div class="line">                // 表示该监听描述符出错</div><div class="line">                if (event.flags &amp; EV_ERROR) &#123;</div><div class="line">                    close(clientfd);</div><div class="line">                    printf(&quot;EV_ERROR: %s\n&quot;, strerror(event_list[i].data));</div><div class="line">                &#125;</div><div class="line">                // 表示sock有新的连接</div><div class="line">                if (clientfd == sock) &#123;</div><div class="line">                    printf(&quot;new connection\n&quot;);</div><div class="line">                    struct sockaddr_in client_addr;</div><div class="line">                    socklen_t client_addr_len = sizeof(client_addr);</div><div class="line">                    int new_fd = accept(sock, (struct sockaddr *) &amp;client_addr, &amp;client_addr_len);</div><div class="line">                    char remote[INET_ADDRSTRLEN];</div><div class="line">                    printf(&quot;connected with ip: %s, port: %d\n&quot;,</div><div class="line">                           inet_ntop(AF_INET, &amp;client_addr.sin_addr, remote, INET_ADDRSTRLEN),</div><div class="line">                           ntohs(client_addr.sin_port));</div><div class="line">                &#125;</div><div class="line">                if (clientfd == fileno(stdin)) &#123;</div><div class="line">                    memset(buf, 0, BUF_SIZE);</div><div class="line">                    fgets(buf, BUF_SIZE, stdin);</div><div class="line">                    printf(&quot;data from stdin: %s\n&quot;, buf);</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    close(sock);</div><div class="line">    return 0;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><ul>
<li><a href="https://wiki.netbsd.org/tutorials/kqueue_tutorial/" target="_blank" rel="external">kqueue tutorial</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想在Mac上造点小轮子, 然而epoll是在Linux平台独有的, 所以想到了用kqueue来替代. 记录一下自己的学习过程.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Introduction&quot;&gt;&lt;a href=&quot;#Introduction&quot; class=&quot;headerlink&quot; title=&quot;Introduction&quot;&gt;&lt;/a&gt;Introduction&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kqueue&lt;/code&gt;是在UNIX上高效的IO复用技术, 类比于linux平台中的&lt;code&gt;epoll&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;IO复用原理大概为: 网卡设备对应一个中断号, 当网卡收到网络端的消息的时候会向CPU发起中断请求, 然后CPU处理该请求. 通过驱动程序 进而操作系统得到通知, 系统然后通知epoll/kqueue, epoll/kqueue通知用户代码. &lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>浅谈Raft</title>
    <link href="http://andrewliu.in/2016/08/06/%E6%B5%85%E8%B0%88Raft/"/>
    <id>http://andrewliu.in/2016/08/06/浅谈Raft/</id>
    <published>2016-08-06T06:10:48.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>工作忙了，总是骗自己说的没时间写博客，其实都是自己懒癌发作而已。题目写了浅谈，肯定文章是不够深入透彻的，刚刚爬进分布式门槛的婴儿，需要慢慢的学会走路。希望后面能通过自己总结再来一篇<code>深入理解Raft</code>(显然又在挖坑</p>
</blockquote>
<a id="more"></a>
<h2 id="一致性算法特性"><a href="#一致性算法特性" class="headerlink" title="一致性算法特性"></a>一致性算法特性</h2><ol>
<li><code>安全性保</code>证：在非拜占庭错误情况下，包括网络延迟、分区、丢包、冗余和乱序等错误都可以保证正确。</li>
<li><code>可用性</code>：集群中只要有大多数的机器可运行并且能够相互通信、和客户端通信，就可以保证可用。因此，一个典型的包含 5 个节点的集群可以容忍两个节点的失败。服务器被停止就认为是失败。他们当有稳定的存储的时候可以从状态中恢复回来并重新加入集群。<code>paxos在有2f + 1个节点时可以容忍f个节点失败</code>.</li>
<li><code>不依赖时序来保证一致性</code>：物理时钟错误或者极端的消息延迟在可能只有在最坏情况下才会导致可用性问题。</li>
<li>通常情况下，一条指令可以尽可能快的在集群中大多数节点响应一轮远程过程调用时完成。小部分比较慢的节点不会影响系统整体的性能。</li>
</ol>
<h2 id="Paxos算法"><a href="#Paxos算法" class="headerlink" title="Paxos算法"></a>Paxos算法</h2><p>提到Raft算法的同时, 总是无法避免的要说起Paxos, 在Raft论文中也提到, Raft算法初衷是为了解决Paxos难以理解而提出, 所以总要看看Paxos算法到底是个什么东西</p>
<p>Paxos算法的缺陷:</p>
<ol>
<li>Paxos 算法<code>难以理解</code></li>
<li>Paxos算法没有提供一个足够好用来构建一个现实系统的基础</li>
</ol>
<h2 id="Raft算法"><a href="#Raft算法" class="headerlink" title="Raft算法"></a>Raft算法</h2><blockquote>
<p>Raft是一种为了<code>管理复制日志</code>的<code>一致性算法</code></p>
</blockquote>
<ul>
<li>Raft 将一致性算法分解成了<code>领导人(leader)选举、日志复制和安全性</code>三个模块.</li>
</ul>
<p>Raft算法的特性:</p>
<ul>
<li><code>强领导者</code>：Raft 使用一种更强的领导能力形式。比如，日志条目只从领导者发送给其他的服务器。这种方式简化了对复制日志的管理并且使得 Raft 算法更加易于理解。</li>
<li><code>领导选举</code>：Raft 算法使用一个随机计时器来选举领导者。这种方式只是在任何一致性算法都必须实现的心跳机制上增加了一点机制。</li>
<li><code>关系调整</code>：Raft 使用一种共同一致的方法来处理集群成员变换的问题，在这种方法中，两种不同的配置都要求的大多数机器会重叠。这就使得集群在成员变换的时候依然可以继续工作。</li>
</ul>
<h3 id="领导人选举"><a href="#领导人选举" class="headerlink" title="领导人选举"></a>领导人选举</h3><p><img src="http://ww1.sinaimg.cn/mw690/ab508d3djw1f65cdl86xsj20r40yan8y.jpg" alt=""></p>
<ol>
<li>在任何时刻，每一个服务器节点都处于这三个状态之一：领导人(leader)、跟随者(follower)或者候选人(candidate). 跟随者都是被动的：他们不会发送任何请求，只是简单的响应来自领导者或者候选人的请求。领导人处理所有的客户端请求</li>
<li>Raft 把时间分割成任意长度的<code>任期(terms)</code>, 如上面图中的Figure 5, 任期用连续的整数标记。每一段任期从一次选举开始，一个或者多个候选人尝试成为领导者。如果一个候选人赢得选举，然后他就在接下来的任期内充当领导人的职责。在某些情况下，一次选举过程会造成选票的瓜分。在这种情况下，这一任期会以没有领导人结束(<code>t3</code>)；一个新的任期（和一次新的选举）会很快重新开始。Raft 保证了在一个给定的任期内，<code>最多只有一个</code>领导者</li>
<li>领导者<code>周期性的向所有跟随者发送心跳包</code>。如果一个跟随者在一段时间里没有接收到任何消息，也就是选举超时, 然后他就会认为系统中没有可用的领导者然后开始进行选举以选出新的领导者。</li>
<li>此跟随者先要增加自己的<code>当前任期号</code>并且<code>转换到候选人状态</code>, 开始进入<strong>领导人选举</strong>. 他会并行的向集群中的其他服务器节点发送RPC请求给自己投票。候选人会继续保持着当前状态直到以下三件事情之一发生：(a) 他自己赢得了这次的选举, 则向其他服务器发送心跳检测，(b) 其他的服务器成为领导者, 候选者选为跟随者，(c) 一段时间之后没有任何一个获胜的人, 每一个候选人都会超时，然后通过增加当前任期号来开始一轮新的选举。</li>
</ol>
<h3 id="日志复制"><a href="#日志复制" class="headerlink" title="日志复制"></a>日志复制</h3><p>日志复制具有以下特性: </p>
<ul>
<li>如果在不同的日志中的两个条目拥有相同的索引和任期号，那么他们存储了相同的指令。</li>
<li>如果在不同的日志中的两个条目拥有相同的索引和任期号，那么他们之前的所有日志条目也全部相同</li>
</ul>
<p>可能因为领导者在日志提交前挂掉, 导致所有的服务器中的日志不一定的情况. 领导人处理不一致是通过强制跟随者直接复制自己的日志来解决了.</p>
<p>领导人针对每一个跟随者维护了一个 <code>nextIndex</code>，这表示下一个需要发送给跟随者的日志条目的索引地址。当一个领导人刚获得权力的时候，他初始化所有的 nextIndex 值为自己日志中的最后一条。如果一个跟随者的日志和领导人不一致，那么在下一次的附加日志 RPC 时的一致性检查就会失败。在被跟随者拒绝之后，领导人就会减小 nextIndex 值并进行重试。最终 nextIndex 会在某个位置使得领导人和跟随者的日志达成一致。当这种情况发生，附加日志 RPC 就会成功，这时就会把跟随者冲突的日志条目全部删除并且加上领导人的日志。一旦附加日志 RPC 成功，那么跟随者的日志就会和领导人保持一致，并且在接下来的任期里一直继续保持。</p>
<h3 id="安全性"><a href="#安全性" class="headerlink" title="安全性"></a>安全性</h3><ol>
<li>选举限制,  候选人发起RPC 中包含了候选人的日志信息，投票人会拒绝掉那些日志没有自己新的投票请求</li>
<li>如果已经服务器已经在某个给定的索引值应用了日志条目到自己的状态机里，那么其他的服务器不会应用一个不一样的日志到同一个索引值上</li>
</ol>
<h2 id="日志压缩"><a href="#日志压缩" class="headerlink" title="日志压缩"></a>日志压缩</h2><p>目的是减少不断增长的日志所占用服务器空间大小</p>
<p>采用的方法如下图:<br><img src="http://ww3.sinaimg.cn/mw690/ab508d3djw1f65et9wva3j20p40legry.jpg" alt=""></p>
<h2 id="客户端交互"><a href="#客户端交互" class="headerlink" title="客户端交互"></a>客户端交互</h2><ol>
<li>客户端发送command给领导人, 若领导人未知，挑选任意节点，若该节点不是领导人，则重定向至领导人</li>
<li>领导人追加日志项，等待<code>提交</code>，更新本地状态机，最终响应客户端</li>
<li>若客户端超时，则不断重试，直至收到响应为止</li>
</ol>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="https://raft.github.io/" target="_blank" rel="external">Raft官网</a></li>
<li><a href="http://thesecretlivesofdata.com/raft/" target="_blank" rel="external">illustrated Raft guide</a></li>
<li>Paper: <code>In Search of an Understandable Consensus Algorithm</code></li>
<li><a href="http://blog.csdn.net/cszhouwei/article/details/38374603" target="_blank" rel="external">Raft一致性算法</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;工作忙了，总是骗自己说的没时间写博客，其实都是自己懒癌发作而已。题目写了浅谈，肯定文章是不够深入透彻的，刚刚爬进分布式门槛的婴儿，需要慢慢的学会走路。希望后面能通过自己总结再来一篇&lt;code&gt;深入理解Raft&lt;/code&gt;(显然又在挖坑&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="Go" scheme="http://andrewliu.in/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>shadowsocks源码剖析</title>
    <link href="http://andrewliu.in/2016/07/24/shadowsocks%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90/"/>
    <id>http://andrewliu.in/2016/07/24/shadowsocks源码剖析/</id>
    <published>2016-07-24T14:09:02.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>VPS上嗨皮的跑了那么久shadowsock的server了, 但一直比较想搞明白具体是怎么实现的. 前段时间比较忙, 最近总算有点私人时间了, 速度的强读一发shadowsocks源码</p>
</blockquote>
<h2 id="什么是shadowsocks"><a href="#什么是shadowsocks" class="headerlink" title="什么是shadowsocks?"></a>什么是shadowsocks?</h2><ul>
<li>不想再强行安利一发了, 毕竟开发者都被请去喝茶了</li>
<li>只需要知道和<code>G * F * W</code>有关就好了</li>
<li>如果上面你都不知道, 说明这篇文章与你无缘, 叉掉吧</li>
</ul>
<p>所读源码基于<a href="https://github.com/shadowsocks/shadowsocks-go" target="_blank" rel="external">shadowsocks-go 1.1.5</a>, 为什么选择golang实现版本? 我难道会告诉你, Golang的吉祥物太萌了, 我已经决定好好安利golang了. 正经点的回复, golang的语法灵活不失简洁, 棒棒哒.</p>
<a id="more"></a>
<h2 id="终入正题"><a href="#终入正题" class="headerlink" title="终入正题"></a>终入正题</h2><p>先来张图, 假装自己很专业…</p>
<p><img src="http://ww2.sinaimg.cn/large/ab508d3djw1f64vgco021j215m082jtz.jpg" alt=""></p>
<p><strong>流程概述:</strong></p>
<ol>
<li>shadowsocks包含local和server两个程序, local运行在当前登录想要翻墙的机器上, server运行在墙外的服务器上(比如我的server运行在<a href="http://www.vultr.com/?ref=6844015" target="_blank" rel="external">vultr家的VPS</a>上)</li>
<li>local监控本地1080端口, 提供socks v5协议接口, 浏览器请求和local的1080端口建立TCP连接, 首先进行local端对本机进程进行心跳检测, 然后与server端建立请求发出实际请求包(<code>目标的地址和端口</code>)</li>
<li>连接传输的数据通过加密,一般使用<code>aes-256-cfb</code></li>
<li>server端解密收到的数据, 然后与实际请求的目标ip和端口建立TCP连接, 将获取的数据写回到local端, local端最终写回到浏览器进程完成一次翻越长城.</li>
</ol>
<h2 id="show-me-your-code"><a href="#show-me-your-code" class="headerlink" title="show me your code"></a>show me your code</h2><blockquote>
<p>口说无凭, 也让人觉得云里雾里, 所以来是直接上代码吧</p>
</blockquote>
<p><strong>PS: 代码均进行了精简, 只保持核心逻辑</strong></p>
<ul>
<li>首先来看shadowsocks-local文件</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line">// local的入口</div><div class="line">func main() &#123;</div><div class="line">    /*</div><div class="line">     * 1. 设置日志配置和命令行参数解析</div><div class="line">     * 2. 从文件中读取相关配置, 如server ip, port, 用户设置的密码, 本地监控端口等</div><div class="line">     */</div><div class="line">    run(cmdLocal + &quot;:&quot; + strconv.Itoa(config.LocalPort))</div><div class="line">&#125;</div><div class="line"></div><div class="line">// 实际监控函数</div><div class="line">func run(listenAddr string) &#123;</div><div class="line">    /*</div><div class="line">     * 1. 设置要监控的端口</div><div class="line">     * 2. 在一个死循环中, 不断接收请求, 然后在goroutine中调用handleConnection(conn)处理请求</div><div class="line">     */</div><div class="line">    ln, err := net.Listen(&quot;tcp&quot;, listenAddr)</div><div class="line">    for &#123;  // 运行与一个死循环</div><div class="line">        conn, err := ln.Accept()</div><div class="line">        go handleConnection(conn)</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">// 运行在goroutine中请求处理函数</div><div class="line">func handleConnection(conn net.Conn) &#123;</div><div class="line">    /*</div><div class="line">     * 1. 与请求进程进行心跳检测</div><div class="line">     * 2. 解析请求获取原始数据, 用于获取实际请求ip和port</div><div class="line">     * 3. 与墙外的server请求建立TCP连接, 并发送数据</div><div class="line">     * 4. 等待墙外server写入数据, 然后将数据写回给请求进程</div><div class="line">     */</div><div class="line">    handShake(conn)</div><div class="line">    rawaddr, addr, err := getRequest(conn)</div><div class="line"></div><div class="line">    _, err = conn.Write([]byte&#123;0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43&#125;)</div><div class="line">    remote, err := createServerConn(rawaddr, addr)</div><div class="line"></div><div class="line">    go ss.PipeThenClose(conn, remote)</div><div class="line">    ss.PipeThenClose(remote, conn)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>最后我们来看一下 <code>PipeThenClose</code>这个函数的实现</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line">// PipeThenClose copies data from src to dst, closes dst when done.</div><div class="line">func PipeThenClose(src, dst net.Conn) &#123;</div><div class="line">    // closes dst when done.</div><div class="line">    defer dst.Close()</div><div class="line">    /*</div><div class="line">     * 1. 从leaky buffer中获取空闲buffer</div><div class="line">     * 2. 从source net.Conn中读取数据, 然后转发给dst net.Conn</div><div class="line">     */</div><div class="line">    buf := leakyBuf.Get()</div><div class="line">    defer leakyBuf.Put(buf)</div><div class="line">    for &#123;</div><div class="line">        SetReadTimeout(src)</div><div class="line">        n, err := src.Read(buf)</div><div class="line">        // read may return EOF with n &gt; 0</div><div class="line">        // should always process n &gt; 0 bytes before handling error</div><div class="line">        if n &gt; 0 &#123;</div><div class="line">            // Note: avoid overwrite err returned by Read.</div><div class="line">            if _, err := dst.Write(buf[0:n]); err != nil &#123;</div><div class="line">                Debug.Println(&quot;write:&quot;, err)</div><div class="line">                break</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        if err != nil &#123;</div><div class="line">            break</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<ul>
<li>再来看shadowsocks-server代码, 你会发现两者惊人的相似</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">// 主要来handleConnection</div><div class="line">// goroutine, 内部需要自行保证线程安全</div><div class="line">func handleConnection(conn *ss.Conn, auth bool) &#123;</div><div class="line">    /* </div><div class="line">     * 1. 解析请求的ip:port</div><div class="line">     * 2. 建立TCP连接</div><div class="line">     * 3. 向目标站点发起请求, 响应数据写会到local</div><div class="line">     */</div><div class="line">    host, ota, err := getRequest(conn, auth)</div><div class="line"></div><div class="line">    //对应ip:port建立TCP连接</div><div class="line">    remote, err := net.Dial(&quot;tcp&quot;, host)</div><div class="line"></div><div class="line">    if ota &#123;</div><div class="line">        go ss.PipeThenCloseOta(conn, remote)</div><div class="line">    &#125; else &#123;</div><div class="line">        go ss.PipeThenClose(conn, remote)</div><div class="line">    &#125;</div><div class="line">    // 从remote读取数据</div><div class="line">    ss.PipeThenClose(remote, conn)</div><div class="line">    closed = true</div><div class="line">    return</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>由local和server端总共四个<code>PipeThenClose</code>函数完成了由local到server, 再由server到local的数据传输. 但是要注意的是在<code>server端是没有与local进行心跳检测的阶段</code></p>
<h2 id="socks-v5"><a href="#socks-v5" class="headerlink" title="socks v5"></a>socks v5</h2><p>socks v5代码TCP协议的流程:</p>
<ol>
<li>用户进程与监听1080端口的进程建立TCP连接</li>
<li>心跳检测, 用户进程向监听进程发送<code>VER, NMETHODS, METHODS</code>, 监听进程向请求进程响应<code>VER, METHOD</code></li>
<li>用户进程结束心跳检测, 向监听进程发起请求, 格式如下</li>
<li>监听进程对用户进程响应, 表示成功, 则表示用户进程可以进行发送数据.</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">向监听进程的请求:</div><div class="line">　  +----+-----+-------+------+----------+----------+</div><div class="line">　　|VER | CMD |　RSV　| ATYP | DST.ADDR | DST.PORT |</div><div class="line">　　+----+-----+-------+------+----------+----------+</div><div class="line">　　| 1　| 　1 | X&apos;00&apos; | 　1　| Variable |　　 2　　|</div><div class="line">　　+----+-----+-------+------+----------+----------+</div><div class="line">监听进程的响应: </div><div class="line">　　+----+-----+-------+------+----------+----------+</div><div class="line">　　|VER | REP |　RSV　| ATYP | BND.ADDR | BND.PORT |</div><div class="line">　　+----+-----+-------+------+----------+----------+</div><div class="line">　　| 1　|　1　| X&apos;00&apos; |　1 　| Variable | 　　2　　|</div><div class="line">　　+----+-----+-------+------+----------+----------+</div><div class="line"></div><div class="line">// 对应shadowsocks-local中的:</div><div class="line">_, err = conn.Write([]byte&#123;0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43&#125;)</div></pre></td></tr></table></figure>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="https://github.com/shadowsocks/shadowsocks-go" target="_blank" rel="external">shadowsocks-go 1.1.5</a></li>
<li><a href="https://www.ietf.org/rfc/rfc1928.txt" target="_blank" rel="external">socks v5 协议的 RFC</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;VPS上嗨皮的跑了那么久shadowsock的server了, 但一直比较想搞明白具体是怎么实现的. 前段时间比较忙, 最近总算有点私人时间了, 速度的强读一发shadowsocks源码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;什么是shadowsocks&quot;&gt;&lt;a href=&quot;#什么是shadowsocks&quot; class=&quot;headerlink&quot; title=&quot;什么是shadowsocks?&quot;&gt;&lt;/a&gt;什么是shadowsocks?&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;不想再强行安利一发了, 毕竟开发者都被请去喝茶了&lt;/li&gt;
&lt;li&gt;只需要知道和&lt;code&gt;G * F * W&lt;/code&gt;有关就好了&lt;/li&gt;
&lt;li&gt;如果上面你都不知道, 说明这篇文章与你无缘, 叉掉吧&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所读源码基于&lt;a href=&quot;https://github.com/shadowsocks/shadowsocks-go&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;shadowsocks-go 1.1.5&lt;/a&gt;, 为什么选择golang实现版本? 我难道会告诉你, Golang的吉祥物太萌了, 我已经决定好好安利golang了. 正经点的回复, golang的语法灵活不失简洁, 棒棒哒.&lt;/p&gt;
    
    </summary>
    
    
      <category term="Go" scheme="http://andrewliu.in/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Golang RPC</title>
    <link href="http://andrewliu.in/2016/07/02/Golang-RPC/"/>
    <id>http://andrewliu.in/2016/07/02/Golang-RPC/</id>
    <published>2016-07-02T15:18:02.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p>RPC(Remote Procedure Call)是<code>分布式系统</code>中的一个关键机制.</p>
</blockquote>
<h2 id="Go-HTTP-RPC"><a href="#Go-HTTP-RPC" class="headerlink" title="Go HTTP RPC"></a>Go HTTP RPC</h2><a id="more"></a>
<figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 基于官方的example</span></div><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line">    <span class="string">"fmt"</span></div><div class="line">    <span class="string">"net"</span></div><div class="line">    <span class="string">"net/rpc"</span></div><div class="line">    <span class="string">"log"</span></div><div class="line">    <span class="string">"net/http"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="keyword">type</span> Args <span class="keyword">struct</span> &#123;  <span class="comment">// 参数封装</span></div><div class="line">    A, B <span class="keyword">int</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">type</span> Arith <span class="keyword">int</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Arith)</span> <span class="title">Multiply</span><span class="params">(args *Args, reply *<span class="keyword">int</span>)</span> <span class="title">error</span></span> &#123;</div><div class="line">    *reply = args.A * args.B</div><div class="line">    <span class="keyword">return</span> <span class="literal">nil</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">makeService</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">/*</span></div><div class="line">     func Register(rcvr interface&#123;&#125;) error</div><div class="line">     将Service(Arith)注册到默认的Server中</div><div class="line">      */</div><div class="line">    rpc.Register(<span class="built_in">new</span>(Arith))</div><div class="line">    <span class="comment">/*</span></div><div class="line">     func HandleHTTP()</div><div class="line">     RPC消息有HTTP Handler来处理, Handler注册到默认的Server</div><div class="line">      */</div><div class="line">    rpc.HandleHTTP()</div><div class="line">    <span class="comment">/*</span></div><div class="line">     func Listen(net, laddr string) (Listener, error)</div><div class="line">     第一个参数为面向流的网络("tcp", "tcp4", "tcp6", "unix", "unixpacket").</div><div class="line">     第二个参数为字符串形式的地址, 如"127.0.0.1:1234", 省略host表示可以监听所有host</div><div class="line">      */</div><div class="line">    l, e := net.Listen(<span class="string">"tcp"</span>, <span class="string">":1234"</span>)</div><div class="line">    <span class="keyword">if</span> e != <span class="literal">nil</span> &#123;</div><div class="line">        log.Fatal(<span class="string">"listen error:"</span>, e)</div><div class="line">    &#125;</div><div class="line">    <span class="comment">/*</span></div><div class="line">    func Serve(l net.Listener, handler Handler) error</div><div class="line">     */</div><div class="line">    <span class="keyword">go</span> http.Serve(l, <span class="literal">nil</span>)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">makeClient</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">/*</span></div><div class="line">    func DialHTTP(network, address string) (*Client, error)</div><div class="line">    参数类似于net.Listen</div><div class="line">     */</div><div class="line">    client, err := rpc.DialHTTP(<span class="string">"tcp"</span>, <span class="string">"127.0.0.1"</span> + <span class="string">":1234"</span>)</div><div class="line">    <span class="keyword">defer</span> client.Close()  <span class="comment">// 延迟关闭client</span></div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        log.Fatal(<span class="string">"dialing:"</span>, err)</div><div class="line">    &#125;</div><div class="line">    args := &amp;Args&#123;<span class="number">7</span>, <span class="number">8</span>&#125;</div><div class="line">    <span class="keyword">var</span> reply <span class="keyword">int</span></div><div class="line">    <span class="comment">/*</span></div><div class="line">    func (client *Client) Call(serviceMethod string, args interface&#123;&#125;, reply interface&#123;&#125;) error</div><div class="line">    client的方法, 第一个参数为调用服务的方法, 第二个参数方法需要的参数, 第三个参数为执行结果</div><div class="line">     */</div><div class="line">    err = client.Call(<span class="string">"Arith.Multiply"</span>, args, reply)</div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        log.Fatal(<span class="string">"Arith rpc call error:"</span>, err)</div><div class="line">    &#125;</div><div class="line">    fmt.Println(reply)</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">// 只是为了一个启动使用了goroutine，正常情况应该分别启动一个客户端和一个服务器</span></div><div class="line">    makeService()</div><div class="line">    makeClient()</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="Go-JSON-RPC"><a href="#Go-JSON-RPC" class="headerlink" title="Go JSON RPC"></a>Go JSON RPC</h2><figure class="highlight go"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> main</div><div class="line"></div><div class="line"><span class="keyword">import</span> (</div><div class="line">    <span class="string">"fmt"</span></div><div class="line">    <span class="string">"net"</span></div><div class="line">    <span class="string">"net/rpc"</span></div><div class="line">    <span class="string">"log"</span></div><div class="line">    <span class="string">"net/http"</span></div><div class="line">    <span class="string">"net/rpc/jsonrpc"</span></div><div class="line">)</div><div class="line"></div><div class="line"><span class="keyword">type</span> Args <span class="keyword">struct</span> &#123;  <span class="comment">// 参数封装</span></div><div class="line">    A, B <span class="keyword">int</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">type</span> Arith <span class="keyword">int</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Arith)</span> <span class="title">Multiply</span><span class="params">(args *Args, reply *<span class="keyword">int</span>)</span> <span class="title">error</span></span> &#123;</div><div class="line">    *reply = args.A * args.B</div><div class="line">    <span class="keyword">return</span> <span class="literal">nil</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 使用json作为rpc</span></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">makeJSONService</span><span class="params">()</span></span> &#123;</div><div class="line">    rpc.Register(<span class="built_in">new</span>(Arith))</div><div class="line">    tcpAddr, err := net.ResolveTCPAddr(<span class="string">"tcp"</span>, <span class="string">":1234"</span>)</div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        <span class="built_in">panic</span>(err)</div><div class="line">    &#125;</div><div class="line">    <span class="comment">/*</span></div><div class="line">     ListenTCP 类似于 Listen. func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error)</div><div class="line">     第一个参数为面向流的网络("tcp", "tcp4", or "tcp6").</div><div class="line">     第二个参数为字符串形式的地址, 如"127.0.0.1:1234", 省略host表示可以监听所有host</div><div class="line">    */</div><div class="line">    listener, err := net.ListenTCP(<span class="string">"tcp"</span>, tcpAddr)</div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        <span class="built_in">panic</span>(err)</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">go</span> <span class="function"><span class="keyword">func</span> <span class="params">()</span></span> &#123;</div><div class="line">        <span class="keyword">for</span> &#123;</div><div class="line">            conn, err := listener.Accept()</div><div class="line">            <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">                <span class="keyword">continue</span></div><div class="line">            &#125;</div><div class="line">            <span class="comment">// 注意此处的不同</span></div><div class="line">            <span class="comment">/*</span></div><div class="line">            func ServeConn(conn io.ReadWriteCloser)</div><div class="line">            此函数阻塞运行一个JSON-RPC</div><div class="line">             */</div><div class="line">            jsonrpc.ServeConn(conn)</div><div class="line">        &#125;</div><div class="line">    &#125;()</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">func</span> <span class="title">makeJSONClient</span><span class="params">()</span></span> &#123;</div><div class="line">    <span class="comment">// JSON-rpc的客户端基本和TCP一样</span></div><div class="line">    client, err := jsonrpc.Dial(<span class="string">"tcp"</span>, <span class="string">"127.0.0.1:1234"</span>)</div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        log.Fatal(<span class="string">"dialing:"</span>, err)</div><div class="line">    &#125;</div><div class="line">    <span class="comment">// Synchronous call</span></div><div class="line">    args := &amp;Args&#123;<span class="number">17</span>, <span class="number">8</span>&#125;</div><div class="line">    <span class="keyword">var</span> reply <span class="keyword">int</span></div><div class="line">    err = client.Call(<span class="string">"Arith.Multiply"</span>, args, &amp;reply)</div><div class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</div><div class="line">        log.Fatal(<span class="string">"Arith rpc call error:"</span>, err)</div><div class="line">    &#125;</div><div class="line">    fmt.Println(reply)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h2 id="6-824-labrpc结构"><a href="#6-824-labrpc结构" class="headerlink" title="6.824 labrpc结构"></a>6.824 labrpc结构</h2><p><strong>数据结构</strong>:</p>
<ul>
<li><code>Network</code>用于模拟网络, 使整个RPC调用过程中在内部发生, 不需要真正的执行网路请求</li>
<li><code>Server</code>一个RPC Server, 内部包含多个注册的Service</li>
<li><code>ClientEnd</code>表示RPC客户端的数据结构</li>
<li><code>Service</code>用于客户端来注册struct, 然后封装入该数据结构</li>
<li><code>reqSrc</code> RPC调用的参数被解析然后重新封装到整个数据结构中</li>
<li><code>replyMsg</code>封装RPC调用的返回结果</li>
</ul>
<p><strong>架构(可以选择性忽略Network部分)</strong></p>
<p><img src="http://ww4.sinaimg.cn/large/ab508d3djw1f2rxpft969j20i30c176z.jpg" alt=""></p>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="http://blog.brucefeng.info/post/what-is-rpc" target="_blank" rel="external">RPC是什么</a></li>
<li><a href="https://pdos.csail.mit.edu/6.824/notes/l-rpc.txt" target="_blank" rel="external">6.824 2016 Lecture 2: Infrastructure: RPC and threads</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RPC(Remote Procedure Call)是&lt;code&gt;分布式系统&lt;/code&gt;中的一个关键机制.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Go-HTTP-RPC&quot;&gt;&lt;a href=&quot;#Go-HTTP-RPC&quot; class=&quot;headerlink&quot; title=&quot;Go HTTP RPC&quot;&gt;&lt;/a&gt;Go HTTP RPC&lt;/h2&gt;
    
    </summary>
    
    
      <category term="Go" scheme="http://andrewliu.in/tags/Go/"/>
    
  </entry>
  
  <entry>
    <title>Tornado源码剖析</title>
    <link href="http://andrewliu.in/2016/06/19/Tornado%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90/"/>
    <id>http://andrewliu.in/2016/06/19/Tornado源码剖析/</id>
    <published>2016-06-19T15:05:25.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="pdb调试"><a href="#pdb调试" class="headerlink" title="pdb调试"></a>pdb调试</h2><blockquote>
<p>简单的介绍一下pdb的调试, 更详细的命令查看<a href="https://docs.python.org/2/library/pdb.html" target="_blank" rel="external">python pdbf官方文档</a></p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">import pdb</div><div class="line">// 使用以下语句希望debug的地方打断点</div><div class="line">pdb.set_trace()</div></pre></td></tr></table></figure>
<table>
<thead>
<tr>
<th style="text-align:center">命令</th>
<th style="text-align:center">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">n</td>
<td style="text-align:center">运行下一行代码</td>
</tr>
<tr>
<td style="text-align:center">p</td>
<td style="text-align:center">计算p后面的表达式(当前上下文中), 并打印表达式的值</td>
</tr>
<tr>
<td style="text-align:center">s</td>
<td style="text-align:center">进入函数</td>
</tr>
<tr>
<td style="text-align:center">r</td>
<td style="text-align:center">从函数中返回</td>
</tr>
<tr>
<td style="text-align:center">b</td>
<td style="text-align:center">动态设置断点</td>
</tr>
<tr>
<td style="text-align:center">w</td>
<td style="text-align:center">打印当前栈信息</td>
</tr>
<tr>
<td style="text-align:center">q</td>
<td style="text-align:center">退出pdb</td>
</tr>
</tbody>
</table>
<a id="more"></a>
<h2 id="Tornado剖析"><a href="#Tornado剖析" class="headerlink" title="Tornado剖析"></a>Tornado剖析</h2><blockquote>
<p><code>Tornado</code> is a Python web framework and asynchronous networking library, originally developed at FriendFeed. By using <code>non-blocking network I/O</code>, Tornado can scale to tens of thousands of open connections, making it ideal for long polling, WebSockets, and other applications that require a long-lived connection to each user.</p>
</blockquote>
<p><strong>源码剖析基于Tornado 2.0.0及以下测试源码</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line">#!/usr/bin/env python</div><div class="line"># -*- coding: utf-8 -*-</div><div class="line">import tornado.web</div><div class="line">import tornado.httpserver</div><div class="line">import tornado.ioloop</div><div class="line">import tornado.options</div><div class="line">import os.path</div><div class="line"></div><div class="line">from tornado.options import define, options</div><div class="line">define(&quot;port&quot;, default=8000, help=&quot;run on the given port&quot;, type=int)</div><div class="line"></div><div class="line"></div><div class="line"># 继承Application</div><div class="line">class Application(tornado.web.Application):</div><div class="line"></div><div class="line">    def __init__(self):</div><div class="line">        handlers = [</div><div class="line">            (r&quot;/&quot;, MainHandler),</div><div class="line">        ]</div><div class="line">        settings = dict(</div><div class="line">            template_path=os.path.join(os.path.dirname(__file__), &quot;templates&quot;),</div><div class="line">            static_path=os.path.join(os.path.dirname(__file__), &quot;static&quot;),</div><div class="line">            debug=True,</div><div class="line">        )</div><div class="line">        tornado.web.Application.__init__(self, handlers, **settings)</div><div class="line"></div><div class="line"></div><div class="line"># URI Hanlder逻辑</div><div class="line">class MainHandler(tornado.web.RequestHandler):</div><div class="line">    def get(self):</div><div class="line">        import pdb</div><div class="line">        pdb.set_trace()</div><div class="line">        self.write(&quot;Hello, World&quot;)</div><div class="line"></div><div class="line"></div><div class="line">def run():</div><div class="line">    tornado.options.parse_command_line()</div><div class="line">    http_server = tornado.httpserver.HTTPServer(Application())</div><div class="line">    http_server.listen(options.port)</div><div class="line">    print &quot;Start server, http://localhost:%s&quot; % options.port</div><div class="line">    tornado.ioloop.IOLoop.instance().start()</div><div class="line"></div><div class="line"></div><div class="line">if __name__ == &quot;__main__&quot;:</div><div class="line">    run()</div></pre></td></tr></table></figure>
<p>首先运行一上源码(服务器端), 另外开启一个<code>Terminal</code>来发出请求(也可以使用浏览器来访问). 服务端收到请求后, 会在我们<code>pdb.set_trace()</code>停下等待调试.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line">// 新的Terminal命令行执行以下命令</div><div class="line">$ curl http://localhost:8000</div><div class="line"></div><div class="line">// 此时服务器端会进入`pdb`调试状态. 输入w以获得当前调用栈信息如下:</div><div class="line">(Pdb) w</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/bin/run(13)&lt;module&gt;()</div><div class="line">-&gt; sys.exit(learn.learn_source.run())</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/learn/learn_source.py(41)run()</div><div class="line">-&gt; tornado.ioloop.IOLoop.instance().start()</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/ioloop.py(233)start()</div><div class="line">-&gt; self._run_callback(callback)</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/ioloop.py(370)_run_callback()</div><div class="line">-&gt; callback()</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/stack_context.py(159)wrapped()</div><div class="line">-&gt; callback(*args, **kwargs)</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/iostream.py(235)wrapper()</div><div class="line">-&gt; callback(*args)</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/stack_context.py(159)wrapped()</div><div class="line">-&gt; callback(*args, **kwargs)</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/httpserver.py(400)_on_headers()</div><div class="line">-&gt; self.request_callback(self._request)</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/web.py(1282)__call__()</div><div class="line">-&gt; handler._execute(transforms, *args, **kwargs)</div><div class="line">  /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/.buildout/eggs/tornado-2.0-py2.7.egg/tornado/web.py(927)_execute()</div><div class="line">-&gt; getattr(self, self.request.method.lower())(*args, **kwargs)</div><div class="line">&gt; /Users/andrew_liu/Development/BackEnd/Python3/TornadoToturial/src/learn/learn_source.py(33)get()</div><div class="line">-&gt; self.write(&quot;Hello, World&quot;)</div></pre></td></tr></table></figure>
<p>一. 首先<code>Application</code>调用<code>__init__</code>, 通过<code>self.add_handlers(&quot;.*$&quot;, handlers)</code>配置路由, 配置静态资源资源路径.<br>二. <code>tornado.httpserver.HTTPServer</code>接收一个<code>Application</code>参数并命名为<code>request_callback</code>, <strong>注意此处Application实例被命名为request_callback</strong> 留意<code>HTTPServer</code>中有一个属性<code>self._sockets</code>保存<code>fd</code>到<code>socket object</code>的映射.<br>三. 调用<code>HTTPServer</code>的<code>listen(options.port)</code>方法开始做socket监听. listen中做了两件事. 一: bind创建socket对象设置并设置非阻塞, 并进行<code>socket.bind()</code>和<code>socket.listen()</code>监听. 并在<code>self._sockets</code>保存socket描述符和socket对象的映射. 二: <code>start</code>函数初始化一个<code>IOLoop</code>对象(单例对象), 并遍历<code>self._sockets</code>socket描述符, 将其添加到<code>IOLoop</code>并绑定读事件</p>
<blockquote>
<p>为了方便查看, 本来去掉源码中的异常捕获和一些干扰阅读的细节</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"># 步骤三中的核心代码</div><div class="line"></div><div class="line"># 步骤三第一部分</div><div class="line">class HTTPServer(object):</div><div class="line"></div><div class="line">    def bind(self, port, address=None, family=socket.AF_UNSPEC)</div><div class="line">        # 网编编程的一套, socket =&gt; bind =&gt; listen</div><div class="line">        sock = socket.socket(af, socktype, proto)</div><div class="line">        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)</div><div class="line">        sock.setblocking(0)</div><div class="line">        sock.bind(sockaddr)</div><div class="line">        sock.listen(128)</div><div class="line">        self._sockets[sock.fileno()] = sock</div><div class="line"></div><div class="line"></div><div class="line">    # 步骤三第二部分</div><div class="line">    def start(self, num_processes=1)</div><div class="line">        if not self.io_loop:</div><div class="line">            self.io_loop = ioloop.IOLoop.instance()  # 返回一个全局的IOLoop对象</div><div class="line">        for fd in self._sockets.keys():  # 对整个fd和socket对象的映射集合, 都加入到epoll, 并注册回调</div><div class="line">            self.io_loop.add_handler(fd, self._handle_events, ioloop.IOLoop.READ)  # 给每个fd绑定读事件, self._handle_events为回调事件(当fd可读的时候, 则调用此函数)</div></pre></td></tr></table></figure>
<p>四. <code>tornado.ioloop.IOLoop.instance().start()</code>中通过步骤三中全局的IOLoop对象执行<code>start</code>. 此处核心代码为<code>event_pairs = self._impl.poll(0.2)</code>. 其中<code>self._impl</code>根据不同平台来选择<code>select/poll/epoll/kqueue</code>, 每当有事件可读时, 则执行其回调函数<code>self._handle_events</code>. <strong>整个回调函数为HTTPServer中的handler_events函数</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"># 步骤四start中的核心代码</div><div class="line">class IOLoop(object):</div><div class="line">    def start(self)</div><div class="line">        while True:</div><div class="line">            event_pairs = self._impl.poll(poll_timeout)  # epoll对触发读事件的描述符返回, 此处说明有客户端访问服务器</div><div class="line">            self._events.update(event_pairs)  # 将要处理的事件更新到self._events这个dict中</div><div class="line">            while self._events:</div><div class="line">                fd, events = self._events.popitem()  # 随机取出一个事件(key-value)</div><div class="line">                self._handlers[fd](fd, events)  # self._handlers是在第三步中add_handler中添加的, self._handlers是fd描述符和self._handle_events形成的映射(dcit). 此处从self._handlers中取出fd描述符对应的self._handle_events执行.</div></pre></td></tr></table></figure>
<p>五. 最后我们发现, 让了一圈又回到<code>HTTPServer._handle_events</code>函数上. 函数中创建了IOStream对象和HTTPConnection对象.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"># 步骤五的核心代码</div><div class="line"></div><div class="line"># 当我们访问http://localhost:8000, epoll返回事件</div><div class="line">class HTTPServer(object):</div><div class="line">    def _handle_events(fd, event):</div><div class="line">        while True:</div><div class="line">            connection, address = self._sockets[fd].accept()  # 此处connection为客户端连接</div><div class="line">            stream = iostream.IOStream(connection, io_loop=self.io_loop)</div><div class="line">            HTTPConnection(stream, address, self.request_callback, self.no_keep_alive, self.xheaders)</div></pre></td></tr></table></figure>
<p>其中<code>iostream.IOstream</code>回通过client socket(<code>connection</code>)来初始化,  并且完成将fd注册到<code>IOLoop的epoll</code>中. 当fd可读时, 会调用<code>IOStream._handle_events</code>函数做回调, 描述符可读, 即客户端发送了HTTP, 读取客户端发送的数据写入IOStream中的<code>self._read_buffer</code>读缓冲区中.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">class IOStream(object):</div><div class="line">    def __init__(self, socket, io_loop=None, max_buffer_size=104857600, read_chunk_size=4096):</div><div class="line">        self.socket = socket</div><div class="line">        self.socket.setblocking(False)</div><div class="line">        self.io_loop = io_loop or ioloop.IOLoop.instance()</div><div class="line">        self.max_buffer_size = max_buffer_size</div><div class="line">        self.read_chunk_size = read_chunk_size</div><div class="line">        self._state = self.io_loop.ERROR</div><div class="line">        with stack_context.NullContext():</div><div class="line">            self.io_loop.add_handler(</div><div class="line">                self.socket.fileno(), self._handle_events, self._state)  # 将client socket注册到io_loop中, 并绑定回调事件self.handle_events(IOStream)</div><div class="line">                </div><div class="line">    def _handle_events(self, fd, events):</div><div class="line">        if events &amp; self.io_loop.READ:  # 当客户端有链接时, 则event可读</div><div class="line">            self._handle_read()</div><div class="line">    </div><div class="line">    def _handle_read(self):</div><div class="line">        while True:</div><div class="line">            result = self._read_to_buffer()  # 将数据接入读缓冲区self._read_buffer中</div></pre></td></tr></table></figure>
<p>然后通过iostream来初始化<code>HTTPConnection</code>, 注意HTTPConnection中的<code>self.request_callback</code>属性就是在HTTPServer初始化时的<code>Application</code>. 其中执行<code>self.stream.read_until</code>并读取缓冲区的数据, 然后回调<code>HTTPConnection._on_headers</code>解析HTTP请求的头部, 然后<strong>出现了最重要的一步!!!</strong>, <code>self.request_callback(self._request)</code>, 终于出现了, 这是最吼的! <code>self.request_callback</code>就是我们用来初始化<code>HTTPServer</code>的<code>Application</code>对象呀!!! 这货有个黑魔法函数<code>__call__</code>, 可以直接对Application对象进行传参调用!!! 传入客户端的request请求到Application中.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"># 这个类执行真正的客户端请求处理</div><div class="line">class HTTPConnection(object):</div><div class="line">    &quot;&quot;&quot;Handles a connection to an HTTP client, executing HTTP requests.</div><div class="line">    &quot;&quot;&quot;</div><div class="line">    def __init__(self, stream, address, request_callback, no_keep_alive=False, xheaders=False):</div><div class="line">        self.stream = stream  # IOStream包含数据缓冲区</div><div class="line">        self.address = address</div><div class="line">        self.request_callback = request_callback  # Application类</div><div class="line">        self.no_keep_alive = no_keep_alive</div><div class="line">        self.xheaders = xheaders</div><div class="line">        self._request = None </div><div class="line">        self._header_callback = stack_context.wrap(self._on_headers)</div><div class="line">        self.stream.read_until(b(&quot;\r\n\r\n&quot;), self._header_callback)  # 通过指定分隔符从self.stream中读取数据, 然后回调self._header_callback, 即self._on_headers</div><div class="line">        </div><div class="line">    def _on_headers(self, data):</div><div class="line">        self._request = HTTPRequest(connection=self, method=method, uri=uri, version=version,headers=headers, remote_ip=self.address[0])  # 对请求的头部进行解析, 然后生成HTTPRequest对象, 注意此处的connection(HTTPConnection), 向客户端发送数据会用到</div><div class="line">        self.request_callback(self._request)  # 最后终于出现self.request_callback了!!! 这就是Application呀!! 执行他的黑魔法__call__方法</div></pre></td></tr></table></figure>
<p>六. 重新看栈信息<code>self.request_callback(self._request) web.py(1282)__call__()</code>完全符合我们的分析. <code>__call__</code>接收<code>self._request</code>, 其中通过<code>_request</code>中host信息作路径匹配, 如果路径相匹配则返回我们创建的<code>Handler类</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">class Application(object):</div><div class="line">    def __call__(self, request):</div><div class="line">        handlers = self._get_host_handlers(request)  # 获取与host匹配的handler</div><div class="line">        for spec in handlers:</div><div class="line">            match = spec.regex.match(request.path)</div><div class="line">            if match:</div><div class="line">                handler = spec.handler_class(self, request, **spec.kwargs)  # 找到最初我们注册的MainHandler并创建实例.</div><div class="line">        handler._execute(transforms, *args, **kwargs)</div><div class="line">        </div><div class="line">    def _get_host_handlers(self, request):</div><div class="line">        host = request.host.lower().split(&apos;:&apos;)[0]</div><div class="line">        for pattern, handlers in self.handlers:</div><div class="line">            if pattern.match(host):</div><div class="line">                return handlers</div></pre></td></tr></table></figure>
<p>我们再次查看栈信息, 发现此时的handler即为<code>MainHandler</code>, 并执行对象的<code>_execute</code>方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">-&gt; handler._execute(transforms, *args, **kwargs)</div><div class="line">(Pdb) l</div><div class="line">1277                 if getattr(RequestHandler, &quot;_templates&quot;, None):</div><div class="line">1278                     for loader in RequestHandler._templates.values():</div><div class="line">1279                         loader.reset()</div><div class="line">1280                 RequestHandler._static_hashes = &#123;&#125;</div><div class="line">1281</div><div class="line">1282 -&gt;            handler._execute(transforms, *args, **kwargs)</div><div class="line">1283             return handler</div><div class="line">1284</div><div class="line">1285         def reverse_url(self, name, *args):</div><div class="line">1286             &quot;&quot;&quot;Returns a URL path for handler named `name`</div><div class="line">1287</div><div class="line">(Pdb) print handler</div><div class="line">&lt;learn.learn_source.MainHandler object at 0x1054c7210&gt;</div></pre></td></tr></table></figure>
<p>七. <code>handler._execute</code>找到<code>request</code>中的HTTP方法, 然后执行对应的函数. 我们在MainHandler中实现了get方法, 此处会调用对应的方法. 并将数据发送给客户端.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div></pre></td><td class="code"><pre><div class="line">class RequestHandler(object):</div><div class="line">    def _execute(self, transforms, *args, **kwargs):</div><div class="line">        self.prepare()   # 次数调用了prepre方法</div><div class="line">        if not self._finished:</div><div class="line">            args = [self.decode_argument(arg) for arg in args]</div><div class="line">            kwargs = dict((k, self.decode_argument(v, name=k)) for (k,v) in kwargs.iteritems())</div><div class="line">            getattr(self, self.request.method.lower())(*args, **kwargs)  # 获取对应的get/post等方法的具体实现并执行.</div><div class="line">            if self._auto_finish and not self._finished:</div><div class="line">                self.finish()  # 数据的返回由finish完成</div><div class="line">    </div><div class="line">    def finish(self, chunk=None):</div><div class="line">        # 中间执行一些构造响应头部的操作</div><div class="line">        if not self.application._wsgi:</div><div class="line">            self.flush(include_footers=True)  # 数据刷新操作.</div><div class="line">            self.request.finish()  # 移除客户端的相关操作</div><div class="line">            self._log()</div><div class="line">        self._finished = True</div><div class="line">    </div><div class="line">    def flush(self, include_footers=False):</div><div class="line">        # Ignore the chunk and only write the headers for HEAD requests</div><div class="line">        if self.request.method == &quot;HEAD&quot;:</div><div class="line">            if headers: self.request.write(headers)</div><div class="line">            return</div><div class="line">        if headers or chunk:</div><div class="line">            self.request.write(headers + chunk) # write执行写入操作</div><div class="line"></div><div class="line">class HTTPRequest(object):</div><div class="line">    def write(self, chunk):</div><div class="line">        &quot;&quot;&quot;Writes the given chunk to the response stream.&quot;&quot;&quot;</div><div class="line">        assert isinstance(chunk, bytes_type)</div><div class="line">        self.connection.write(chunk)  # 此处connection是一个HTTPConnection</div><div class="line"></div><div class="line">class HTTPConnection(object):</div><div class="line">    def write(self, chunk):</div><div class="line">        &quot;&quot;&quot;Writes a chunk of output to the stream.&quot;&quot;&quot;</div><div class="line">        assert self._request, &quot;Request closed&quot;</div><div class="line">        if not self.stream.closed():</div><div class="line">            self.stream.write(chunk, self._on_write_complete)  # 实现写入操作有IOStream完成.</div><div class="line"></div><div class="line"># IOStream是对客户端socket的封装</div><div class="line">class IOStream(object):</div><div class="line">    def write(self, data, callback=None):</div><div class="line">        &quot;&quot;&quot;Write the given data to this stream.</div><div class="line">        &quot;&quot;&quot;</div><div class="line">        assert isinstance(data, bytes_type)</div><div class="line">        self._check_closed()</div><div class="line">        self._write_buffer.append(data)</div><div class="line">        self._add_io_state(self.io_loop.WRITE)  # 这里改变为WRITE, 则执行写操作</div><div class="line">        self._write_callback = stack_context.wrap(callback)</div><div class="line">    </div><div class="line">    def _handle_events(self, fd, events):</div><div class="line">        if events &amp; self.io_loop.WRITE:</div><div class="line">            if self._connecting:</div><div class="line">                self._handle_connect()</div><div class="line">            self._handle_write()</div><div class="line"></div><div class="line">    def _handle_write(self):</div><div class="line">        while self._write_buffer:</div><div class="line">            num_bytes = self.socket.send(self._write_buffer[0])  # 终于完成了数据发送</div></pre></td></tr></table></figure>
<p>八. 完成数据写入client socket后, <code>HTTPRequest.finish()</code>被调用执行移除时间和关闭客户端socket操作. 是不是已经晕了, 稍等我们来画个图来理一理整个流程.</p>
<p><img src="http://ww1.sinaimg.cn/large/ab508d3djw1f2yxuks8orj20im0mnaem.jpg" alt=""></p>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="http://blog.csdn.net/zhaoxia_guo/article/details/6921572" target="_blank" rel="external">Tornado: 1. 流程分析</a></li>
<li><a href="http://www.jianshu.com/p/6d8dfbf5dcf5" target="_blank" rel="external">Tornado 源码分析 - 基础篇</a></li>
<li><a href="http://www.yeolar.com/note/2013/02/09/tornado-core/" target="_blank" rel="external">Tornado核心框架</a></li>
<li><a href="https://github.com/jiajunhuang/blog/blob/master/tornado.rst?hmsr=toutiao.io&amp;utm_medium=toutiao.io&amp;utm_source=toutiao.io" target="_blank" rel="external">Tornado 源码阅读</a></li>
<li><a href="https://www.ibm.com/developerworks/cn/linux/l-cn-pythondebugger/" target="_blank" rel="external">Python 代码调试技巧</a></li>
<li><a href="https://www.zhihu.com/question/37271342/answer/81607536" target="_blank" rel="external">为什么 IO 多路复用要搭配非阻塞 IO?
</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;pdb调试&quot;&gt;&lt;a href=&quot;#pdb调试&quot; class=&quot;headerlink&quot; title=&quot;pdb调试&quot;&gt;&lt;/a&gt;pdb调试&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;简单的介绍一下pdb的调试, 更详细的命令查看&lt;a href=&quot;https://docs.python.org/2/library/pdb.html&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;python pdbf官方文档&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;import pdb&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;// 使用以下语句希望debug的地方打断点&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;pdb.set_trace()&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;命令&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;n&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;运行下一行代码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;p&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;计算p后面的表达式(当前上下文中), 并打印表达式的值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;s&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;进入函数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;r&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;从函数中返回&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;b&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;动态设置断点&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;w&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;打印当前栈信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;q&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;退出pdb&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
    
    </summary>
    
    
      <category term="Python" scheme="http://andrewliu.in/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Google protobuf(C++) 学习笔记</title>
    <link href="http://andrewliu.in/2016/06/05/Google-protobuf-C-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    <id>http://andrewliu.in/2016/06/05/Google-protobuf-C-学习笔记/</id>
    <published>2016-06-05T10:26:17.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<blockquote>
<p> <code>Google Protocol Buffers</code>简称 Protobuf, 是<code>Google</code>公司内部的混合语言数据标准. 它提供一种轻量, 高效的结构化数据存储结构. </p>
</blockquote>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p><strong>为什么学习protobuf?</strong></p>
<p>想通过protobuf的序列化来做一个C++的微型RPC框架.<br>本文主要是学习protobuf的使用, 大量参考官方文档.</p>
<a id="more"></a>
<p><strong>为什么要使用protobuf?</strong></p>
<ol>
<li>因为<code>protobuf</code>是谷歌出的, 性能不过, 重要是的我是<code>谷歌脑残粉</code>(逃</li>
<li>官方文档中提到一些protobuf的优点, protobuf灵活高效的结构化数据存储格式. 方便用于序列化, 适合做RPC的数据交换.</li>
<li>相比<code>XML</code>, <code>protobuf</code>比 XML 更小、更快、更简单. 仅需要写一个<code>*.proto</code>文件描述需要的数据结构, protobuf会帮助你实现相关类和方法(自动化多好!).</li>
<li>目前提供<code>C++, Java, Python, Go, C#等多种语言</code>的API</li>
</ol>
<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p><strong>神奇Mac版homebrew帮你解决一切问题</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"># 安装最新的protobuf</div><div class="line">$ brew install protobuf</div><div class="line"></div><div class="line"># 验证protobuf是否安装成功</div><div class="line">$ protoc --version</div><div class="line"># 在我电脑上的输出</div><div class="line">libprotoc 3.0.0</div></pre></td></tr></table></figure>
<h2 id="学习笔记"><a href="#学习笔记" class="headerlink" title="学习笔记"></a>学习笔记</h2><p><strong>通过一个简单的官方例子来学习protobuf</strong></p>
<p>我们首先定义一个<code>addressbook.proto</code>, 通过<code>message</code>来定义需要序列化的数据结构, message内包含许多key-value对</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"># addressbook.proto</div><div class="line">package tutorial;</div><div class="line"></div><div class="line">message Person &#123;</div><div class="line">  required string name = 1;</div><div class="line">  required int32 id = 2;</div><div class="line">  optional string email = 3;</div><div class="line"></div><div class="line">  enum PhoneType &#123;</div><div class="line">    MOBILE = 0;</div><div class="line">    HOME = 1;</div><div class="line">    WORK = 2;</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  message PhoneNumber &#123;</div><div class="line">    required string number = 1;</div><div class="line">    optional PhoneType type = 2 [default = HOME];</div><div class="line">  &#125;</div><div class="line"></div><div class="line">  repeated PhoneNumber phone = 4;</div><div class="line">&#125;</div><div class="line"></div><div class="line">message AddressBook &#123;</div><div class="line">  repeated Person person = 1;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><strong>ps:</strong></p>
<ol>
<li><code>message</code>中有多种类型, <code>bool, int32, float, double, ,string and enum</code>以及内嵌的<code>message</code></li>
<li><code>message</code>内可以定义optional、required、repeated字段</li>
</ol>
<p><strong>定义好*.proto文件后, 我们通过protobuf提供的protoc命令行工具来自动生成相关数据结构源码</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">// 编译.proto文件</div><div class="line">$ protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto</div><div class="line"></div><div class="line">// 范例, 源目录为当前文件, 输出路径为当前文件</div><div class="line">$ protoc -I=./ --cpp_out=./ ./addressbook.proto</div></pre></td></tr></table></figure>
<ul>
<li><code>-I</code>指明源文件所在目录</li>
<li><code>--cpp_out</code>指向想要输出的文件路径</li>
<li>最后一个参数为<code>.proto</code>所在的路径</li>
</ul>
<h2 id="使用protobuf"><a href="#使用protobuf" class="headerlink" title="使用protobuf"></a>使用protobuf</h2><p>生成我们自定义的类后, 我们开始尝试使用生成的文件. <code>add_person.cpp</code>用于从用户的输入中, 创建一个Person并初始化一个Person对象, 并将该对象通过二进制的形式写入给定文件中.</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 代码出自官方文档</span></div><div class="line"><span class="comment">// add_person.cpp</span></div><div class="line"></div><div class="line"><span class="comment">//</span></div><div class="line"><span class="comment">// Created by Andrew_liu on 16/4/17.</span></div><div class="line"><span class="comment">//</span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;fstream&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"addressbook.pb.h"</span></span></div><div class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</div><div class="line"></div><div class="line"><span class="comment">// 基于用户输出填充Person message</span></div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">PromptForAddress</span><span class="params">(tutorial::Person* person)</span> </span>&#123;</div><div class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"Enter person ID number: "</span>;</div><div class="line">    <span class="keyword">int</span> id;</div><div class="line">    <span class="built_in">cin</span> &gt;&gt; id;</div><div class="line">    person-&gt;set_id(id);</div><div class="line">    <span class="built_in">cin</span>.ignore(<span class="number">256</span>, <span class="string">'\n'</span>);</div><div class="line"></div><div class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"Enter name: "</span>;</div><div class="line">    getline(<span class="built_in">cin</span>, *person-&gt;mutable_name());</div><div class="line"></div><div class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"Enter email address (blank for none): "</span>;</div><div class="line">    <span class="built_in">string</span> email;</div><div class="line">    getline(<span class="built_in">cin</span>, email);</div><div class="line">    <span class="keyword">if</span>(!email.empty()) &#123;</div><div class="line">        person-&gt;set_email(email);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</div><div class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Enter a phone number: "</span>;</div><div class="line">        <span class="built_in">string</span> number;</div><div class="line">        getline(<span class="built_in">cin</span>, number);</div><div class="line">        <span class="keyword">if</span>(number.empty()) &#123;</div><div class="line">            <span class="keyword">break</span>;</div><div class="line">        &#125;</div><div class="line">        tutorial::Person::PhoneNumber* phone_number = person-&gt;add_phone();</div><div class="line">        phone_number-&gt;set_number(number);</div><div class="line"></div><div class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Is this a mobile, home, or work phone(type: mobile/home/work): "</span>;</div><div class="line">        <span class="built_in">string</span> type;</div><div class="line">        getline(<span class="built_in">cin</span>, type);</div><div class="line">        <span class="keyword">if</span> (type == <span class="string">"mobile"</span>) &#123;</div><div class="line">            phone_number-&gt;set_type(tutorial::Person::MOBILE);</div><div class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (type == <span class="string">"home"</span>) &#123;</div><div class="line">            phone_number-&gt;set_type(tutorial::Person::HOME);</div><div class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (type == <span class="string">"work"</span>) &#123;</div><div class="line">            phone_number-&gt;set_type(tutorial::Person::WORK);</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            <span class="built_in">cout</span> &lt;&lt; <span class="string">"Unknown phone type. Using default."</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>&#123;</div><div class="line">    GOOGLE_PROTOBUF_VERIFY_VERSION;</div><div class="line">    <span class="keyword">if</span> (argc != <span class="number">2</span>) &#123;</div><div class="line">        <span class="built_in">cerr</span> &lt;&lt; <span class="string">"Usage: "</span> &lt;&lt; argv[<span class="number">0</span>] &lt;&lt; <span class="string">" ADDRESS_BOOK_FILE"</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line">    &#125;</div><div class="line">    tutorial::AddressBook address_book;</div><div class="line"></div><div class="line">    &#123;</div><div class="line">        <span class="function">fstream <span class="title">input</span><span class="params">(argv[<span class="number">1</span>], ios::in | ios::binary)</span></span>;</div><div class="line">        <span class="keyword">if</span> (!input) &#123;</div><div class="line">            <span class="built_in">cout</span> &lt;&lt; argv[<span class="number">1</span>] &lt;&lt; <span class="string">": File not found. Creating a new file. "</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!address_book.ParseFromIstream(&amp;input)) &#123;</div><div class="line">            <span class="built_in">cerr</span> &lt;&lt; <span class="string">"Failed to parse address book."</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">            <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">// add an address</span></div><div class="line">    PromptForAddress(address_book.add_person());</div><div class="line"></div><div class="line">    &#123;</div><div class="line">        <span class="comment">// write new address book back to disk</span></div><div class="line">        <span class="function">fstream <span class="title">output</span><span class="params">(argv[<span class="number">1</span>], ios::out | ios::trunc | ios::binary)</span></span>;</div><div class="line">        <span class="keyword">if</span>(!address_book.SerializeToOstream(&amp;output)) &#123;</div><div class="line">            <span class="built_in">cerr</span> &lt;&lt; <span class="string">"Failed to write address book."</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">            <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">// delete all global object allocated by libprotobuf</span></div><div class="line">    google::protobuf::ShutdownProtobufLibrary();</div><div class="line">    <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><code>list_person.cpp</code>通过给定的文件名读取Person对象, 并将每个Person对象输出.</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// list_person.cpp</span></div><div class="line"><span class="comment">// Created by Andrew_liu on 16/4/17.</span></div><div class="line"><span class="comment">//</span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;fstream&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"addressbook.pb.h"</span></span></div><div class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">ListPeople</span><span class="params">(<span class="keyword">const</span> tutorial::AddressBook&amp; address_book)</span> </span>&#123;</div><div class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;  i &lt; address_book.person_size(); i++) &#123;</div><div class="line">        <span class="keyword">const</span> tutorial::Person&amp; person = address_book.person(i);</div><div class="line"></div><div class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Person ID: "</span> &lt;&lt; person.id() &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Name: "</span> &lt;&lt; person.name() &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        <span class="keyword">if</span> (person.has_email()) &#123;</div><div class="line">            <span class="built_in">cout</span> &lt;&lt; <span class="string">"E-mail address: "</span> &lt;&lt; person.email() &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; person.phone_size(); ++j) &#123;</div><div class="line">            <span class="keyword">const</span> tutorial::Person::PhoneNumber&amp; phone_number = person.phone(j);</div><div class="line"></div><div class="line">            <span class="keyword">switch</span> (phone_number.type()) &#123;</div><div class="line">                <span class="keyword">case</span> tutorial::Person::MOBILE:</div><div class="line">                    <span class="built_in">cout</span> &lt;&lt; <span class="string">"Mobile phone #: "</span>;</div><div class="line">                    <span class="keyword">break</span>;</div><div class="line">                <span class="keyword">case</span> tutorial::Person::HOME:</div><div class="line">                    <span class="built_in">cout</span> &lt;&lt; <span class="string">"Home phone #: "</span>;</div><div class="line">                    <span class="keyword">break</span>;</div><div class="line">                <span class="keyword">case</span> tutorial::Person::WORK:</div><div class="line">                    <span class="built_in">cout</span> &lt;&lt; <span class="string">"Work phone #: "</span>;</div><div class="line">            &#125;</div><div class="line">            <span class="built_in">cout</span> &lt;&lt; phone_number.number() &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>&#123;</div><div class="line">    GOOGLE_PROTOBUF_VERIFY_VERSION;</div><div class="line"></div><div class="line">    <span class="keyword">if</span> (argc != <span class="number">2</span>) &#123;</div><div class="line">        <span class="built_in">cerr</span> &lt;&lt; <span class="string">"Usage:  "</span> &lt;&lt; argv[<span class="number">0</span>] &lt;&lt; <span class="string">" ADDRESS_BOOK_FILE"</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    tutorial::AddressBook address_book;</div><div class="line"></div><div class="line">    &#123;</div><div class="line">        <span class="function">fstream <span class="title">input</span><span class="params">(argv[<span class="number">1</span>], ios::in | ios::binary)</span></span>;</div><div class="line">        <span class="keyword">if</span> (!address_book.ParseFromIstream(&amp;input)) &#123;</div><div class="line">            <span class="built_in">cerr</span> &lt;&lt; <span class="string">"Failed to parse address book."</span> &lt;&lt; <span class="built_in">endl</span>;</div><div class="line">            <span class="keyword">return</span> <span class="number">-1</span>;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    ListPeople(address_book);</div><div class="line">    google::protobuf::ShutdownProtobufLibrary();</div><div class="line"></div><div class="line">    <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>完成后, 我们分别编译两个源文件, 编译命令如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"># 编译add_person.cpp, 生成writer可执行文件</div><div class="line">$ g++ -o writer add_person.cpp addressbook.pb.cc `pkg-config --cflags --libs protobuf`</div><div class="line"></div><div class="line"># 编译list_person.cpp, 生成reader可执行文件</div><div class="line">g++ -o reader list_person.cpp addressbook.pb.cc `pkg-config --cflags --libs protobuf`</div></pre></td></tr></table></figure>
<p>图片为执行<code>writer</code>和<code>reader</code>的结果</p>
<p><img src="http://ww3.sinaimg.cn/large/ab508d3djw1f2zvi7x4hxj21kw0ef45h.jpg" alt=""></p>
<h2 id="实现RPC"><a href="#实现RPC" class="headerlink" title="实现RPC"></a>实现RPC</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">package news;</div><div class="line">message NewsRequest &#123;</div><div class="line">    required <span class="built_in">string</span> message = <span class="number">1</span>;</div><div class="line">&#125;;</div><div class="line">message NewsResponse &#123;</div><div class="line">    required <span class="built_in">string</span> response = <span class="number">1</span>;</div><div class="line">&#125;;</div><div class="line">service NewsService &#123;</div><div class="line">    <span class="function">rpc <span class="title">News</span><span class="params">(NewsRequest)</span> <span class="title">returns</span> <span class="params">(NewsResponse)</span></span>;</div><div class="line">&#125;;</div></pre></td></tr></table></figure>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="https://github.com/google/protobuf/tree/master/src" target="_blank" rel="external">C++ Installation and Compile</a></li>
<li><a href="https://developers.google.com/protocol-buffers/docs/cpptutorial#why-use-protocol-buffers" target="_blank" rel="external">Protocol Buffer Basics: C++</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt; &lt;code&gt;Google Protocol Buffers&lt;/code&gt;简称 Protobuf, 是&lt;code&gt;Google&lt;/code&gt;公司内部的混合语言数据标准. 它提供一种轻量, 高效的结构化数据存储结构. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;简介&quot;&gt;&lt;a href=&quot;#简介&quot; class=&quot;headerlink&quot; title=&quot;简介&quot;&gt;&lt;/a&gt;简介&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;为什么学习protobuf?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;想通过protobuf的序列化来做一个C++的微型RPC框架.&lt;br&gt;本文主要是学习protobuf的使用, 大量参考官方文档.&lt;/p&gt;
    
    </summary>
    
    
      <category term="C++" scheme="http://andrewliu.in/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>The Google File System</title>
    <link href="http://andrewliu.in/2016/05/15/The-Google-File-System/"/>
    <id>http://andrewliu.in/2016/05/15/The-Google-File-System/</id>
    <published>2016-05-15T15:28:55.000Z</published>
    <updated>2017-06-17T22:14:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循<a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh" target="_blank" rel="external">署名-非商业用途-保持一致</a>的创作共用协议.</p>
<p><img src="http://ww1.sinaimg.cn/large/ab508d3djw1f2sj4b9nasj20q80begnu.jpg" alt=""></p>
<blockquote>
<p>GFS是一个共享的分布式文件系统</p>
</blockquote>
<ul>
<li>GFS提供了一个与位置无关的名字空间(namespace), 这使得数据可以为了负载均衡或灾难冗余等目的在不同位置间透明迁移</li>
<li>GFS并没有在文件系统层面提供任何Cache机制, 但借助了Linux底层cache机制.</li>
<li>GFS文件以分层目录的形式组织, 用路径名来表示, 文件的写入主要依赖追加(append)操作完成</li>
</ul>
<a id="more"></a>
<h2 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h2><ul>
<li>GFS集群包含<code>单Master</code>和多个<code>chunkservers</code></li>
<li>文件被划分为固定大小的块(chunk, 默认为64MB), 在块被创建时, 由Master分配给块的全局唯一不可变的64位的块句柄(chunk handle), 每个块默认存储三个副本到不同的chunserver上(容错)</li>
<li>Master维护所有的文件系统元数据. 这些元数据包括命名空间、访问控制信息、文件到块的映射信息、以及当前块的位置(命令空间, 文件到块映射和chunk所有副本的位置为Master的三大原信息存储在内存中保证访问速度, 命名空间和文件到块的映射会持久化到操作日志中). Master节点还控制着系统范围内的活动, 比如, 块租用管理、孤立块的垃圾回收、以及块在chunkserver间的迁移。Master用<code>心跳信息(HeartBeat messages)</code>周期地和每个chunkserver通讯, 向chunkserver发送指令并收集其状态.</li>
<li><strong>无论是客户端还是chunkserver都不需要缓存文件数据</strong>, chunkserver由linux自带的缓存策略来完成缓存</li>
</ul>
<h2 id="通信流程"><a href="#通信流程" class="headerlink" title="通信流程:"></a>通信流程:</h2><ol>
<li>GFS client使用固定的块大小(fix chunk size)将应用程序指定的文件名和字节偏移转换成文件中的块索引(chunk index)</li>
<li>GFS client向master发送request(包含文件名和块索引)询问应该与那些chunkservers通信</li>
<li>master 响应对应的块句柄(chunk handle)和块副本位置信息</li>
<li>GFS client发送request到某个副本(距离最近的一个). request中包含快句柄和块内字节范围. 对同一个块的进一步读取不再需要client和master的交互了, 直到client缓存(可以通信的chunkservers, 从步骤3中缓存)信息过期或者文件被重新打开</li>
</ol>
<h2 id="一致性模型"><a href="#一致性模型" class="headerlink" title="一致性模型"></a>一致性模型</h2><p>GFS是松一致性模型(<code>a relaxed consistency model</code>)</p>
<ul>
<li>文件命名空间的变更(如文件创建）是原子性的. 它们只能由master控制：<code>命名空间锁(namspace locking)</code>保证了原子性和正确性</li>
</ul>
<h2 id="系统交互"><a href="#系统交互" class="headerlink" title="系统交互"></a>系统交互</h2><p><img src="http://ww1.sinaimg.cn/large/ab508d3djw1f2w02a0xi3j20oi0m4q5o.jpg" alt=""></p>
<p><strong>控制流</strong></p>
<ol>
<li>客户机询问master哪一个chunkserver持有该块当前的租约以及其它副本的位置. 如果没有chunkserver持有租约, master将租约授权给它选择的副本.</li>
<li>master将<code>主副本(被master授予租约的副本)的标识符以及其它副本（次级副本）的位置返回</code>给客户机. 客户机为将来的变更缓存这些数据. 只有在主副本不可达, 或者其回应它已不再持有租约的时候,客户机才需要再一次联系master</li>
<li>客户机将数据推送到所有副本.客户机可以以任意的顺序推送数据. chunkserver将数据存储在内部LRU 缓存中, 直到数据被使用或者过期. 通过<code>从控制流解耦数据流</code>, 我们可以基于网络拓扑而不管哪个chunksever上有主副本, 通过调度昂贵的数据流来提高系统性能</li>
<li>当所有的副本都确认接收到了数据, 客户机对主副本发送写请求. 这个请求标识了早前推送到所有副本的数据. 主副本为接收到的所有变更分配连续的序列号, 由于变更可能来自多个客户机, 这就提供了必要的序列化. 它以序列号的顺序把变更应用到它自己的本地状态中(并保存变更顺序)</li>
<li>主副本将写请求转发到所有的次级副本. 每个次级副本依照主副本分配的序列顺序应用变更</li>
<li>所有次级副本回复主副本并标明它们已经完成了操作</li>
<li>主副本回复客户机. <code>任何副本遇到的任何错误都报告给客户机</code>. 出错的情况下,写操作可能在主副本和次级副本的任意子集上执行成功(如果在主副本失败, 就不会分配序列号和转发). 客户端请求被认定为失败, 被修改的域处于不一致的状态. 我们的客户机代码通过重试失败的变更来处理这样的错误.在退到从头开始重试之前, 客户机会将<code>从步骤3到步骤7</code>做几次重试</li>
</ol>
<hr>
<ul>
<li>步骤3描述将数据流从控制流中解耦. 在控制流从客户机到主副本再到所有次级级副本的同时, 数据以管道的方式, <code>线性</code>地的沿着一个精心挑选的chunkserver链推送(push). 这样可以充分利用每个机器的带宽.</li>
<li>GFS提供了一种叫做<code>记录追加(record append)</code>的原子追加操作(类似UNIX以<code>O_APPEND模式</code>打开文件)</li>
<li>快照操作(<code>the snapshot operation</code>)几乎瞬间完成对一个文件或者目录树的拷贝, 并且最小化对正在进行的变更的任何干扰. 快照使用标准的<code>写时复制</code>来实现.</li>
</ul>
<h2 id="Master操作"><a href="#Master操作" class="headerlink" title="Master操作"></a>Master操作</h2><ol>
<li>Master的许多操作消耗大量时间, 为了不阻塞其他操作, 允许同时执行多个操作, 使用<code>名称空间域上的锁(读写锁)</code>来保证正确的串行化(serialization)</li>
<li>GFS在文件删除后物理空间的回收是由<code>垃圾收集(garbage collection)</code>完成的</li>
</ol>
<blockquote>
<p>看到这里有个小疑惑, Master失效了会怎么选举?</p>
</blockquote>
<h2 id="容错和诊断"><a href="#容错和诊断" class="headerlink" title="容错和诊断"></a>容错和诊断</h2><blockquote>
<p>快速恢复和备份来实现GFS集群的高可用性</p>
</blockquote>
<ul>
<li>快速回复, 使用直接kill系统中进程的方式来关闭服务器, 未完成的客户端请求则需要进行重连和重试.</li>
<li>块拷贝, <code>chunk</code>副本默认会保证三个</li>
<li>Master拷贝, 此处回答了我的疑惑, 当Master挂掉, 首先尽快重启, 如果Master机器的硬件故障, 则监控系统会启动一个新的master进程. <code>master中数据和状态的恢复通过操作日志(operation log)和检查点(checkpoints)</code>, 并且master操作日志和检查点都在多台机器上备份. 另外还有一种<code>影子master(shadow master)策略</code>来不保证在主master挂掉后, 系统依然可用.</li>
<li>诊断工具, 诊断日志(diagnostic log), RPC日志来帮助定位问题, debug和性能分析.</li>
</ul>
<p><strong>GFS的优点:</strong></p>
<ol>
<li>支持大规模的读取和写入</li>
<li>高吞吐量</li>
<li>对数据(chunk)有良好的容错策略</li>
</ol>
<p><strong>GFS的缺点:</strong></p>
<ol>
<li>Master的容错策略略有不足</li>
<li>chunk默认64MB, 对小文件不友好.</li>
</ol>
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
<li><a href="https://pdos.csail.mit.edu/6.824/papers/gfs.pdf" target="_blank" rel="external">GFS Paper</a></li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh&quot; target=&quot;_blank&quot; rel=&quot;external&quot;&gt;署名-非商业用途-保持一致&lt;/a&gt;的创作共用协议.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://ww1.sinaimg.cn/large/ab508d3djw1f2sj4b9nasj20q80begnu.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GFS是一个共享的分布式文件系统&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;GFS提供了一个与位置无关的名字空间(namespace), 这使得数据可以为了负载均衡或灾难冗余等目的在不同位置间透明迁移&lt;/li&gt;
&lt;li&gt;GFS并没有在文件系统层面提供任何Cache机制, 但借助了Linux底层cache机制.&lt;/li&gt;
&lt;li&gt;GFS文件以分层目录的形式组织, 用路径名来表示, 文件的写入主要依赖追加(append)操作完成&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
      <category term="分布式" scheme="http://andrewliu.in/tags/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    
  </entry>
  
</feed>
