<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>basic coder</title>
	<atom:link href="http://basiccoder.com/feed" rel="self" type="application/rss+xml" />
	<link>http://basiccoder.com</link>
	<description>I am just a coder...</description>
	<lastBuildDate>Wed, 04 Jan 2012 08:38:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Common Lisp使用iolib进行网络编程</title>
		<link>http://basiccoder.com/network-programing-in-common-lisp-using-iolib.html</link>
		<comments>http://basiccoder.com/network-programing-in-common-lisp-using-iolib.html#comments</comments>
		<pubDate>Wed, 04 Jan 2012 08:38:24 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Lisp]]></category>
		<category><![CDATA[Common Lisp]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[quicklisp]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1022</guid>
		<description><![CDATA[Common Lisp进行网络编程可用的库还是挺多的，比较常用的库有<a href="http://common-lisp.net/project/usocket/">usocket</a>和<a href="http://common-lisp.net/project/iolib/">iolib</a>，usocket我简单了解了一下没有真正拿来用，它的API比较简单，文档写得比较全面，相比之下，iolib要比usocket强大的多，但缺点是文档太少，官方的文档可用的内容非常少，但如果能阅读一下iolib的相关源码，就会发现其实iolib是一个很强大的网络编程库，其中包含了DNS解析，socket基本操作（bind,listen等等），IO多路复用以及通常用来做IPC的socketpair，而且iolib的multiplex用起来有种libevent的感觉，用iolib可以实现一般的应用层网络编程，至于是否支持raw socket，我还没仔细研究，不过感觉应该问题不大。

<strong>1.iolib的安装</strong>
使用<a href="http://www.cliki.net/ASDF-Install">asdf-install</a>可以在线安装iolib，但貌似asdf-install不会自动解决包的依赖问题，最近才发现原来asdf-install其实已经是一个废弃的项目，官方已经不推荐使用了，在cliki的asdf-install首页最开头就有一句醒目的提示语：


<blockquote>
ASDF-install is OBSOLETE. DO NOT USE ASDF-INSTALL, EVER. DO NOT ASK AROUND ABOUT HOW TO GET IT RUNNING. IT IS O-B-S-O-L-E-T-E. Not working. Not maintained. Please use quicklisp instead.</blockquote... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>Common Lisp进行网络编程可用的库还是挺多的，比较常用的库有<a href="http://common-lisp.net/project/usocket/">usocket</a>和<a href="http://common-lisp.net/project/iolib/">iolib</a>，usocket我简单了解了一下没有真正拿来用，它的API比较简单，文档写得比较全面，相比之下，iolib要比usocket强大的多，但缺点是文档太少，官方的文档可用的内容非常少，但如果能阅读一下iolib的相关源码，就会发现其实iolib是一个很强大的网络编程库，其中包含了DNS解析，socket基本操作（bind,listen等等），IO多路复用以及通常用来做IPC的socketpair，而且iolib的multiplex用起来有种libevent的感觉，用iolib可以实现一般的应用层网络编程，至于是否支持raw socket，我还没仔细研究，不过感觉应该问题不大。</p>
<p><strong>1.iolib的安装</strong><br />
使用<a href="http://www.cliki.net/ASDF-Install">asdf-install</a>可以在线安装iolib，但貌似asdf-install不会自动解决包的依赖问题，最近才发现原来asdf-install其实已经是一个废弃的项目，官方已经不推荐使用了，在cliki的asdf-install首页最开头就有一句醒目的提示语：</p>
<blockquote><p>
ASDF-install is OBSOLETE. DO NOT USE ASDF-INSTALL, EVER. DO NOT ASK AROUND ABOUT HOW TO GET IT RUNNING. IT IS O-B-S-O-L-E-T-E. Not working. Not maintained. Please use quicklisp instead.</p></blockquote>
<p>取而代之的是<a href="http://www.quicklisp.org/beta/index.html">quicklisp</a>，之前就有人跟我推荐过quicklisp我还没来得及尝试，这几天试了下确实非常方便，可以自动地下载程序包及其依赖的相关程序包，无需手工解决依赖问题，让我想到debian的apt-get，关于quicklisp的安装和使用都非常简单，在它首页上都有使用说明。而且quicklisp几乎每个月都会在<a href="http://blog.quicklisp.org/">官方blog</a>上放出过去的几个月程序包的下载排行（如：<a href="http://blog.quicklisp.org/search?updated-min=2011-01-01T00:00:00-05:00&#038;updated-max=2012-01-01T00:00:00-05:00&#038;max-results=29">Project download stats for November</a>），可以在选择程序包的时候有个参考。</p>
<p><strong>2.创建passive socket</strong></p>
<p>当创建一个用于充当server角色的程序时通常需要创建passive socket，用来监听客户端的连接，关于socket编程的基本步骤已经是大家所熟知的了，create socket,bind,listen,accept等等，iolib是使用cffi(<a href="http://common-lisp.net/project/cffi/">The Common Foreign Function Interface</a>)通过调用linux系统调用来实现的，因此和用C语言编程几乎是一个套路，方法如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setq</span> socket
      <span style="color: #66cc66;">&#40;</span>make-socket
       <span style="color: #66cc66;">:</span><span style="color: #555;">connect</span> <span style="color: #66cc66;">:</span><span style="color: #555;">passive</span>
       <span style="color: #66cc66;">:</span><span style="color: #555;">address-family</span> <span style="color: #66cc66;">:</span><span style="color: #555;">internet</span>
       <span style="color: #66cc66;">:</span><span style="color: #555;">type</span> <span style="color: #66cc66;">:</span><span style="color: #555;">stream</span>
       <span style="color: #66cc66;">:</span><span style="color: #555;">external-format</span> '<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">utf-</span><span style="color: #cc66cc;">8</span> <span style="color: #66cc66;">:</span><span style="color: #555;">eol-style</span> <span style="color: #66cc66;">:</span><span style="color: #555;">crlf</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>bind-address socket
              <span style="color: #66cc66;">&#40;</span>ensure-address <span style="color: #ff0000;">&quot;127.0.0.1&quot;</span><span style="color: #66cc66;">&#41;</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">port</span> <span style="color: #cc66cc;">1086</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">reuse-addr</span> t<span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>listen-on socket <span style="color: #66cc66;">:</span><span style="color: #555;">backlog</span> <span style="color: #cc66cc;">10</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setq</span> client <span style="color: #66cc66;">&#40;</span>accept-connection socket<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>multiple-value-bind <span style="color: #66cc66;">&#40;</span>who port<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>remote-<span style="color: #b1b100;">name</span> socket<span style="color: #66cc66;">&#41;</span>
      <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;Client ~A:~D connected.~%&quot;</span> who port<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>close socket<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>几个操作一目了解，更细节的操作（比如如何创建UDP套接字）就去翻下源码好了，bind-address这个这个函数第二个参数用ensure-address把字符串转换成所需要的address类型，iolib也定义了一系列形如+ipv4-unspecified+的静态变量，类似于C语言里面的INADDR_ANY，最后一个参数reuse-addr相当于用setsockopt对套接字设置SO_REUSEADDR选项。</p>
<p>对于iolib的passive我一直有一个问题未能解决，当程序作为server在监听客户端连接时，在C语言中可以使用CTRL+C给程序发送SIGINT信号让程序终止，排除TIME_WAIT等这些情况，程序再次启动时仍可以bind到同一个指定端口（即使没有显示地调用close关闭套接字），但在slime中使用C-c C-c终止程序，并确保在slime-selecter中已经结束掉所有的用户线程之后，再次启动程序绑定同一个端口便会提示端口已被占用，除非结束掉lisp进程(sbcl/ccl等)，我也就直接选择重启emacs，这个问题一直未能解决，困扰我很长时间，所以我只能在程序运行中不断地插入close来在不该关闭的地方临时关闭套接字。</p>
<p><strong>3. 创建active socket</strong></p>
<p>通常使用active socket的程序是作为客户端的角色，下面是我写的一段简单的示例代码，用于发送一个http请求：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">let</span> <span style="color: #66cc66;">&#40;</span>socket ip http<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> socket <span style="color: #66cc66;">&#40;</span>make-socket
              <span style="color: #66cc66;">:</span><span style="color: #555;">connect</span> <span style="color: #66cc66;">:</span><span style="color: #555;">active</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">address-family</span> <span style="color: #66cc66;">:</span><span style="color: #555;">internet</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">type</span> <span style="color: #66cc66;">:</span><span style="color: #555;">stream</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">external-format</span> '<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">utf-</span><span style="color: #cc66cc;">8</span><span style="color: #66cc66;">&#41;</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">ipv6</span> <span style="color: #b1b100;">nil</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> ip <span style="color: #66cc66;">&#40;</span>lookup-hostname <span style="color: #ff0000;">&quot;basiccoder.com&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;IP of ~a is: ~a~%&quot;</span> host ip<span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span>connect socket ip <span style="color: #66cc66;">:</span><span style="color: #555;">port</span> <span style="color: #cc66cc;">80</span> <span style="color: #66cc66;">:</span><span style="color: #555;">wait</span> t<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;Connected to ~a via ~a:~a to ~a:~a~%&quot;</span>
          host <span style="color: #66cc66;">&#40;</span>local-host socket<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>local-port socket<span style="color: #66cc66;">&#41;</span>
          <span style="color: #66cc66;">&#40;</span>remote-host socket<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>remote-host socket<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> http <span style="color: #66cc66;">&#40;</span>make-http-request <span style="color: #ff0000;">&quot;GET&quot;</span> <span style="color: #ff0000;">&quot;/&quot;</span> host
               <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;Connection&quot;</span> <span style="color: #ff0000;">&quot;Closed&quot;</span><span style="color: #66cc66;">&#41;</span>
                <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;User-Agent&quot;</span> <span style="color: #ff0000;">&quot;Mozilla&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;send: ~A~%&quot;</span> http<span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span>format socket http<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>finish-output socket<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>使用lookup-hostname来解决IP地址，通过connect来向远程服务器进行连接，make-http-request是我写的一个生成http请求头的一个宏，返回http请求字符串;创建的socket对象其实是一个流对象，因此可以使用format向流中写入数据，写入的数据会保存在缓冲区中，当调用(finish-output socket)函数时开始执行数据的发送操作。iolib也提供了send-to函数，不详细讨论了。</p>
<p><strong>4. 从流中读取数据</strong><br />
由于socket对象是一个流对象，因此可以用任何从流中读出数据的方法来从socket中接收数据，如read-line,read-byte等等，但read-line存在一个问题，当使用read-line读取数据时，当数据中存在非ASCII字符中便会抛出异常，它在读取的过程中会对数据进行ASCII解码，而read-byte则不会存在这个问题，因为它读出的是二进制的字节，它不关心编码方式，但read-byte我感觉很多情况下是不太适用的，因为它一次只能读取一个字节，一般情况下多次执行这样的操作效率不会太高，当然，iolib也提供了receive-from函数，间接地调用了系统调用recvfrom()，是一种带缓冲区的接收方式，也比较符合C语言的编程习惯。</p>
<p>receive-from的使用方法如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>multiple-value-bind <span style="color: #66cc66;">&#40;</span>buf-vector rbytes<span style="color: #66cc66;">&#41;</span>
          <span style="color: #66cc66;">&#40;</span>receive-from socket <span style="color: #66cc66;">:</span><span style="color: #555;">buffer</span> buf-vector
                               <span style="color: #66cc66;">:</span><span style="color: #555;">start</span> <span style="color: #cc66cc;">0</span> <span style="color: #66cc66;">:</span><span style="color: #555;">end</span> <span style="color: #cc66cc;">4096</span>
                               <span style="color: #66cc66;">:</span><span style="color: #555;">size</span> <span style="color: #cc66cc;">4096</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>receive-from返回的是values,主返回值是包含接收到的数据的vector，另一个返回值是读取到的字节数，函数调用的参数里面:buffer不是必须的，当:buffer未指定时，则需要指定:size参数，这时receive-from会自动创建一个指定大小的vector并将数据填充后返回。receive-from返回的vector中保存的是字节码值，并不是字符串，可以使用octets-to-string函数将其转换成string，具体可以参考下我的这篇日志：<a href="http://basiccoder.com/add-gbk-support-to-babel.html">Common Lisp为Babel添加GBK支持</a>。</p>
<p><strong>5. IO多路复用</strong></p>
<p>iolib提供了multiplex机制，原理也是对epoll/poll/kqueue进行了封装，我在linux下测试默认是用的epoll，使用方法和libevent非常相似，首先要创建一个全局的event base:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> *http-event-base*
        <span style="color: #66cc66;">&#40;</span>make-instance 'iomux<span style="color: #66cc66;">:</span><span style="color: #555;">event-base</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&lt;</span>/lisp<span style="color: #66cc66;">&gt;</span>
&nbsp;
将要进行利用的socket对象添加到该event base中，使用set-io-handler函数：
<span style="color: #66cc66;">&lt;</span>pre lang<span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;lisp&quot;</span><span style="color: #66cc66;">&gt;</span>
<span style="color: #66cc66;">&#40;</span>set-io-handler *http-event-base*
                <span style="color: #66cc66;">&#40;</span>socket-os-fd socket<span style="color: #66cc66;">&#41;</span>
                <span style="color: #66cc66;">:</span><span style="color: #555;">read</span>
                <span style="color: #66cc66;">&#40;</span>make-http-event-loop conn client<span style="color: #66cc66;">&#41;</span>
                <span style="color: #66cc66;">:</span><span style="color: #555;">one-shot</span> t<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>第三个选项:read表示监听套接字是否有数据可读，同类的选项还有:write和:error，第四个参数是事件发生是要执行的回调函数，由于lisp中没有类似于C语言中的void*这种方式，不会像C语言一样给回调函数通过一个指针来传递相关的参数，但lisp的高阶函数使用传递额外参数更加方便了，上述代码中的make-http-event-loop函数的返回值是一个lambda函数，用来作为set-io-handler的回调函数，而 conn和clinet两个参数可以通过make-http-event-loop传递给lambda函数：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">defun</span> make-http-event-loop <span style="color: #66cc66;">&#40;</span>conn client<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">lambda</span> <span style="color: #66cc66;">&#40;</span>fd event exception<span style="color: #66cc66;">&#41;</span>
     <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;event ~A on fd(~D) with connection
:~A client :~A&quot;</span> event fd conn client<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>最后，调用event-dispatch函数来进入事件循环：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>event-dispatch *http-event-base*<span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">when</span> *http-event-base*
  <span style="color: #66cc66;">&#40;</span>close *http-event-base*<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p><strong>6. iolib的其它参考文档</strong></p>
<div>
[1] <a href="http://common-lisp.net/project/iolib/manual/">http://common-lisp.net/project/iolib/manual/</a><br />
[2] <a href="http://pages.cs.wisc.edu/~psilord/blog/data/iolib-tutorial/tutorial.html">http://pages.cs.wisc.edu/~psilord/blog/data/iolib-tutorial/tutorial.html</a></div>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/network-programing-in-common-lisp-using-iolib.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Common Lisp为Babel添加GBK支持</title>
		<link>http://basiccoder.com/add-gbk-support-to-babel.html</link>
		<comments>http://basiccoder.com/add-gbk-support-to-babel.html#comments</comments>
		<pubDate>Thu, 22 Dec 2011 05:14:51 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Lisp]]></category>
		<category><![CDATA[babel]]></category>
		<category><![CDATA[Common Lisp]]></category>
		<category><![CDATA[iolib]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1018</guid>
		<description><![CDATA[前段时间在学Common Lisp，接触新语言我干的第一件事一般是通过HTTP抓取某个web页面，因为对网络编程比较感兴趣，而且平时写的程序也多是网络相关的，所以比较关心这方面的用法，于是用IOLib写了一个简单的小程序尝试着抓取了几个大门户网站的页面代码，关于IOLib的基本用法改天我也写篇日志记录一下，也算是和大家分享一下，毕竟能找到的中文资料比较少，而且文档也不是特别全，就像<a href="http://pages.cs.wisc.edu/~psilord/blog/27.html">这篇文章里面说的</a>：”Such is the nature of open source documentation. “，于是大多数的用法都得通过hack源代码来弄明白，言归正传，在写这个小程序的时候我遇到了一些问题，关于字符编码的问题，下面慢慢道来吧。

IOLib的receive-from方法是通过调用recvfrom来进行的，这种带缓存的接收方式很符合其它语言进行编程的套路，但它所接收到的buffer数据是需要存储在一个vector '(unsigned-byte 8)中的，虽然字符串在本质上也是向量，但对于字符串的很多操作不能直接应用于vector，而且vector中的元素都是每个字符的unicode编码，而不是确定的字符，于是便需要进行转换，最初我使用的办法：
<pre lang="lisp">
(map 'string #'code-char buffer)
</pre... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>前段时间在学Common Lisp，接触新语言我干的第一件事一般是通过HTTP抓取某个web页面，因为对网络编程比较感兴趣，而且平时写的程序也多是网络相关的，所以比较关心这方面的用法，于是用IOLib写了一个简单的小程序尝试着抓取了几个大门户网站的页面代码，关于IOLib的基本用法改天我也写篇日志记录一下，也算是和大家分享一下，毕竟能找到的中文资料比较少，而且文档也不是特别全，就像<a href="http://pages.cs.wisc.edu/~psilord/blog/27.html">这篇文章里面说的</a>：”Such is the nature of open source documentation. “，于是大多数的用法都得通过hack源代码来弄明白，言归正传，在写这个小程序的时候我遇到了一些问题，关于字符编码的问题，下面慢慢道来吧。</p>
<p>IOLib的receive-from方法是通过调用recvfrom来进行的，这种带缓存的接收方式很符合其它语言进行编程的套路，但它所接收到的buffer数据是需要存储在一个vector &#8216;(unsigned-byte 8)中的，虽然字符串在本质上也是向量，但对于字符串的很多操作不能直接应用于vector，而且vector中的元素都是每个字符的unicode编码，而不是确定的字符，于是便需要进行转换，最初我使用的办法：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>map 'string #'code-char buffer<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>这个方法简单粗暴，直接在对一个vector上的每一个元素应用code-char函数，然后将输出映射为string，首先需要了解code-char/char-code这对函数的用法，大多数的Common Lisp实现都使用Unicode字符编码，当然，Unicode向下兼容ASCII和ISO-8859-1，所以这一对函数的作用就是在字符和它的Unicode码之间相互转换，如：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>char-code #\中<span style="color: #66cc66;">&#41;</span>
<span style="color: #cc66cc;">20013</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>code-char <span style="color: #cc66cc;">20013</span><span style="color: #66cc66;">&#41;</span>
#\U4E2D
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;~d&quot;</span> #x4E2D<span style="color: #66cc66;">&#41;</span>
<span style="color: #cc66cc;">20013</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;~a&quot;</span> #\U4E2D<span style="color: #66cc66;">&#41;</span>
中</pre></div></div>

<p>字符“中”的Unicode码是20013，用code-char将其转换成字符之后，REPL并不是将可视的字符直接显示出来，而是显示#\U4E2D，表示这是一个Unicode字符，后面的4E2D是该字符的16进程Unicode码，换成10进制也便是20013，将该这了符直接打印出的话便可以得到字符“中”了。</p>
<p>当抓取的页面编码不是Unicode（一般情况下都不是的），直接使用这种办法转换成字符串就会导致除ASCII字符以外的其它字符乱码。</p>
<p>Google搜索下在stackoverflow上发现这个问题的解决办法，<a href="http://common-lisp.net/project/babel/">babel</a>和<a href="http://weitz.de/flexi-streams/">flexi-streams</a>的octets-to-string函数可以实现由vector向string的转换，而vector必须是(make-array element-length :element-type &#8216;(unsigned-byte 8))类型，在使用该函数在抓取新浪/网易等门户网站的数据的时候会抛出异常：</p>
<p>代码：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>babel<span style="color: #66cc66;">:</span><span style="color: #555;">octets-to-string</span> buffer <span style="color: #66cc66;">:</span><span style="color: #555;">encoding</span> <span style="color: #66cc66;">:</span><span style="color: #555;">utf-</span><span style="color: #cc66cc;">8</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>异常：</p>
<pre>
Illegal :UTF-8 character starting at position 437.
 [Condition of type BABEL-ENCODINGS:INVALID-UTF8-CONTINUATION-BYTE]
</pre>
<p>起初我对这个问题百思不得其解，后来在水木还有StackOverflow上提出这个问题之后，我才发现原来我的问题是那么弱，也非常感谢大家的热心解答，确实如字面意思，有非法的UTF-8字符，也就是说字符串并非使用UTF-8编码的，而我习惯性地以为这些大网站理应都是UTF-8编码才对，谁知跟我想的正好想反，他们大多都不是UTF-8，有GBK甚至有GB2312，关于GBK和UTF-8我在推特上提起过这个问题，有推友说大网站仍在延用GB2312是为了节省流量，这个我可以接受，也有推友说使用GBK是因为它比UTF-8可能多几个字符，这个我也可以接受，但有的推友却跑过来说GB*是国标，用UTF-8什么的那是崇洋媚外，这个我表示无论如何也接受不了，互联网是没有国界的存在，在这样一个大的开放平台上你搞个国标有个毛用，当然我们的互联网也不开放是真的。</p>
<p>GB2312是GBK的子集，GBK向下兼容GB2312，使用GB2312就意味着能使用的字符数要远小于GBK，这两者相对于UTF-8对于中文的优点就是它们对于汉字的编码是两个字节的，而UTF-8是三个字节的，当然，我说的仅是指汉字，UTF-8是变长编码的，它也支持2字节和4字节甚至更多。</p>
<p>babel是不支持GBK的，sbcl的sb-ext:octets-to-string和Closure的ccl:octets-to-string都支持GBK，我个人比较较真，想给babel写个GBK的patch，以便可以实现平台无关的转码。</p>
<p>可能是我搜索技术不行，搜到的关于GBK的中文正式文档不多，只在维基百科上找到了关于<a href="http://en.wikipedia.org/wiki/GBK">GBK的介绍</a>，虽然没有涉及到具体的GBK到Unicode转码规则，但它提供的关于GBK的介绍也是很有帮助的，发现原来GBK与Unicode的转换并不像UTF-8与Unicode之间的转换那样有固定的规则，而是需要通过查表实现，于是乎我在网上下载到了一个包含所有GBK符号的码表，排列顺序也是按GBK的相关规则来的，解码的过程我参考了这篇文章：<a href="http://dourok.info/2010/06/%E4%BB%8Egbk%E5%88%B0unicode%E7%9A%84%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6%E6%98%A0%E5%B0%84/">从GBK到Unicode的中文字符映射</a>，编码的话就是反其道而行之，原理很简单，参考维基百科上的两个图就可以推算出来。</p>
<p>对于lisp而言，宏是这门语言一个很大的亮点，通过宏甚至可以定制语言的特性，babel写义了几个宏作为编解码的接口，最重要的有下面四个:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">define-octet-counter
define-code-point-counter
define-encoder
define-decoder</pre></div></div>

<p>前两个宏首先对要进行编/解码的数据进行预处理，分别计算出编/解码后的数据所占用的长度，从而预先分配存储空间。后两个宏分别定义对应的编码和解码算法，这两个宏具体的使用方法都是参考了enc-unicode.lisp这个文件里面关于UTF-8的编解码代码。</p>
<p>添加了GBK支持的babel我推送到github上去了(<a href="https://github.com/levin108/babel/">https://github.com/levin108/babel/</a>)，需要的同学可以下载，有什么写得不对的或者不好的欢迎提出来，我是新手需要学习。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/add-gbk-support-to-babel.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>使用ASDF构建Common Lisp程序包</title>
		<link>http://basiccoder.com/constructing-common-lisp-package-by-asdf.html</link>
		<comments>http://basiccoder.com/constructing-common-lisp-package-by-asdf.html#comments</comments>
		<pubDate>Tue, 13 Dec 2011 03:46:43 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Lisp]]></category>
		<category><![CDATA[asdf]]></category>
		<category><![CDATA[Common Lisp]]></category>
		<category><![CDATA[Emacs]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1010</guid>
		<description><![CDATA[在切入正题之前先写点不相关的，工作确定之后便开始忙论文的事，忙里偷闲总想搞点什么以做娱乐，不得不说，腾讯面试官说过的要精通两到三门不同的语言我印象很深刻，自己也想尝试一下新东西，VIM让我审美疲劳了，也想尝试一下Emacs，机缘巧合由田春老师翻译的<a href="http://www.amazon.cn/%E5%AE%9E%E7%94%A8Common-Lisp%E7%BC%96%E7%A8%8B-Peter-Seibel/dp/B005V9BBDK/ref=sr_1_1?ie=UTF8&#038;qid=1323743979&#038;sr=8-1">《实用Common Lisp编程》</a>刚上市不久，Emacs和Lisp也有不少渊源，再加上Lisp作为一门生命持久的元老级别的语言，至今仍然能倍受广大黑客的推崇，我相信它一定有学习的价值，而且Hadoop的MapReduce据说也是受Lisp的map和reduce函数的启发而来，相信对于Lisp的学习肯定不会是浪费时间，尽管将来工作中应用Lisp的机会可能很少，但深入学习的话肯定会对自己有一定的启发和帮助。
 
 于是几乎同一时间我开始尝试使用Emacs并在卓越上订购了中文版的《实用Common Lisp编程》，抽空阅读尝试。总起来说这本书是非常不错的，几乎是面面俱到，但有些我认为也很有用的宏如defstruct,deftype,check-type等书中没有给出相关介绍，另外关于cl的package书中有一章节专门讲了定义的规则，但对于package的管理及安装并没有提及，我个人觉得如果是practical编程的话提一下cl中重要的ASDF包管理工具还是很有必要的，既然书中没有提到就得自己通过其它的渠道去了解学习，这方面中文的资料相对较少，大多数的资料都是在外文网站上查到的，当然也包括到stackoverflow上的提问... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>在切入正题之前先写点不相关的，工作确定之后便开始忙论文的事，忙里偷闲总想搞点什么以做娱乐，不得不说，腾讯面试官说过的要精通两到三门不同的语言我印象很深刻，自己也想尝试一下新东西，VIM让我审美疲劳了，也想尝试一下Emacs，机缘巧合由田春老师翻译的<a href="http://www.amazon.cn/%E5%AE%9E%E7%94%A8Common-Lisp%E7%BC%96%E7%A8%8B-Peter-Seibel/dp/B005V9BBDK/ref=sr_1_1?ie=UTF8&#038;qid=1323743979&#038;sr=8-1">《实用Common Lisp编程》</a>刚上市不久，Emacs和Lisp也有不少渊源，再加上Lisp作为一门生命持久的元老级别的语言，至今仍然能倍受广大黑客的推崇，我相信它一定有学习的价值，而且Hadoop的MapReduce据说也是受Lisp的map和reduce函数的启发而来，相信对于Lisp的学习肯定不会是浪费时间，尽管将来工作中应用Lisp的机会可能很少，但深入学习的话肯定会对自己有一定的启发和帮助。</p>
<p> 于是几乎同一时间我开始尝试使用Emacs并在卓越上订购了中文版的《实用Common Lisp编程》，抽空阅读尝试。总起来说这本书是非常不错的，几乎是面面俱到，但有些我认为也很有用的宏如defstruct,deftype,check-type等书中没有给出相关介绍，另外关于cl的package书中有一章节专门讲了定义的规则，但对于package的管理及安装并没有提及，我个人觉得如果是practical编程的话提一下cl中重要的ASDF包管理工具还是很有必要的，既然书中没有提到就得自己通过其它的渠道去了解学习，这方面中文的资料相对较少，大多数的资料都是在外文网站上查到的，当然也包括到stackoverflow上的提问。</p>
<p>ASDF全称是Another System Definition Facility，asdf这个组合很有意思，正好和左手的键盘基本按键重合，当初还以为是作者很有个性地起了这个随意的名字呢。我个人的理解是ASDF是类似于automake或者cmake的工具，提供一种程序包的管理方法和工具，因此也没什么复杂的地方，只不过有一些基本的规则需要遵守，了解了就没什么了。</p>
<p>程序包中一般会有一个.asd文件，该文件定义了程序包中源码文件的组织方式及依赖关系，类似于cmake中的CMakelists.txt，当然该文的编码方式是使用lisp风格的。</p>
<p><strong>1. asdf工具的安装配置。</strong><br />
目前存在的common lisp实现有很多，在这篇文章中有介绍：<a href="http://common-lisp.net/~dlw/LispSurvey.html">Common Lisp Implementations: A Survey</a>，免费的common lisp实现中性能比较好使用也比较简单的应该属sbcl了，《practical common lisp》也推荐了sbcl，作为一个新手我第一选择当然也选了sbcl，在sbcl中已经集成了asdf工具，无需再手动安装，但安装方法也很简单，可以参考<a href="http://common-lisp.net/project/asdf/asdf.html#Loading-ASDF">ASDF Manual</a>，该手册对asdf有详细的介绍。</p>
<p><strong>2. asdf包的编译加载。</strong><br />
asdf是一个工具集，可以对包进行各种操作，其中包括编译，加载等。asdf对于包的编译和加载等操作都需要基于.asd文件，也就是说编译某个包asdf需要先找到该包对应的.asd文件，该文件一般是存放在源码根目录中的，asdf对于该文件的寻址有两种方式，new style和old style，下面分别简单介绍下这两种方式：</p>
<p>old style是为了兼容旧版本的程序的，目前已经不推荐新程序使用，其方法是将程序包的.asd文件所存在的路径添加到asdf的*central-registry*变量中，可使用如下语句完成该操作：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>push <span style="color: #ff0000;">&quot;/home/levin/lisp/spider/&quot;</span> asdf<span style="color: #66cc66;">:</span>*central-registry*<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>上述操作便可将/home/levin/lisp/spider/（注意结尾的&#8217;/'）这个路径添加到*central-registry*变量中，这样asdf便可对该包进行寻址，但在REPL中执行的该操作只对当前的会话有效的，REPL重启后需要重新添加路径到该变量中，未免有些繁琐，可以将该语句添加至common lisp实现的启动文件中，如sbcl即为.sbclrc这个文件，这样在REPL启动之后该路径便会自动添加到*central-registry*变量中。</p>
<p>new style是ASDF2所提倡使用的方法，其方法是在~/.config/中创建common-lisp目录用于存放相关的配置文件，和该主题相关的配置文件需要创建一个名为source-registry.conf.d的目录，在该目录下可以创建文件名任意的文件，将下面的语句添加至该文件中即可，文件如：</p>
<p>~/.config/common-lisp/source-registry.conf.d/01-spider-source.conf</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">directory</span> <span style="color: #ff0000;">&quot;/home/lisp/lisp/spider/&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>如上表示在/home/lisp/lisp/spider/这个目录中查找.asd文件，也可以将:directory替换成:tree，使asdf在目录中递归寻找.asd文件：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">tree</span> <span style="color: #ff0000;">&quot;/home/lisp/lisp/spider/&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>完成对asdf包的寻址操作之后，便可以使用asdf对程序包进行编译加载，编译和加载包分别可使用：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">compile-system</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">load-system</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>也可以使用asdf的operate对包进行编译和加载：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">oos</span> 'asdf<span style="color: #66cc66;">:</span><span style="color: #555;">compile-op</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">oos</span> 'asdf<span style="color: #66cc66;">:</span><span style="color: #555;">load-op</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>对于一个未经编译完成的包在编译完成会自动加载，同样，未编译完成的包在加载时也会自动先进行编译。</p>
<p><strong>3. asdf系统的构建：</strong></p>
<p>之前提到的.asd文件是asdf的很重要的文件，该文件最简单的形式如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>defpackage <span style="color: #66cc66;">:</span><span style="color: #555;">spider-system</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">use</span> <span style="color: #66cc66;">:</span><span style="color: #555;">cl</span> <span style="color: #66cc66;">:</span><span style="color: #555;">asdf</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#40;</span>in-package <span style="color: #66cc66;">:</span><span style="color: #555;">spider-system</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>defsystem spider
  <span style="color: #66cc66;">:</span><span style="color: #b1b100;">name</span> <span style="color: #ff0000;">&quot;spider&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">author</span> <span style="color: #ff0000;">&quot;levin li&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">version</span> <span style="color: #ff0000;">&quot;0.0.1&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">license</span> <span style="color: #ff0000;">&quot;MIT&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">description</span> <span style="color: #ff0000;">&quot;A spider program.&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">depends-on</span> <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">iolib</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">components</span> <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">file</span> <span style="color: #ff0000;">&quot;package&quot;</span><span style="color: #66cc66;">&#41;</span>
               <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">file</span> <span style="color: #ff0000;">&quot;spider&quot;</span> <span style="color: #66cc66;">:</span><span style="color: #555;">depends-on</span> <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;package&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>最开头的包定义是为了防止system名与其它包冲突，定义的包中包含一些基本信息，:depends-on声明了该程序将要依赖的包的，:components定义了包中的组件，即源代码，上述的源码包中只有两个文件，package.lisp和spider.lisp，package.lisp是定义一个cl package，可以理解为其它语言中的namespace，package.lisp定义了一个package，之后的程序如spider.lisp可能都会包含在该包中，因此:depends-on(“package”)这句话就非常重要，它会在编译spider.lisp之前先加载已经编译的好的package.lisp，若未声明spider依赖package，则在一次编译完成后REPL重启并对spider.lisp进行修改，而package.lisp未加改动，则asdf只会编译修改过的spider.lisp，这时候系统会提示spider包未找到，因为asdf发现package.lisp的目标文件是最新的，默认不会对其进行编译和加载，这也是我刚开始遇到的问题之一，在认真读了ASDF Manual后才解决了这个问题，关于该文件的一些详细的定义可以参考ASDF Manual。</p>
<p>package.lisp的内容如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>in-package <span style="color: #66cc66;">:</span><span style="color: #555;">cl-user</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>defpackage <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">use</span> <span style="color: #66cc66;">:</span><span style="color: #555;">cl</span> <span style="color: #66cc66;">:</span><span style="color: #555;">iolib</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">export</span> <span style="color: #66cc66;">:</span><span style="color: #555;">send-request</span>
           <span style="color: #66cc66;">:</span><span style="color: #555;">test</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>其中定义了包的名称，包的依赖的其它包，及要导出的符号等，关于package的更多介绍还是参考《practical common lisp》等资料。而spider.lisp是代码文件，开头需要有一行:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>in-package <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>表明当前程序包在spider包中定义，继而可以在spider.lisp中编写其它的逻辑代码，定义函数，宏或变量，所有的这些定义都被包含在包spider中，其中只有在package.lisp中声明为:export的符号才可以被其它包所引用，如上定义，该包中只有send-request和test两个函数可以被其它外部包引用。</p>
<p><strong>4. asdf包的安装</strong></p>
<p>绝大多数的common lisp包都是使用asdf组织的，可以使用asdf-install工具安装软件包，asdf-install使用之前需要先加载：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">oos</span> 'asdf<span style="color: #66cc66;">:</span><span style="color: #555;">load-op</span> <span style="color: #66cc66;">:</span><span style="color: #555;">asdf-install</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>之后便可以使用asdf-install安装软件包，一般而言有三种方式：</p>
<p>1. 通过包的名字进行安装：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf-install<span style="color: #66cc66;">:</span><span style="color: #555;">install</span> <span style="color: #ff0000;">&quot;iolib&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>这时asdf-install会自动从cliki.net上下载可用的包并安装，<a href="http://www.cliki.net/asdf-install">http://www.cliki.net/asdf-install</a>这个页面列出了在线可用的软件包的列表。</p>
<p>2. 通过包的url进行安装：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf-install<span style="color: #66cc66;">:</span><span style="color: #555;">install</span> <span style="color: #ff0000;">&quot;http://weitz.de/files/cl-ppcre.tar.gz&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>3. 通过包的本地路径进行安装：<br />
CL-USER> (asdf-install:install “/home/levin/lisp/iolib.tar.gz”)</p>
<p>更加详细的安装方法可以参考<a href="http://common-lisp.net/project/asdf-install/tutorial/">asdf-install turorial</a>.</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/constructing-common-lisp-package-by-asdf.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>毕业求职经历</title>
		<link>http://basiccoder.com/graduate-job-hunting.html</link>
		<comments>http://basiccoder.com/graduate-job-hunting.html#comments</comments>
		<pubDate>Wed, 02 Nov 2011 15:48:55 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[My Life]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=941</guid>
		<description><![CDATA[前天去淘宝把三方交了，找工作就到此为止了，找工作的经历虽然不算复杂漫长，但也觉得很累很辛苦。

正式开始投简历找工作应该是从九月份开始的，九月中旬注册了几家招聘网站，陆陆续续地开始往外投简历，目标也比较明确，虽然专业一直是通信，但却想去互联网公司做后端开发，服务器或者分布式系统相关的开发，所以通信类的公司没有怎么投简历，只投了华为的云计算方向和爱立信的软件研发方向。国企没有投，片面地觉得国企是最没有战斗力的公司，还是想到互联网行业的私企做些有挑战性的工作。

第一家面试的公司是IBM研究院，在上地的钻石大厦，离北邮超级远，先坐车到清华西门，然后转车到西北旺，下车后发现右手边是农田，左手边是树林，没有路人，没有出租车，于是完全迷路了，后来沿着树林走了一段时间后偶然发现树林对面有座大楼冒出个头来，于是穿过树林过去问了下那楼的保安，说那就是钻石大厦，OMG，让我好找。IBM CRL没有笔试，师兄推荐的部门，因为是第一次面试没什么经验，也没有准备英文自我介绍，还有些紧张，做了一个简单的slide，没有被问到复杂的算法问题，因为时间比较短，所以挑了一两个重点的项目讲了讲，结束后感觉表现真的很烂，也可能是师兄推荐的原因最后让我拿到了二面的机会，二面安排在十一前，仍然是讲项目，在师兄的指点下改了slide，而且也提前准备了英文自我介绍，这中间也参加过几次面试，也有了点经验，所以感觉还不错，结束后几天也拿到了互联网公司的offer，IBM CRL感觉不太适合我，那边偏向于研究一些，像我这种做工程的在那边确实不是太好发展，于是联系师兄把情况说清楚了，已经浪费了大家两次面试的时间了，如果有第三次面试机会我还是不去了，这样IBM的面试也就到此为止了... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>前天去淘宝把三方交了，找工作就到此为止了，找工作的经历虽然不算复杂漫长，但也觉得很累很辛苦。</p>
<p>正式开始投简历找工作应该是从九月份开始的，九月中旬注册了几家招聘网站，陆陆续续地开始往外投简历，目标也比较明确，虽然专业一直是通信，但却想去互联网公司做后端开发，服务器或者分布式系统相关的开发，所以通信类的公司没有怎么投简历，只投了华为的云计算方向和爱立信的软件研发方向。国企没有投，片面地觉得国企是最没有战斗力的公司，还是想到互联网行业的私企做些有挑战性的工作。</p>
<p>第一家面试的公司是IBM研究院，在上地的钻石大厦，离北邮超级远，先坐车到清华西门，然后转车到西北旺，下车后发现右手边是农田，左手边是树林，没有路人，没有出租车，于是完全迷路了，后来沿着树林走了一段时间后偶然发现树林对面有座大楼冒出个头来，于是穿过树林过去问了下那楼的保安，说那就是钻石大厦，OMG，让我好找。IBM CRL没有笔试，师兄推荐的部门，因为是第一次面试没什么经验，也没有准备英文自我介绍，还有些紧张，做了一个简单的slide，没有被问到复杂的算法问题，因为时间比较短，所以挑了一两个重点的项目讲了讲，结束后感觉表现真的很烂，也可能是师兄推荐的原因最后让我拿到了二面的机会，二面安排在十一前，仍然是讲项目，在师兄的指点下改了slide，而且也提前准备了英文自我介绍，这中间也参加过几次面试，也有了点经验，所以感觉还不错，结束后几天也拿到了互联网公司的offer，IBM CRL感觉不太适合我，那边偏向于研究一些，像我这种做工程的在那边确实不是太好发展，于是联系师兄把情况说清楚了，已经浪费了大家两次面试的时间了，如果有第三次面试机会我还是不去了，这样IBM的面试也就到此为止了。</p>
<p>第二家面试的单位应该是华为了，之前参加过一个华为的机试，出奇的简单，就只有一道题补全一个函数，功能是求数组里面小于平均数的数的个数，两分钟就写完了然后在机房里面刷推想等下同学，于是就被赶出去了。过了不久通知面试，在知春路某酒店，记得通知的是12点，十一点半赶过去，等了一会儿面试官让先去吃饭，吃完饭回来继续等，直到等到下午将近一点半实在有点不耐烦了，去找hr简单表达了一下不满，于是过了十分钟大约一点半左右被安排了面试。虽然面试的是云计算，但我相信给我面试的那个姐姐应该对云计算也没有什么深入的了解，而且对我的开源项目表现出不屑，只对简历上写的实验室的863项目感点兴趣，说是因为是国家项目，其实学校里面做的国家项目大家都懂的，当时自己心情也确实没那么有耐心了，毕竟已经在那里浪费了两个小时的时间了，一个多小时后结束，第二天居然收到了二面通知，没有去参加二面，华为就这样结束了。</p>
<p>考虑到华为的这种情况，通信公司我已经完全不想考虑了，爱立信的机试我也没有参加。</p>
<p>网易有道经过实验室师兄内推搞到了一个提前参加笔试的机会，清华科技园创业大厦，笔试是在晚上，题量不大但做得很烂，但很幸运地收到了面试通知，一面的时候和面试官比较投缘，问的问题我也大都了解，tcp/ip,socket,epoll等等的，都比较熟，算法问题也都答的差不多，很顺利进了二面，二面表现比较差，问了几个系统设计的题，微博的推送机制，搜索引擎的搜索提示，这些我之前还真没去了解过，想了想把就重点说了下cache，结合着kv系统简单设计了下，面试官也没说好坏，开始问的两个算法题记不清是什么了，但印象中是没有什么好的思路的，那一次面试感觉非常差，但因为没有收到拒信也纠结了好久，终于在十一过后的某一天收到了三面的通知，有点意外，三面的大哥没有问算法，问我的软件的架构，分布式缓存系统设计等等，感觉答的还不错，感觉至少要比二面好，等了好久，却收到了有道的拒信，有道成了第一家拒掉我的公司，找工作需要实力也需要机缘，有道的面试我应该是实力和机缘都不够吧。</p>
<p>淘宝的笔试在华为二面的那天晚上，没有参加华为下午的二面也是怕他们太拖拉耽误了淘宝的笔试，笔试现场很混乱，因为没有记名，霸面的同学把屋子挤得满满的，记不清当时是怎么协调的了，出着汗答到时间结束，最后一个算法大题居然没来得及想，随便写了个简单算法，后来面试的时候瞅了一眼笔试的试卷，那个题只得了3分，后面的附加题到是抽时间做了，线程进程，锁，网络编程都是比较熟悉的东西。在北理参加360笔试的时候收到了淘宝的面试通知，东三环嘉泰金融大厦，离北邮也确实有点远，淘宝当天面试的人很多，但还算比较准时的，一面问的基础都比较熟悉，每次一被问到STL我就怂了，这东西只会简单的用用，不过跟面试官表达过长时间使用C之后，他问的关于STL的都是怎么用的方面，这些我还都是搞得定的，后来让写了个递归的小程序，也很简单，结束后让我去休息去等一会儿，过了一会儿又把我叫到一间小会议室里面，我问这是要干嘛，说是面试啊，原来是连着面的，于是自我介绍讲项目，二面的面试官感觉很犀利，问C10K，prefork/worker-master在C10K问题上的差别，我之前理解的也不太对，面试官也都给我解释清楚了，后来问了很多，关于读的书，平时的爱好等等，还出了两个智力题，只想出来了一个，本来有不少问题要问的，关于淘宝的开源产品等等的问题，后来HR在外面示意时间应该是超了，我就没仔细问，于是又去休息区等着了。中午淘宝请吃了个午饭，下午一点多三面，见到了大牛行癫，被问到了Memcached和Redis的哈希表的处理方式的问题，异步的expand，但Memcached其实我表达错了，Memcached是异步来rehash的，而Redis因为是单线程，它的rehash是嵌在各种操作(lookup,insert)中进行的，都不是一下全部完成的，因为大数据量时rehash会相当耗时，当然，我理解不对的地方行癫也都给我解释清楚了，最后还把二面关于淘宝基础产品的问题都问了，之后就是回来等消息了，几天后收到offer，没有传说中的几十w年薪，选择淘宝是因为在淘宝能做想做的方向，<a href="http://twitter.com/colyli">@colyli</a>帮忙推荐到淘宝的虚拟化团队做分布式存储，这个方向我很感兴趣，所以拿到淘宝offer之后找工作就基本接近尾声了。</p>
<p>360的面试是在淘宝后的几天，北大博雅大酒店，打车杀了过去，360也是三面连着的，前两面是技术面，最后一面是hr面，一面的技术面问了很多基础知识，甚至还问了vim的操作，这些都比较熟悉，另外也问了C10K的问题，其它的都是在聊天了，面试官人很好，给我讲hadoop，分布式系统等等，二面也差不多的流程，感觉面试官主要是想看我能不能把心思都放到工作上，可能是因为简历上我在工作之外做的自己的项目比较多，当然，这个肯定不是问题，三面过后也同样拿到了360的offer，和淘宝几乎是同一时间，感觉非常对不起360，没有决定去却接受了人家的offer，这是非常损rp的，所以在把三方交给淘宝之后赶紧写邮件给360的hr拒掉了这个offer。</p>
<p>腾讯的笔试应该是在清华，比较晚了，因为当时拿到了淘宝的offer，其实已经不太想再折腾了，因为感觉选择越多最后纠结就越多，淘宝的工作我已经很满意了，但后来觉得腾讯是中国互联网老大，不去试试也不甘心，笔试还不错，题基本上都会，一面是在知春路的京仪大酒店，两点钟面试官非常准时，问的东西都很常细，线程池，锁，网络编程的细节，算法，都是些很细节的问题，那次面试我才知道有pthread_cond_timewait()这个函数，惭愧，二面安排在腾讯北京分公司，苏州街银科大厦，面试过程比较随意，面试人很好，感觉就是聊天，问了一些关于项目的问题，最后问了一个智力题，当时感冒还发着烧，脑子实在是转不动，于是就没想出来，于是就这样结束了，三面是hr面，hr叔叔人也很好，问了一下工作中遇到的问题，offer的期待等等，二十多分钟，最后送我走的时候还给我介绍了腾讯北京这边的情况，给我介绍了腾讯演播室，带我看了很多名星的签名，照片，走的时候还看到了超级正典腾讯18层的前台MM，突然觉得去腾讯工作也不错哈。hr说要11月1号到10号之间才会发offer，30号我就需要把三方交给淘宝，跟hr说过这个情况，说自己没有太多选择，希望能尽快得到腾讯的消息好做决定，但30号也实在太仓促只有两天的时间，于是最后只能选择了淘宝，今天是2号仍然没有收到腾讯的消息，有可能是挂了，即便是有幸拿到offer现在也去不了了。</p>
<p>参加过的笔试还有微软，EMC这两家笔试完都没消息了，微软笔试答得很烂，可以理解，EMC笔试感觉还不错的，结果也一样挂了，当时都没怎么重视笔试，以为笔试一般不会挂掉，但事实上我大多数都是挂在了笔试。</p>
<p>之前也参加过谷歌的笔试，安排在了清华的技科楼，当时顶着巨大的堵车压力勉强按时赶到清华，但诺大一个清华居然没有人知道技科楼怎么走，问了好久都没有找到路，最后好不容易找到个地图才发现了那位置，找到考场的时候已经开始考了将近20分钟了，最近我还很嚣张地提前交了试卷，其实感觉答得还算可以，不过肯定是过不了的，后来果然收到了谷歌的拒信，不过能有机会参加一次谷歌的笔试也很满足啦，嗯。</p>
<p>参加过笔试面试的差不多就这几家，没有广撒网式的投简历，之前也投过新浪搜狐亚马逊等互联网公司的简历，最后都放弃了笔试，当然，绝不是看不上这些公司，只是觉得有个差不多的工作就可以了，选择越多纠结越多，而且最后拿到别人的offer最后又不去给人家公司也会带来麻烦，影响人家招聘。</p>
<p>找工作的经历基本上就这些了，抓紧时间把毕业论文搞出来，也希望能有时间可以去淘宝实习几天，提前了解下要做的东西。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/graduate-job-hunting.html/feed</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>Redis的事件循环与定时器模型</title>
		<link>http://basiccoder.com/redis%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%be%aa%e7%8e%af%e4%b8%8e%e5%ae%9a%e6%97%b6%e5%99%a8%e6%a8%a1%e5%9e%8b.html</link>
		<comments>http://basiccoder.com/redis%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%be%aa%e7%8e%af%e4%b8%8e%e5%ae%9a%e6%97%b6%e5%99%a8%e6%a8%a1%e5%9e%8b.html#comments</comments>
		<pubDate>Fri, 07 Oct 2011 11:33:25 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[NoSql]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[Redis]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=930</guid>
		<description><![CDATA[假期的最后一天，简单翻阅了下<a href="http://redis.io">Redis</a>的源码，读一款server软件的源码我一般是从进程/线程模型开始的，Redis让我有些诧异，它采用了单进程单线程的模型，一般的server软件都会采用多进程或者多线程再或者多线程多进程混合的模型来设计，从而充分利用多核处理器的并行计算能力来提高软件的性能，Redis这种模型我只能推断程序的可并行化程度不高，顺序计算反而能省去多线程同步和维护线程池/进程池的开销，我对于数据库server端的设计没有什么经验也没有太多的理解，如有谬误欢迎大家指正。

当然，这里要写的不是关于Redis的进程模型，而是Redis的事件模型和定时器模型。

Redis没有依赖<a href="http://en.wikipedia.org/wiki/Libevent">libevent</a>，而是自己通过IO多路复用的方式来实现了事件循环和定时器，不像<a href="http://en.wikipedia.org/wiki/Nginx">nginx</a>或者<a href="http://en.wikipedia.org/wiki/Apache">apache</a>有多种多路复用方式可供选择，Redis只采用了三种：epoll/kqueue/select，默认采用<a href="http://en.wikipedia.org/wiki/Epoll">epoll</a>，在linux环境下最优的方式当然是epoll，当在FreeBSD平台下epoll不存在时则使用<a href="http://en.wikipedia.org/wiki/Kqueue">kqueue</a>，当然若两种方式都未定义则使用性能最差的<a href="http://en.wikipedia.org/wiki/Select_%28Unix%29">select</a>，我只阅读了跟epoll相关的代码... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>假期的最后一天，简单翻阅了下<a href="http://redis.io">Redis</a>的源码，读一款server软件的源码我一般是从进程/线程模型开始的，Redis让我有些诧异，它采用了单进程单线程的模型，一般的server软件都会采用多进程或者多线程再或者多线程多进程混合的模型来设计，从而充分利用多核处理器的并行计算能力来提高软件的性能，Redis这种模型我只能推断程序的可并行化程度不高，顺序计算反而能省去多线程同步和维护线程池/进程池的开销，我对于数据库server端的设计没有什么经验也没有太多的理解，如有谬误欢迎大家指正。</p>
<p>当然，这里要写的不是关于Redis的进程模型，而是Redis的事件模型和定时器模型。</p>
<p>Redis没有依赖<a href="http://en.wikipedia.org/wiki/Libevent">libevent</a>，而是自己通过IO多路复用的方式来实现了事件循环和定时器，不像<a href="http://en.wikipedia.org/wiki/Nginx">nginx</a>或者<a href="http://en.wikipedia.org/wiki/Apache">apache</a>有多种多路复用方式可供选择，Redis只采用了三种：epoll/kqueue/select，默认采用<a href="http://en.wikipedia.org/wiki/Epoll">epoll</a>，在linux环境下最优的方式当然是epoll，当在FreeBSD平台下epoll不存在时则使用<a href="http://en.wikipedia.org/wiki/Kqueue">kqueue</a>，当然若两种方式都未定义则使用性能最差的<a href="http://en.wikipedia.org/wiki/Select_%28Unix%29">select</a>，我只阅读了跟epoll相关的代码。</p>
<p>在<strong>main()</strong>函数的最后调用了<strong>aeMain()</strong>这个函数进入Redis的事件循环，这个函数的很简单，循环调用<strong>aeProcessEvents()</strong>来对事件进行处理:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> aeMain<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    eventLoop<span style="color: #339933;">-&gt;</span>stop <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>eventLoop<span style="color: #339933;">-&gt;</span>stop<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">-&gt;</span>beforesleep <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span>
            eventLoop<span style="color: #339933;">-&gt;</span>beforesleep<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        aeProcessEvents<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span> AE_ALL_EVENTS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>在此之前Redis做了很多初始化的工作，这些工作大多是在<strong>initServer()</strong>这个函数中执行的，初始化一些相关的list,dict等，调用<strong>aeCreateEventLoop()</strong>初始化<a href="http://en.wikipedia.org/wiki/Event_loop">eventloop</a>，这个函数初始化eventloop相关的数据结构，并最终调用了<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_create.2.html">epoll_create()</a>函数，对epoll上下文进行初始化。紧接着Redis创建了用于listen的socket对象，并调用<strong>aeCreateFileEvent()</strong>把该socket描述符的读事件加入到事件池中去，另外，还调用了<strong>aeCreateTimeEvent()</strong>函数来初始化一下定时器，定期地执行<strong>serverCron()</strong>这个函数，接下来看一下<strong>aeCreateFileEvent()</strong>和<strong>aeCreateTimeEvent()</strong>这两个函数。</p>
<p><strong>aeCreateFileEvent()</strong>这个函数初始化<strong>aeFileEvent</strong>结构(该结构保存事件的一些状态，以及事件的文件描述符等)，并调用<strong>aeApiAddEvent()</strong>函数将描述符相关的事件添加到事件池中，对于epoll它的实现如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> aeApiAddEvent<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> fd<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> mask<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    aeApiState <span style="color: #339933;">*</span>state <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>apidata<span style="color: #339933;">;</span>
    <span style="color: #993333;">struct</span> epoll_event ee<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. */</span>
    <span style="color: #993333;">int</span> op <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>events<span style="color: #009900;">&#91;</span>fd<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">mask</span> <span style="color: #339933;">==</span> AE_NONE <span style="color: #339933;">?</span>
            EPOLL_CTL_ADD <span style="color: #339933;">:</span> EPOLL_CTL_MOD<span style="color: #339933;">;</span>
&nbsp;
    ee.<span style="color: #202020;">events</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    mask <span style="color: #339933;">|=</span> eventLoop<span style="color: #339933;">-&gt;</span>events<span style="color: #009900;">&#91;</span>fd<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">mask</span><span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* Merge old events */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&amp;</span> AE_READABLE<span style="color: #009900;">&#41;</span> ee.<span style="color: #202020;">events</span> <span style="color: #339933;">|=</span> EPOLLIN<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&amp;</span> AE_WRITABLE<span style="color: #009900;">&#41;</span> ee.<span style="color: #202020;">events</span> <span style="color: #339933;">|=</span> EPOLLOUT<span style="color: #339933;">;</span>
    ee.<span style="color: #202020;">data</span>.<span style="color: #202020;">u64</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* avoid valgrind warning */</span>
    ee.<span style="color: #202020;">data</span>.<span style="color: #202020;">fd</span> <span style="color: #339933;">=</span> fd<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>epoll_ctl<span style="color: #009900;">&#40;</span>state<span style="color: #339933;">-&gt;</span>epfd<span style="color: #339933;">,</span>op<span style="color: #339933;">,</span>fd<span style="color: #339933;">,&amp;</span>ee<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>非常简洁，这个函数只不过是把<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_ctl.2.html">epoll_ctl()</a>相关的操作做了一下封装，至此描述符已经加入到事件池中进行监听了，接着看<strong>aeCreateTimeEvent()</strong>这个函数。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">long</span> <span style="color: #993333;">long</span> aeCreateTimeEvent<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #339933;">,</span> <span style="color: #993333;">long</span> <span style="color: #993333;">long</span> milliseconds<span style="color: #339933;">,</span>
        aeTimeProc <span style="color: #339933;">*</span>proc<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>clientData<span style="color: #339933;">,</span>
        aeEventFinalizerProc <span style="color: #339933;">*</span>finalizerProc<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">long</span> <span style="color: #993333;">long</span> id <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>timeEventNextId<span style="color: #339933;">++;</span>
    aeTimeEvent <span style="color: #339933;">*</span>te<span style="color: #339933;">;</span>
&nbsp;
    te <span style="color: #339933;">=</span> zmalloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>te<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>te <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> AE_ERR<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>id <span style="color: #339933;">=</span> id<span style="color: #339933;">;</span>
    aeAddMillisecondsToNow<span style="color: #009900;">&#40;</span>milliseconds<span style="color: #339933;">,&amp;</span>te<span style="color: #339933;">-&gt;</span>when_sec<span style="color: #339933;">,&amp;</span>te<span style="color: #339933;">-&gt;</span>when_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>timeProc <span style="color: #339933;">=</span> proc<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>finalizerProc <span style="color: #339933;">=</span> finalizerProc<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>clientData <span style="color: #339933;">=</span> clientData<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>next <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>timeEventHead<span style="color: #339933;">;</span>
    eventLoop<span style="color: #339933;">-&gt;</span>timeEventHead <span style="color: #339933;">=</span> te<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> id<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>同样是初始化数据结构，但没有调用<strong>aeApiAddEvent()</strong>这个函数，当然，定时器又不需要文件描述符，当然不需要添加相关事件，定时器的实现只是使用了<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>的定时功能，<strong>aeAddMillisecondsToNow()</strong>这个函数顾名思义是把当前时间加上一个给定的毫秒数，然后算出一个when_sec和when_ms，eventloop对象的<strong>timeEventHead</strong>实际上是一个单向链表，它用于保存所有的定时器事件，当添加一个定时器事件时其实只是向该链表头中插入了一个元素，其会后由<strong>aeProcessEvents()</strong>这个函数遍历该链表取出超时的事件进行处理，接着我们看下这个事件处理里面最核心的函数。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> aeProcessEvents<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> flags<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span> processed <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> numevents<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Nothing to do? return ASAP */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_FILE_EVENTS<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Note that we want call select() even if there are no
     * file events to process as long as we want to process time
     * events, in order to sleep until the next time event is ready
     * to fire. */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">-&gt;</span>maxfd <span style="color: #339933;">!=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span> <span style="color: #339933;">||</span>
        <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_DONT_WAIT<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">int</span> j<span style="color: #339933;">;</span>
        aeTimeEvent <span style="color: #339933;">*</span>shortest <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
        <span style="color: #993333;">struct</span> timeval tv<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>tvp<span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_DONT_WAIT<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
            shortest <span style="color: #339933;">=</span> aeSearchNearestTimer<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>shortest<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #993333;">long</span> now_sec<span style="color: #339933;">,</span> now_ms<span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;">/* Calculate the time missing for the nearest
             * timer to fire. */</span>
            aeGetTime<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>now_sec<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>now_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            tvp <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>tv<span style="color: #339933;">;</span>
            tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">=</span> shortest<span style="color: #339933;">-&gt;</span>when_sec <span style="color: #339933;">-</span> now_sec<span style="color: #339933;">;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>shortest<span style="color: #339933;">-&gt;</span>when_ms <span style="color: #339933;">&lt;</span> now_ms<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>shortest<span style="color: #339933;">-&gt;</span>when_ms<span style="color: #339933;">+</span><span style="color: #0000dd;">1000</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> now_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span><span style="color: #0000dd;">1000</span><span style="color: #339933;">;</span>
                tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">--;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>shortest<span style="color: #339933;">-&gt;</span>when_ms <span style="color: #339933;">-</span> now_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span><span style="color: #0000dd;">1000</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #808080; font-style: italic;">/* If we have to check for events but need to return
             * ASAP because of AE_DONT_WAIT we need to se the timeout
             * to zero */</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_DONT_WAIT<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                tv.<span style="color: #202020;">tv_sec</span> <span style="color: #339933;">=</span> tv.<span style="color: #202020;">tv_usec</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
                tvp <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>tv<span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #808080; font-style: italic;">/* Otherwise we can block */</span>
                tvp <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* wait forever */</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        numevents <span style="color: #339933;">=</span> aeApiPoll<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span> tvp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>j <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> j <span style="color: #339933;">&lt;</span> numevents<span style="color: #339933;">;</span> j<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            aeFileEvent <span style="color: #339933;">*</span>fe <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>eventLoop<span style="color: #339933;">-&gt;</span>events<span style="color: #009900;">&#91;</span>eventLoop<span style="color: #339933;">-&gt;</span>fired<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">fd</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> mask <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>fired<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">mask</span><span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> fd <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>fired<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">fd</span><span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> rfired <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
	    <span style="color: #808080; font-style: italic;">/* note the fe-&gt;mask &amp; mask &amp; ... code: maybe an already processed
             * event removed an element that fired and we still didn't
             * processed, so we check if the event is still valid. */</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fe<span style="color: #339933;">-&gt;</span>mask <span style="color: #339933;">&amp;</span> mask <span style="color: #339933;">&amp;</span> AE_READABLE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                rfired <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
                fe<span style="color: #339933;">-&gt;</span>rfileProc<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span>fd<span style="color: #339933;">,</span>fe<span style="color: #339933;">-&gt;</span>clientData<span style="color: #339933;">,</span>mask<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fe<span style="color: #339933;">-&gt;</span>mask <span style="color: #339933;">&amp;</span> mask <span style="color: #339933;">&amp;</span> AE_WRITABLE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rfired <span style="color: #339933;">||</span> fe<span style="color: #339933;">-&gt;</span>wfileProc <span style="color: #339933;">!=</span> fe<span style="color: #339933;">-&gt;</span>rfileProc<span style="color: #009900;">&#41;</span>
                    fe<span style="color: #339933;">-&gt;</span>wfileProc<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span>fd<span style="color: #339933;">,</span>fe<span style="color: #339933;">-&gt;</span>clientData<span style="color: #339933;">,</span>mask<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            processed<span style="color: #339933;">++;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #808080; font-style: italic;">/* Check time events */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS<span style="color: #009900;">&#41;</span>
        processed <span style="color: #339933;">+=</span> processTimeEvents<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> processed<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* return the number of processed file/time events */</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这其中的<strong>aeApiPoll()</strong>这个函数其实就是对<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>操作的一个封装，<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>的最后一个参数是一个毫秒级的超时时间，Redis充分利用了这个时间在对IO事件进行监听的同时来实现了定时。这个函数的前面一大部分代码是在计算这个超时时间的，它调用<strong>aeSearchNearestTimer()</strong>这个函数来获取最近要超时的一个定时器对象，如何获取的呢？就是遍历刚才的提到的那个<strong>timeEventHead</strong>链表，来找出时间值最小的一个，注意是遍历，因为链表中的定时器也是无序的，不过我相信作者有一天会把它换成红黑树或者其它的数据结构吧。如果找到一个将要超时的定时器，则将它与当前时间进行比较，如果当前时间大于定时器时间则表示定时器已超时，将超时时间设为0，若当前时间小于定时器时间，则将超时时间设为两者之差。如果定时器队列为空，或者说没有任何定时器事件，则可以根据<strong>AE_DONT_WAIT</strong>这个标志来决定<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>是non-blocking立即返回，还是一直阻塞在那里。</p>
<p><strong>aeApiPoll()</strong>函数返回时，有两种情况，一种是IO事件被触发，另一种是定时器超时，当IO事件被触发时，遍历所有活跃描述符并调用相关的回调函数对其进行处理。当没有IO事件被触发，而是超时时，则返回值numevents为0，函数会转向<strong>processTimeEvents()</strong>来遍历定时器列表，调用定时器回调函数处理定时器事件，当IO事件被触发而并没有定时器超时时，如果设置了<strong>AE_TIME_EVENTS</strong>标志则也会对定时器列表进行遍历，主循环便是如此，我认为这会多少对效率有一定的影响，当然可能现在的Redis定时器列表并不太大，所以效率问题也可以忽略。</p>
<p>以上是简单地对今天的工作做的总结，欢迎大家批评指教。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/redis%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%be%aa%e7%8e%af%e4%b8%8e%e5%ae%9a%e6%97%b6%e5%99%a8%e6%a8%a1%e5%9e%8b.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Memcached内存管理机制浅析</title>
		<link>http://basiccoder.com/memcached-memory-mamagement.html</link>
		<comments>http://basiccoder.com/memcached-memory-mamagement.html#comments</comments>
		<pubDate>Thu, 08 Sep 2011 12:41:12 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[NoSql]]></category>
		<category><![CDATA[Memcached]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=923</guid>
		<description><![CDATA[<a href="http://memcached.org/">Memcached</a>的内存管理在网上也可以搜集到不少不错的文章，新浪的这篇《<a href="http://blog.developers.api.sina.com.cn/?p=124">Memcached深度分析</a>》讲得不错，读别人的文章还是不如自己直接去读源码分析源码来得直接，这里写一下我阅读Memcached源码时对于Memcached内存管理机制的理解。

Memcached的代码结构很简单，从<strong>main()</strong>函数入口进去之后便是几个模块的初始化函数，和内存管理相关的主要有两个函数，一个是<strong>assoc_init()</strong>，这个是用来初始化哈希表的，关于这个哈希表的作用留在外面讨论，另一个是<strong>slabs_init()</strong>，该函数用来初始化slab，下面先来讨论一下slab机制。

<div><strong>1. 内存slab的管理</strong></div>

<div><strong>1.1 slabs的初始化</strong></div>
Memcached把内存分为一个个的slab，每个slab又分成一个个的chunk，系统会定义一个slab_class数组，其中每个元素是都是一个对该slab的描述，包括这个slab里面的每个chunk的大小，这个slab里面包含多少个chunk等信息，先把slab分布情况打印出来看看，对Memcached的内存分配有个大体的认识，然后再去读代码可能会好一些。... ]]></description>
			<content:encoded><![CDATA[<p><a href="http://memcached.org/">Memcached</a>的内存管理在网上也可以搜集到不少不错的文章，新浪的这篇《<a href="http://blog.developers.api.sina.com.cn/?p=124">Memcached深度分析</a>》讲得不错，读别人的文章还是不如自己直接去读源码分析源码来得直接，这里写一下我阅读Memcached源码时对于Memcached内存管理机制的理解。</p>
<p>Memcached的代码结构很简单，从<strong>main()</strong>函数入口进去之后便是几个模块的初始化函数，和内存管理相关的主要有两个函数，一个是<strong>assoc_init()</strong>，这个是用来初始化哈希表的，关于这个哈希表的作用留在外面讨论，另一个是<strong>slabs_init()</strong>，该函数用来初始化slab，下面先来讨论一下slab机制。</p>
<div><strong>1. 内存slab的管理</strong></div>
<div><strong>1.1 slabs的初始化</strong></div>
<p>Memcached把内存分为一个个的slab，每个slab又分成一个个的chunk，系统会定义一个slab_class数组，其中每个元素是都是一个对该slab的描述，包括这个slab里面的每个chunk的大小，这个slab里面包含多少个chunk等信息，先把slab分布情况打印出来看看，对Memcached的内存分配有个大体的认识，然后再去读代码可能会好一些。</p>
<div style="line-height:14px">
$ memcached -vv<br />
slab class   1: chunk size        80 perslab   13107<br />
slab class   2: chunk size       104 perslab   10082<br />
slab class   3: chunk size       136 perslab    7710<br />
slab class   4: chunk size       176 perslab    5957<br />
slab class   5: chunk size       224 perslab    4681<br />
slab class   6: chunk size       280 perslab    3744<br />
slab class   7: chunk size       352 perslab    2978<br />
slab class   8: chunk size       440 perslab    2383<br />
slab class   9: chunk size       552 perslab    1899<br />
slab class  10: chunk size       696 perslab    1506<br />
slab class  11: chunk size       872 perslab    1202<br />
slab class  12: chunk size      1096 perslab     956<br />
slab class  13: chunk size      1376 perslab     762<br />
slab class  14: chunk size      1720 perslab     609<br />
slab class  15: chunk size      2152 perslab     487<br />
slab class  16: chunk size      2696 perslab     388<br />
slab class  17: chunk size      3376 perslab     310<br />
slab class  18: chunk size      4224 perslab     248<br />
slab class  19: chunk size      5280 perslab     198<br />
slab class  20: chunk size      6600 perslab     158<br />
slab class  21: chunk size      8256 perslab     127<br />
slab class  22: chunk size     10320 perslab     101<br />
slab class  23: chunk size     12904 perslab      81<br />
slab class  24: chunk size     16136 perslab      64<br />
slab class  25: chunk size     20176 perslab      51<br />
slab class  26: chunk size     25224 perslab      41<br />
slab class  27: chunk size     31536 perslab      33<br />
slab class  28: chunk size     39424 perslab      26<br />
slab class  29: chunk size     49280 perslab      21<br />
slab class  30: chunk size     61600 perslab      17<br />
slab class  31: chunk size     77000 perslab      13<br />
slab class  32: chunk size     96256 perslab      10<br />
slab class  33: chunk size    120320 perslab       8<br />
slab class  34: chunk size    150400 perslab       6<br />
slab class  35: chunk size    188000 perslab       5<br />
slab class  36: chunk size    235000 perslab       4<br />
slab class  37: chunk size    293752 perslab       3<br />
slab class  38: chunk size    367192 perslab       2<br />
slab class  39: chunk size    458992 perslab       2<br />
slab class  40: chunk size    573744 perslab       1<br />
slab class  41: chunk size    717184 perslab       1<br />
slab class  42: chunk size   1048576 perslab       1
</div>
<p>这是Memcached的默认配置，chunk size是按照<strong>CHUNK_ALIGN_BYTES</strong>对齐的，chunk size相比于前一个slab中的chunk size有一个上升因子factor，1.4.7里面factor的默认值是1.25，我们可以看到按默认配置slab总共分成了42类。<br />
先给出一个我用Dia画的Memcached的内存分配图，<a href="http://projects.gnome.org/dia/">Dia</a>不如Visio好用，凑合着画了一个，如果有理解不对的地方欢迎大家指出。<br />
<a href="http://basiccoder.com/wp-content/uploads/2011/09/memcached_memory_layout.png"><img src="http://basiccoder.com/wp-content/uploads/2011/09/memcached_memory_layout.png" alt="" title="memcached_memory_layout" width="516" height="417" class="aligncenter size-full wp-image-925" /></a></p>
<p>接下来看一下slabs_init()的代码，还是只保留关键代码，节省版面。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> slabs_init<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> size_t limit<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">double</span> factor<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> bool prealloc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span> i <span style="color: #339933;">=</span> POWER_SMALLEST <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> size <span style="color: #339933;">=</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>item<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> settings.<span style="color: #202020;">chunk_size</span><span style="color: #339933;">;</span>
&nbsp;
    mem_limit <span style="color: #339933;">=</span> limit<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>prealloc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* Allocate everything in a big chunk with malloc */</span>
        mem_base <span style="color: #339933;">=</span> malloc<span style="color: #009900;">&#40;</span>mem_limit<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mem_base <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            mem_current <span style="color: #339933;">=</span> mem_base<span style="color: #339933;">;</span>
            mem_avail <span style="color: #339933;">=</span> mem_limit<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> 
    <span style="color: #009900;">&#125;</span>
    memset<span style="color: #009900;">&#40;</span>slabclass<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>slabclass<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">++</span>i <span style="color: #339933;">&lt;</span> POWER_LARGEST <span style="color: #339933;">&amp;&amp;</span> size <span style="color: #339933;">&lt;=</span> settings.<span style="color: #202020;">item_size_max</span> <span style="color: #339933;">/</span> factor<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* Make sure items are always n-byte aligned */</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>size <span style="color: #339933;">%</span> CHUNK_ALIGN_BYTES<span style="color: #009900;">&#41;</span>
            size <span style="color: #339933;">+=</span> CHUNK_ALIGN_BYTES <span style="color: #339933;">-</span> <span style="color: #009900;">&#40;</span>size <span style="color: #339933;">%</span> CHUNK_ALIGN_BYTES<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        slabclass<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">size</span> <span style="color: #339933;">=</span> size<span style="color: #339933;">;</span>
        slabclass<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">perslab</span> <span style="color: #339933;">=</span> settings.<span style="color: #202020;">item_size_max</span> <span style="color: #339933;">/</span> slabclass<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">size</span><span style="color: #339933;">;</span>
        size <span style="color: #339933;">*=</span> factor<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    power_largest <span style="color: #339933;">=</span> i<span style="color: #339933;">;</span>
    slabclass<span style="color: #009900;">&#91;</span>power_largest<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">size</span> <span style="color: #339933;">=</span> settings.<span style="color: #202020;">item_size_max</span><span style="color: #339933;">;</span>
    slabclass<span style="color: #009900;">&#91;</span>power_largest<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">perslab</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#ifndef DONT_PREALLOC_SLABS</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>pre_alloc <span style="color: #339933;">=</span> getenv<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;T_MEMD_SLABS_ALLOC&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pre_alloc <span style="color: #339933;">==</span> NULL <span style="color: #339933;">||</span> atoi<span style="color: #009900;">&#40;</span>pre_alloc<span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;prealloc memory.<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            slabs_preallocate<span style="color: #009900;">&#40;</span>power_largest<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #339933;">#endif</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>prealloc这个参数其实是跟后面的<strong>DONT_PREALLOC_SLABS</strong>这个宏是相关的，表示是否要在初始化的时候为slabs分配内存，如果需要预先为slabs分配内存，则先跟系统申请mem_limit字节的内存，之后的slab都是从这块内存上分配的，这块内存大小默认是64M，说起来当时犯了个很低级的错误，当时看到这里的时候发现mem_limit的默认值是1024 * 1024 * 64，于是断点在这里，发现<strong>malloc()</strong>没有返回NULL，当时想我一个2G的机器申请64G的内存到底是怎么分配成功的，纠结了好久才发现不是64G，是64M，所以读代码/写代码的时候还真得保持头脑清醒才行。。。</p>
<p>然后再说prealloc，如果没有定义<strong>DONT_PREALLOC_SLABS</strong>这个宏的话，初始化的时候会先申请64M的内存，接着调用preallocate这个函数，看下这个函数的注释，然后我要吐槽一下我的英语，前两天被它的那句注释搞晕了。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#ifndef DONT_PREALLOC_SLABS</span>
<span style="color: #808080; font-style: italic;">/* Preallocate as many slab pages as possible (called from slabs_init)
on start-up, so users don't get confused out-of-memory errors when
they do have free (in-slab) space, but no space to make new slabs.
if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all
slab types can be made. if max memory is less than 18 MB, only the
smaller ones will be made. */</span>
<span style="color: #993333;">static</span> <span style="color: #993333;">void</span> slabs_preallocate <span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> maxslabs<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span></pre></div></div>

<p>这句话的大体意思应该是：在启动的时候尽可能多地分配slabs页，所以用户不要因为内存足够却获得OOM而感到郁闷。。。</p>
<p>我当时的理解时：在启动的时候尽可能多地分配slabs页，这样用户就不会因为内存足够却被提示OOM而感到郁闷了。。。</p>
<p>我仔细地查阅源代码，发现如果prealloc，那么64M的内存用光之后并不会再去malloc新内存，跟我当时理解的这句注释的意思正好相反，仔细研究代码发现代码没有什么问题之后我反过来看了一眼这句注释，我觉得是我把这句英文理解错了吧。。。</p>
<p>OK，也就是说如果开启了prealloc功能的话，那么很有可能在有空闲内存的情况下分配内存失败，另外提前为slabs分配内存也有可能会造成内存的浪费，有可能所有的item都不会使用某个slab class，这样这个slab class里面分配的内存就浪费掉了，<strong>DONT_PREALLOC_SLABS</strong>在1.4.7里面是默认定义的，也就是说prealloc功能是默认关闭的，于是就不考虑先prealloc了。</p>
<p><strong>slabs_init()</strong>接下来的代码就很简单了，对每个slab的chunk size进行对齐然后设置该slab class的相关成员变量的值。</p>
<div><strong>1.2 slabclass_t的结构介绍</strong></div>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> size<span style="color: #339933;">;</span>      <span style="color: #808080; font-style: italic;">/* sizes of items */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> perslab<span style="color: #339933;">;</span>   <span style="color: #808080; font-style: italic;">/* how many items per slab */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>slots<span style="color: #339933;">;</span>           <span style="color: #808080; font-style: italic;">/* list of item ptrs */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> sl_total<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* size of previous array */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> sl_curr<span style="color: #339933;">;</span>   <span style="color: #808080; font-style: italic;">/* first free slot */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>end_page_ptr<span style="color: #339933;">;</span>         <span style="color: #808080; font-style: italic;">/* pointer to next free item at end of page, or 0 */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> end_page_free<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* number of items remaining at end of last alloced page */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> slabs<span style="color: #339933;">;</span>     <span style="color: #808080; font-style: italic;">/* how many slabs were allocated for this class */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>slab_list<span style="color: #339933;">;</span>       <span style="color: #808080; font-style: italic;">/* array of slab pointers */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> list_size<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* size of prev array */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> killing<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* index+1 of dying slab, or zero if none */</span>
    size_t requested<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* The number of requested bytes */</span>
<span style="color: #009900;">&#125;</span> slabclass_t<span style="color: #339933;">;</span>
<span style="color: #993333;">static</span> slabclass_t slabclass<span style="color: #009900;">&#91;</span>MAX_NUMBER_OF_SLAB_CLASSES<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span></pre></div></div>

<p>size和perslab这两个字段已经说过了，slots这里存放的是空闲的slab列表，当调用<strong>do_slabs_free()</strong>这个函数之后，要释放的chunk就被放到这个数组的尾部，sl_curr数组尾部开始的第一个空闲的chunk，sl_total表示数组的总大小，当sl_curr大小等于sl_total的时候数组会通过<strong>realloc()</strong>进行扩容，容易是旧容量的2倍。</p>
<p>end_page_ptr这个字段表示该slab里面的当前空闲的chunk地址，end_page_free，表示该slab中剩余的空闲chunk的数目，其它的几个字段按注释都很容易理解了。</p>
<div><strong>1.3 创建新的slab</strong></div>
<p>前面提到的<strong>slabs_preallocate()</strong>函数只不过是对每一个已初始化的slab_class调用<strong>do_slabs_newslab()</strong>函数为其分配一块slab内存空间，看下这个函数的代码。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> do_slabs_newslab<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> id<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    slabclass_t <span style="color: #339933;">*</span>p <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>slabclass<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> len <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>size <span style="color: #339933;">*</span> p<span style="color: #339933;">-&gt;</span>perslab<span style="color: #339933;">;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>ptr<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>mem_limit <span style="color: #339933;">&amp;&amp;</span> mem_malloced <span style="color: #339933;">+</span> len <span style="color: #339933;">&gt;</span> mem_limit <span style="color: #339933;">&amp;&amp;</span> p<span style="color: #339933;">-&gt;</span>slabs <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
        <span style="color: #009900;">&#40;</span>grow_slab_list<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
        <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>ptr <span style="color: #339933;">=</span> memory_allocate<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>size_t<span style="color: #009900;">&#41;</span>len<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    memset<span style="color: #009900;">&#40;</span>ptr<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span>size_t<span style="color: #009900;">&#41;</span>len<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">=</span> ptr<span style="color: #339933;">;</span>
    p<span style="color: #339933;">-&gt;</span>end_page_free <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>perslab<span style="color: #339933;">;</span>
&nbsp;
    p<span style="color: #339933;">-&gt;</span>slab_list<span style="color: #009900;">&#91;</span>p<span style="color: #339933;">-&gt;</span>slabs<span style="color: #339933;">++</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> ptr<span style="color: #339933;">;</span>
    mem_malloced <span style="color: #339933;">+=</span> len<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>slab_class的slab_list字段保存的是已分配的slabs列表，该列表实际上是个数组，当数组中没有空闲位置时则会调用<strong>grop_slab_list()</strong>对数组进行扩容，接下来便会调用<strong>memory_allocate()</strong>给slab分配内存，这个函数会检测是否已经开始了prealloc功能，如果开启了便会在预分配的内存块上申请一块内存，当这块预分配的内存用完时并不会对其进行扩容，于是便返回分配内存失败，这也就造成了系统明明有剩余内存，Memcached却提示SERVER_ERROR out of memory，当然，如果没有开启prealloc功能，这个函数便会直接调用<strong>malloc()</strong>分配内存，接下来对各个指针进行初始化。刚分配的空闲slab，它的end_page_str指针是指向slab内存首部的，end_page_free字段代表slab内存中包含中的chunk数。</p>
<div><strong>1.4 在slab上分配内存</strong></div>
<p>在某个slab_class上分配size大小的内存的函数是<strong>do_slabs_alloc()</strong>，这个函数有两个参数，要分配的内存字节数size，和该内存应该存在于哪个slab class上的slab class id. 这两个参数在是有相关性的，在调用该函数的时候class id一般是通过size来计算得出来的，先看一下这个函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>do_slabs_alloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> size_t size<span style="color: #339933;">,</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> id<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    slabclass_t <span style="color: #339933;">*</span>p<span style="color: #339933;">;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>ret <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>id <span style="color: #339933;">&lt;</span> POWER_SMALLEST <span style="color: #339933;">||</span> id <span style="color: #339933;">&gt;</span> power_largest<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    p <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>slabclass<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* fail unless we have space at the end of a recently allocated page,
       we have something on our freelist, or we could allocate a new page */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> <span style="color: #009900;">&#40;</span>p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span> p<span style="color: #339933;">-&gt;</span>sl_curr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span>
           do_slabs_newslab<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* We don't have more memory available */</span>
        ret <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>p<span style="color: #339933;">-&gt;</span>sl_curr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* return off our freelist */</span>
        ret <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>slots<span style="color: #009900;">&#91;</span><span style="color: #339933;">--</span>p<span style="color: #339933;">-&gt;</span>sl_curr<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* if we recently allocated a whole page, return from that */</span>
        ret <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>end_page_ptr<span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">--</span>p<span style="color: #339933;">-&gt;</span>end_page_free <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>caddr_t<span style="color: #009900;">&#41;</span>p<span style="color: #339933;">-&gt;</span>end_page_ptr<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> p<span style="color: #339933;">-&gt;</span>size<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ret<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        p<span style="color: #339933;">-&gt;</span>requested <span style="color: #339933;">+=</span> size<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> ret<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>1.如果end_page_ptr等于0，并且sl_curr等于0，则表示slab中已经没有空闲内存，并且回收的chunk free list里面也没有可用内存了，于是这时候需要调用<strong>do_slabs_newslab()</strong>创建新的slab。</p>
<p>2.如果sl_curr不等于0，则表示chunk free list中还有可用的内存，直接返回一个可用的chunk即可。</p>
<p>3.如果chunk free list里面没有可用内存，而slab中还有空闲内存，则直接从slab中申请一个chunk的内存，然后将end_page_ptr后移。</p>
<div><strong>2. 对象item的管理</strong></div>
<div><strong>2.1 item对象的分配</strong></div>
<p>存入系统的每个key-value对都会被转换成一个item，这个item中保存了相关的状态标志信息，当服务器收到一个set请求时便需要在内存中创建一个item，item的内存理所当然是在上面讨论过的slab分配器上分配的。item的存储使用了LRU的方法，把item链入一个链表中，其中全局变量<em>heads[LARGEST_ID]</em>和<em>tails[LARGEST_ID]</em>这两个数组保存各个slab class所对应的item链表的表头和表尾，item创建的函数<strong>do_item_alloc()</strong>太长，就不把代码贴出来了，描述一下它的过程。</p>
<p>在这里先提一下前面提到的哈希表，哈希表是用来把item通过key散列到哈希表上的，这样就可以通过key来快速地定位item，在<strong>do_item_unlink()</strong>这个函数中，首先要把该item从哈希表中删除，然后再从list中移除，最后检测该item的refcount，如果refcount是0，则调用<strong>item_free()</strong>释放内存，<strong>item_free()</strong>再调用底层的<strong>slab_free()</strong>去释放内存，<strong>slab_free()</strong>只是<strong>do_slab_free()</strong>的线程安全版本，它在内部先加锁随后调用<strong>do_slab_free()</strong>，再之后解锁。</p>
<p>OK，接着看item的分配过程，首先会从链表的尾开始往前找，如果某节点的item设置了过期时间并且该item已过期，则回收该item，调用<strong>do_item_unlink()</strong>把它从链表中取出来，刚才说过<strong>do_item_unlink()</strong>这个函数在refcount为0的时候会释放掉这个item，所以为了防止这个item内存被释放，先将它的refcount设置为1，若向前查找50次都没有找到符合要求的item，则循环断开。</p>
<p>如果没有找到可以回收的item，然后就调用<strong>slabs_alloc()</strong>分配内存，如果内存也分配失败，就尝试着从链表尾开始向前找出一些没有人用的item(refcount=0)，把它<strong>do_item_unlink()</strong>掉，这时候因为refcount=0，所以它相关的内存也会被释放还给slab分配器，这个尝试又从尾向前尝试50次，OK，slab分配器中可能又有可用内存了，再用<strong>slabs_alloc()</strong>分配内存，如果还失败。。。好吧，这次只能从链表中删除一些正在引用但过期时间小于current_time &#8211; CURRENT_REPAIR_TIME的节点，这个尝试又从尾向前尝试50次，OK，再做最后一次尝试再去<strong>slabs_alloc()</strong>分配内存，如果这次还是失败，那就彻底放弃了，内存分配失败。。。</p>
<div><strong>3. Memcached的哈希表</strong></div>
<div><strong>3.1 Memcached用到的哈希算法</strong></div>
<p>Memcached用到的哈希算法比较复杂，算法地址在<a href="http://burtleburtle.net/bob/hash/doobs.html">http://burtleburtle.net/bob/hash/doobs.html</a>，Memcached维护了两个哈希表，primary_hashtable和old_hashtable，primary_hashtable是当前正在使用的哈希表，当表没有进行扩张时从这张表中插入或者查找，old_hashtable用于哈希表扩张的时候使用，它指向旧的哈希表，当哈希表中的item数大于表的大小的3/2时，则哈希表进行扩张，此时插入和查找等操作都是在old_hashtable中进行的。</p>
<div><strong>3.2 数据项插入哈希表</strong></div>
<p>数据项插入哈希表时用了<strong>assoc_insert()</strong>这个函数，下面看下它的代码</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> assoc_insert<span style="color: #009900;">&#40;</span>item <span style="color: #339933;">*</span>it<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">uint32_t</span> hv<span style="color: #339933;">;</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> oldbucket<span style="color: #339933;">;</span>
&nbsp;
    assert<span style="color: #009900;">&#40;</span>assoc_find<span style="color: #009900;">&#40;</span>ITEM_key<span style="color: #009900;">&#40;</span>it<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> it<span style="color: #339933;">-&gt;</span>nkey<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* shouldn't have duplicately named things defined */</span>
&nbsp;
    hv <span style="color: #339933;">=</span> hash<span style="color: #009900;">&#40;</span>ITEM_key<span style="color: #009900;">&#40;</span>it<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> it<span style="color: #339933;">-&gt;</span>nkey<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>expanding <span style="color: #339933;">&amp;&amp;</span>
        <span style="color: #009900;">&#40;</span>oldbucket <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>hv <span style="color: #339933;">&amp;</span> hashmask<span style="color: #009900;">&#40;</span>hashpower <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;=</span> expand_bucket<span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        it<span style="color: #339933;">-&gt;</span>h_next <span style="color: #339933;">=</span> old_hashtable<span style="color: #009900;">&#91;</span>oldbucket<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        old_hashtable<span style="color: #009900;">&#91;</span>oldbucket<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> it<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        it<span style="color: #339933;">-&gt;</span>h_next <span style="color: #339933;">=</span> primary_hashtable<span style="color: #009900;">&#91;</span>hv <span style="color: #339933;">&amp;</span> hashmask<span style="color: #009900;">&#40;</span>hashpower<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        primary_hashtable<span style="color: #009900;">&#91;</span>hv <span style="color: #339933;">&amp;</span> hashmask<span style="color: #009900;">&#40;</span>hashpower<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> it<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    hash_items<span style="color: #339933;">++;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> expanding <span style="color: #339933;">&amp;&amp;</span> hash_items <span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>hashsize<span style="color: #009900;">&#40;</span>hashpower<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> <span style="color: #0000dd;">3</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #0000dd;">2</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        assoc_expand<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    MEMCACHED_ASSOC_INSERT<span style="color: #009900;">&#40;</span>ITEM_key<span style="color: #009900;">&#40;</span>it<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> it<span style="color: #339933;">-&gt;</span>nkey<span style="color: #339933;">,</span> hash_items<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>如果expanding是true，哈希表正在扩张，则把item插入到old_hashtable中，否则则插入到primary_hashtable中，然后检测item数是否大于hashsize * 3 / 2，如果是，则进行扩张，哈希表的查找删除等操作也大致类似，不拿出来说了。</p>
<div><strong>3.3 哈希表的扩张</strong></div>
<p>哈希表的扩张其实是异步进行的，Memcached在初始化时在<strong>main()</strong>函数中会调用<strong>start_assoc_maintenance_thread()</strong>函数来开启一个线程对哈希表进行定期维护，线程函数通过对条件变量的wait进行睡眠，当被激活时发现expanding为true，则对哈希表进行扩张，把旧表的元素复制到新表中，然后释放旧表的内存空间，搞定后再睡去。。。触发哈希表扩张事件的函数是<strong>assoc_expand()</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> assoc_expand<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    old_hashtable <span style="color: #339933;">=</span> primary_hashtable<span style="color: #339933;">;</span>
&nbsp;
    primary_hashtable <span style="color: #339933;">=</span> calloc<span style="color: #009900;">&#40;</span>hashsize<span style="color: #009900;">&#40;</span>hashpower <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>primary_hashtable<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>settings.<span style="color: #202020;">verbose</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span>
            fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Hash table expansion starting<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        hashpower<span style="color: #339933;">++;</span>
        expanding <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">true</span><span style="color: #339933;">;</span>
        expand_bucket <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        pthread_cond_signal<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>maintenance_cond<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        primary_hashtable <span style="color: #339933;">=</span> old_hashtable<span style="color: #339933;">;</span>
        <span style="color: #808080; font-style: italic;">/* Bad news, but we can keep running. */</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这个函数让把old_hashtable指向primary_hashtable，之后给primary_hashtable重新分配内存空间，然后把expanding标志设为true，接着激活maintenace_cond信号，maintenace线程被唤醒开始异步地把old_hashtable中的元素拷贝到primary_hashtable中来。</p>
<p>OK，这是我对Memcached内存管理机制的一个简单的探索和了解，如有谬误的地方，欢迎大家批评指正。</p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/memcached-memory-mamagement.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>VIM复制粘贴的那些事</title>
		<link>http://basiccoder.com/vim-copy-paste-related.html</link>
		<comments>http://basiccoder.com/vim-copy-paste-related.html#comments</comments>
		<pubDate>Wed, 07 Sep 2011 06:06:11 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[slackware]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=910</guid>
		<description><![CDATA[说起vim的复制粘贴一直是个比较困扰我的问题，之前一直用gvim，跟系统剪贴板之间的复制粘贴都没有问题，gvim毕竟还需要再开个窗口，麻烦，而且不如vim那样快捷，但vim里面最让我头痛的是复制粘贴问题，想把vim里面的内容复制到其它的地方貌似怎么也不行，粘贴进来的话免强可以，但格式可能会很乱，折腾来折腾去，今天先是发现了个往外复制比较蹩脚的办法:
<pre lang="vim">:set mouse=v</pre>
这样鼠标就可以变成文本选择指针的样子，可以选择选择字体，然后点右键选复制，或者CTRL+SHIFT+C，但如果<strong>mouse=a</strong>这种模式下的话选择之后，右键的复制是灰色的，当然这种情况复制如果有行号的话行号也会被复制进去，所以在复制前先把行号关了，复制完再打开，这办法貌似也能凑合，但着实太不专业，而且太麻烦，远不如用gvim的<strong>"+y</strong>这种来得方便，<strong>"+y</strong>这个很多推友表示是可以的，但在我这里不可以，刚经<a href="http://twitter.com/multiple1902">@multiple1902</a>指点，有可能是vim编译的问题。

查看了一下vim版本相关信息：
<pre lang="bash">vim --version</pre>
发现<strong>clipboard</strong>和<strong>xterm_clipboard</strong>这些选项都是不可使用的功能，这样看来果然是编译问题了，slackware讲求KISS，编译的时候没有加入该选项也可以理解，于是从vim官网svn上check下来源码(话说我为啥check下来的是7.2呢，难道7.3还没有stable...)，重新编译... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>说起vim的复制粘贴一直是个比较困扰我的问题，之前一直用gvim，跟系统剪贴板之间的复制粘贴都没有问题，gvim毕竟还需要再开个窗口，麻烦，而且不如vim那样快捷，但vim里面最让我头痛的是复制粘贴问题，想把vim里面的内容复制到其它的地方貌似怎么也不行，粘贴进来的话免强可以，但格式可能会很乱，折腾来折腾去，今天先是发现了个往外复制比较蹩脚的办法:</p>

<div class="wp_syntax"><div class="code"><pre class="vim" style="font-family:monospace;"><span style="color: #000000;">:</span><span style="color: #804040;">set</span> <span style="color: #668080;">mouse</span>=v</pre></div></div>

<p>这样鼠标就可以变成文本选择指针的样子，可以选择选择字体，然后点右键选复制，或者CTRL+SHIFT+C，但如果<strong>mouse=a</strong>这种模式下的话选择之后，右键的复制是灰色的，当然这种情况复制如果有行号的话行号也会被复制进去，所以在复制前先把行号关了，复制完再打开，这办法貌似也能凑合，但着实太不专业，而且太麻烦，远不如用gvim的<strong>“+y</strong>这种来得方便，<strong>“+y</strong>这个很多推友表示是可以的，但在我这里不可以，刚经<a href="http://twitter.com/multiple1902">@multiple1902</a>指点，有可能是vim编译的问题。</p>
<p>查看了一下vim版本相关信息：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">vim</span> <span style="color: #660033;">--version</span></pre></div></div>

<p>发现<strong>clipboard</strong>和<strong>xterm_clipboard</strong>这些选项都是不可使用的功能，这样看来果然是编译问题了，slackware讲求KISS，编译的时候没有加入该选项也可以理解，于是从vim官网svn上check下来源码(话说我为啥check下来的是7.2呢，难道7.3还没有stable&#8230;)，重新编译。</p>
<p>编译的时候必须要选上的选项一个是<strong>&#8211;enable-multibyte</strong>，如果没加这个选项的话，中文应该就会乱码了。<br />
另外关于剪贴板相关的选项，具体我也不清楚是哪一个，看了下几个相关的选项觉得最有可能是的是<strong>&#8211;enable-xim</strong>，只加了这两个选项之后编译完测试发现vim已经可以和系统剪贴板之间共享数据了，<strong>“+y</strong>可以使用了，世界一下子就清爽了很多，于是重新加完整选项编译：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">.<span style="color: #339933;">/</span>configure <span style="color: #339933;">--</span>prefix<span style="color: #339933;">=/</span>usr \
            <span style="color: #339933;">--</span>sysconfdir<span style="color: #339933;">=/</span>etc \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>tclinterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>pythoninterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>perlinterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>rubyinterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>cscope \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>multibyte \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>xim \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>gtk2<span style="color: #339933;">-</span>check \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>fontset \
            <span style="color: #339933;">--</span>with<span style="color: #339933;">-</span>x</pre></div></div>

<p>OK，编译完成之后removepkg vim删掉之前slackware安装包中提供的vim，然后用makepkg打包安装，哎呀，说起包管理方式我还是最喜欢slackware的tgz包，虽然没有debian的apt那么华丽，但简洁就是美啊。</p>
<p>vim安装完之后已经可以用了，但存在一个问题，vim退出之后终端便会乱码，到网上搜了半天各种关于ubuntu下的解法，拿到slackware上来完全不适用，命令啦路径啦什么的根本找不到，最后搜到一个非常简单的解决办法，说是这个问题是因为gnome-terminal标题名称的不标准，我矁了一眼标题写着“终端”两个字，给出的解决办法是在前后各加一个空格，让系统让为它是英文，我直接把它改成了Terminal，然后再启动vim然后退出就没有问题了。</p>
<p>总算解决了我对于vim的怨念，一直以为是vim的问题，没想到竟然是slackware打包的问题。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/vim-copy-paste-related.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Memcached的线程模型及状态机</title>
		<link>http://basiccoder.com/thread-model-and-state-machine-of-memcached.html</link>
		<comments>http://basiccoder.com/thread-model-and-state-machine-of-memcached.html#comments</comments>
		<pubDate>Mon, 05 Sep 2011 05:45:10 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[NoSql]]></category>
		<category><![CDATA[libevent]]></category>
		<category><![CDATA[Memcached]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=906</guid>
		<description><![CDATA[<a href="http://memcached.org/">Memcached</a>是一种应用较广泛的分布式内存对象缓存系统，应用之余总想了解它的实现机理，这也就是开源的好处，以至于每接触一款优秀的开源软件都有去阅读它源代码的冲动，Memcached-1.4.7的代码量还是可以接受的，只有10K行左右，我比较关心的两个方面还是它的进程（线程）管理机制和内存管理机制，这里先简单写一下我对Memcached进程管理方面的理解。

Memcached使用<a href="http://libevent.org/">libevent</a>实现事件循环，libevent在Linux环境下默认采用epoll作为IO多路复用方法，这个不重要，接下来要讨论的是Memcached的进程管理模型。

Memcached采用了很典型的Master-Worker模型，采用的是多线程而不是多进程，而线程之间没有冗余的共享数据，这样便降低了多线程进行线程同步的开销，核心的共享数据是消息队列，主线程会把收到的事件请求放入队列，随后调度程序会选择一个空闲的Worker线程来从队列中取出事件请求进行处理。

在main()函数里面，Memcached为主线程调用event_init()创建了一个libevent base对象，随后调用thread_init()来初始化线程，我们来看下这个函数的实现... ]]></description>
			<content:encoded><![CDATA[<p><a href="http://memcached.org/">Memcached</a>是一种应用较广泛的分布式内存对象缓存系统，应用之余总想了解它的实现机理，这也就是开源的好处，以至于每接触一款优秀的开源软件都有去阅读它源代码的冲动，Memcached-1.4.7的代码量还是可以接受的，只有10K行左右，我比较关心的两个方面还是它的进程（线程）管理机制和内存管理机制，这里先简单写一下我对Memcached进程管理方面的理解。</p>
<p>Memcached使用<a href="http://libevent.org/">libevent</a>实现事件循环，libevent在Linux环境下默认采用epoll作为IO多路复用方法，这个不重要，接下来要讨论的是Memcached的进程管理模型。</p>
<p>Memcached采用了很典型的Master-Worker模型，采用的是多线程而不是多进程，而线程之间没有冗余的共享数据，这样便降低了多线程进行线程同步的开销，核心的共享数据是消息队列，主线程会把收到的事件请求放入队列，随后调度程序会选择一个空闲的Worker线程来从队列中取出事件请求进行处理。</p>
<p>在main()函数里面，Memcached为主线程调用event_init()创建了一个libevent base对象，随后调用thread_init()来初始化线程，我们来看下这个函数的实现。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> thread_init<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> nthreads<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> event_base <span style="color: #339933;">*</span>main_base<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span>         i<span style="color: #339933;">;</span>
&nbsp;
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>cache_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>stats_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    pthread_cond_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_cond<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>cqi_freelist_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    cqi_freelist <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
&nbsp;
    threads <span style="color: #339933;">=</span> calloc<span style="color: #009900;">&#40;</span>nthreads<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>LIBEVENT_THREAD<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> threads<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Can't allocate thread descriptors&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    dispatcher_thread.<span style="color: #202020;">base</span> <span style="color: #339933;">=</span> main_base<span style="color: #339933;">;</span>
    dispatcher_thread.<span style="color: #202020;">thread_id</span> <span style="color: #339933;">=</span> pthread_self<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> nthreads<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">int</span> fds<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pipe<span style="color: #009900;">&#40;</span>fds<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Can't create notify pipe&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">notify_receive_fd</span> <span style="color: #339933;">=</span> fds<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">notify_send_fd</span> <span style="color: #339933;">=</span> fds<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
        setup_thread<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Create threads after we've done all the libevent setup. */</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> nthreads<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        create_worker<span style="color: #009900;">&#40;</span>worker_libevent<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Wait for all the threads to set themselves up before returning. */</span>
    pthread_mutex_lock<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>init_count <span style="color: #339933;">&lt;</span> nthreads<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        pthread_cond_wait<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_cond<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>init_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    pthread_mutex_unlock<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>代码很简单，初始化几个全局锁和一个条件变量，init_lock和init_cond这一对锁/条件变量的作用很明显，它们的作用是用来等待所有的worker线程启动完毕后，thread_init()才可以继续执行，见该函数最后的几句，非常通用的用法，等待init_count数达到预定的线程数后主线程方可继续执行，否则就一直在wait，每创建一个worker线程就会让init_count的值加1，创建worker线程的工作是在setup_thread()函数中进行的，这个后面讨论。</p>
<p>thread_init()在初始化完全局锁并为线程池分配分配空间之后便开始对线程池中的每个线程进行初始化，哦，在这之前把全局的调度线程设置成为主线程，OK，接下来就开始遍历线程池，每个线程都有一对notify_fd，它们通过管道连接，这个管道便是主线程对线程池中的工作线程进行调度的接口，看一下setup_thread()这个函数的实现（节省版面，只保留了关键代码）。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> setup_thread<span style="color: #009900;">&#40;</span>LIBEVENT_THREAD <span style="color: #339933;">*</span>me<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    me<span style="color: #339933;">-&gt;</span>base <span style="color: #339933;">=</span> event_init<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> me<span style="color: #339933;">-&gt;</span>base<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Can't allocate event base<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Listen for notifications from other threads */</span>
    event_set<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>me<span style="color: #339933;">-&gt;</span>notify_event<span style="color: #339933;">,</span> me<span style="color: #339933;">-&gt;</span>notify_receive_fd<span style="color: #339933;">,</span>
              EV_READ <span style="color: #339933;">|</span> EV_PERSIST<span style="color: #339933;">,</span> thread_libevent_process<span style="color: #339933;">,</span> me<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    event_base_set<span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>base<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>me<span style="color: #339933;">-&gt;</span>notify_event<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>event_add<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>me<span style="color: #339933;">-&gt;</span>notify_event<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Can't monitor libevent notify pipe<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    me<span style="color: #339933;">-&gt;</span>new_conn_queue <span style="color: #339933;">=</span> malloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> conn_queue<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>new_conn_queue <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Failed to allocate memory for connection queue&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span>EXIT_FAILURE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    cq_init<span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>new_conn_queue<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>首先调用event_init()为该线程初始化一个事件池，我们知道libevent的事件池（event base）不是线程安全的，所以每个线程需要有自己的事件池，所有的IO事件都由这个事件池来处理，在这个函数里面，首先将notify_receive_fd添加到事件监听循环中，设置回调函数thread_libevent_process()，当主线程向该worker线程通过pipe IPC发送消息时，便会触发该worker线程执行thread_libevent_process()函数。随后setup_thread()函数创建connection队列并将其初始化。</p>
<p>接着回过头看thread_init()函数，初始化线程池结束以后，所需要做的工作便是为线程池创建对应的线程，也就是调用了create_worker()函数，这个函数只不过把pthread_create()做了一个简单的封装，至少到目前为止里面没有什么特别的代码，线程的执行函数是worker_libevent()，该函数将全局线程数加一，随后便调用event_base_loop()，完全将事件循环交给了libevent，当然到目前为止libevent的事件池中只有用来进行IPC的管道文件描述符（说到这里，当时我还在奇怪呢，代码执行这里还并没有添加socket描述符到事件池里面，那event_loop岂不一执行就结束了，后来才反应过来里面有IPC管道文件描述符了），那么这些描述符是在什么时候添加的呢？刚才已经讨论过了，是在setup_thread()函数里面，它的事件回调函数是thread_libevent_process()，OK，接下来我们看看这个函数吧。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> thread_libevent_process<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> fd<span style="color: #339933;">,</span> <span style="color: #993333;">short</span> which<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>arg<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    LIBEVENT_THREAD <span style="color: #339933;">*</span>me <span style="color: #339933;">=</span> arg<span style="color: #339933;">;</span>
    CQ_ITEM <span style="color: #339933;">*</span>item<span style="color: #339933;">;</span>
    <span style="color: #993333;">char</span> buf<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>read<span style="color: #009900;">&#40;</span>fd<span style="color: #339933;">,</span> buf<span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>settings.<span style="color: #202020;">verbose</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
            fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Can't read from libevent pipe<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    item <span style="color: #339933;">=</span> cq_pop<span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>new_conn_queue<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>NULL <span style="color: #339933;">!=</span> item<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        conn <span style="color: #339933;">*</span>c <span style="color: #339933;">=</span> conn_new<span style="color: #009900;">&#40;</span>item<span style="color: #339933;">-&gt;</span>sfd<span style="color: #339933;">,</span> item<span style="color: #339933;">-&gt;</span>init_state<span style="color: #339933;">,</span> item<span style="color: #339933;">-&gt;</span>event_flags<span style="color: #339933;">,</span>
                           item<span style="color: #339933;">-&gt;</span>read_buffer_size<span style="color: #339933;">,</span> item<span style="color: #339933;">-&gt;</span>transport<span style="color: #339933;">,</span> me<span style="color: #339933;">-&gt;</span>base<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>c <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            close<span style="color: #009900;">&#40;</span>item<span style="color: #339933;">-&gt;</span>sfd<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            c<span style="color: #339933;">-&gt;</span>thread <span style="color: #339933;">=</span> me<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        cqi_free<span style="color: #009900;">&#40;</span>item<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>函数的一开始便从fd中读取一个字节，不在乎这一个字节是什么，只是主线程发过来的一个单字节（实际上是个\0），用来触发fd的READ事件，主线程通过往worker线程的notify_write_fd中写入一个单字节来实现对worker线程的调度，因此这个函数里面首先要先把这一个字节读取出来，否则会影响以后的事件循环。紧接着从connection队列中弹出一个item，然后根据这个item的信息再创建一个connection，这里的conn还是不要理解为连接的好，理解成任务更好一些，conn对象中有读写缓冲区，状态（用来实现Memcached的有限状态机）等，这conn_new()这个函数里面会创建一个conn对象并对它进行初始化，但最重要的操作是设置对socketfd的事件监听函数，event_handler()，这个函数又调用drive_machine()，其中就实现了Memcached的有限状态机，通过对state的不同值来进行不同的操作，具体的状态如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">const</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span> <span style="color: #993333;">const</span> statenames<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span> <span style="color: #ff0000;">&quot;conn_listening&quot;</span><span style="color: #339933;">,</span>
						      <span style="color: #ff0000;">&quot;conn_new_cmd&quot;</span><span style="color: #339933;">,</span>
						        <span style="color: #ff0000;">&quot;conn_waiting&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_read&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_parse_cmd&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_write&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_nread&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_swallow&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_closing&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_mwrite&quot;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>关于状态机是怎么运行的就不讨论了，代码篇幅过长，涉及到的细节太多，不展开说了，要说的一点是状态机在哪里进入的conn_listening状态，这是某个socketfd的起始状态，socket需要先监听然后accept，再之后才可以进行read,write等操作，listen必然会在程序初始化的过程中调用，我们可以看到在main()函数里面线程初始化结束以后，便开始对socket描述符进行初始化，获取系统存在几个可用的地址信息，然后创建socket描述符，再然后bind()，再之后调用listen()，这些操作都是在server_socket()函数中执行的，该函数初始化监听socket，并针对服务器做一些socket options的设置，最后最关键的一步是针对该socket调用了conn_new()，但事件池是main_base，也就是说accept()事件是由主线程接收到的，accept()是发生在主线程内，这样也就避免了多线程accept()的惊群，accept()函数的执行也是在状态机循环中执行的，drive_machine()函数中，accept()完成后，主线程便调用dispatch_conn_new()对该描述符进行调度，状态机的代码太长，不列出来了，看一看dispatch_conn_machine()的代码吧。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> dispatch_conn_new<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> sfd<span style="color: #339933;">,</span> <span style="color: #000000; font-weight: bold;">enum</span> conn_states init_state<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> event_flags<span style="color: #339933;">,</span>
                       <span style="color: #993333;">int</span> read_buffer_size<span style="color: #339933;">,</span> <span style="color: #000000; font-weight: bold;">enum</span> network_transport transport<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    CQ_ITEM <span style="color: #339933;">*</span>item <span style="color: #339933;">=</span> cqi_new<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> tid <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>last_thread <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">%</span> settings.<span style="color: #202020;">num_threads</span><span style="color: #339933;">;</span>
&nbsp;
    LIBEVENT_THREAD <span style="color: #339933;">*</span>thread <span style="color: #339933;">=</span> threads <span style="color: #339933;">+</span> tid<span style="color: #339933;">;</span>
&nbsp;
    last_thread <span style="color: #339933;">=</span> tid<span style="color: #339933;">;</span>
&nbsp;
    item<span style="color: #339933;">-&gt;</span>sfd <span style="color: #339933;">=</span> sfd<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>init_state <span style="color: #339933;">=</span> init_state<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>event_flags <span style="color: #339933;">=</span> event_flags<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>read_buffer_size <span style="color: #339933;">=</span> read_buffer_size<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>transport <span style="color: #339933;">=</span> transport<span style="color: #339933;">;</span>
&nbsp;
    cq_push<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">-&gt;</span>new_conn_queue<span style="color: #339933;">,</span> item<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    MEMCACHED_CONN_DISPATCH<span style="color: #009900;">&#40;</span>sfd<span style="color: #339933;">,</span> thread<span style="color: #339933;">-&gt;</span>thread_id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>write<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">-&gt;</span>notify_send_fd<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Writing to thread notify pipe&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>创建一个item并压入队列，然后通过一个简单的哈希从线程池中找出一个线程，写一个空字符来激活这个线程（前面讨论过了），接着被激活的线程就会从conn_queue中弹出一个item来进行处理。</p>
<p>OK，关于Memcached的线程模型要说的就这些吧，我自己读源码时对它的浅显的理解，欢迎大家批评讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/thread-model-and-state-machine-of-memcached.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Hybrid开发手记之聊天窗口的WebKit支持</title>
		<link>http://basiccoder.com/hybrid_dev_note_webkit_support_in_chat_window.html</link>
		<comments>http://basiccoder.com/hybrid_dev_note_webkit_support_in_chat_window.html#comments</comments>
		<pubDate>Fri, 05 Aug 2011 12:18:31 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[hybrid]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=896</guid>
		<description><![CDATA[近两天给Hybrid<a href="https://github.com/levin108/hybrid" target="_blank">(https://github.com/levin108/hybrid</a>)的聊天窗口加上了WebKit支持，之前没有实际用过WebKit，而且Web前台开发功力也不强，草草做了一个界面，但相比用GtkTextView来实现看上去还是要舒服好多，先上个图吧：
<a href="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit.png" target="_blank"><img src="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit-278x300.png" alt="" title="chat_with_robot_webkit" width="278" height="300" class="aligncenter size-medium wp-image-897" /></a>

本篇没有什么高深的东西，作为一个简单的开发文档。... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>近两天给Hybrid<a href="https://github.com/levin108/hybrid" target="_blank">(https://github.com/levin108/hybrid</a>)的聊天窗口加上了WebKit支持，之前没有实际用过WebKit，而且Web前台开发功力也不强，草草做了一个界面，但相比用GtkTextView来实现看上去还是要舒服好多，先上个图吧：<br />
<a href="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit.png" target="_blank"><img src="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit-278x300.png" alt="" title="chat_with_robot_webkit" width="278" height="300" class="aligncenter size-medium wp-image-897" /></a></p>
<p>本篇没有什么高深的东西，作为一个简单的开发文档。</p>
<p><strong>一，主题组件化的方法</strong></p>
<p>聊天窗口的显示区域已经组件化，并没有进行深层次的模块化，代码还是在一起编译的，只是逻辑上组件化了。</p>
<p>之前是固定的由GtkTextView实现，在加入GtkWebKit的时候同时也保留了GtkTextView的实现，这两者是可选的，不管是GtkWebKit还是GtkTextView都需要实现四个最基本的函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> GtkWidget<span style="color: #339933;">*</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>text_create<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>text_append<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>GtkWidget <span style="color: #339933;">*,</span> HybridAccount <span style="color: #339933;">*,</span>
							HybridBuddy <span style="color: #339933;">*,</span>	<span style="color: #993333;">const</span> gchar <span style="color: #339933;">*,</span> time_t<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>text_notify<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>GtkWidget <span style="color: #339933;">*,</span> <span style="color: #993333;">const</span> gchar <span style="color: #339933;">*,</span> gint<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>theme_set_ops_func<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>前三个函数是组件的操作函数，功能分别是创建聊天区域，向聊天区域中添加消息，向聊天区域中添加提示消息，最后一个是设置操作集合的钩子函数。</p>
<p>对于这两种不同的实现分别定义了两个文件，chat-textview.c和chat-webkit.c，这两个文件里面分别是两者各自的实现，而它们对外的接口只是一个GtkWidget，这得利于GOBJECT的这种类似多态的特性。</p>
<p>对于不同的实现会定义操作集变量：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> HybridChatTextOps webkit_ops <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	hybrid_chat_webkit_create<span style="color: #339933;">,</span>
	hybrid_chat_webkit_append<span style="color: #339933;">,</span>
	hybrid_chat_webkit_notify
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>聊天窗口当前使用的聊天区域实现方式全由该操作集来确定，而使用哪个操作集可以由两种方式各自的theme_set_ops_func函数来设置。</p>
<p>我们可以把两种组件看成两个不同的主题，在聊天窗口文件中定义了该主题的列表：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> _HybridChatTheme <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">const</span> gchar <span style="color: #339933;">*</span>name<span style="color: #339933;">;</span>
	theme_set_ops_func func<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">static</span> HybridChatTheme theme_list<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
<span style="color: #339933;">#ifdef USE_WEBKIT</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #ff0000;">&quot;webkit&quot;</span><span style="color: #339933;">,</span>
		hybrid_chat_set_webkit_ops
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> 
<span style="color: #339933;">#endif</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #ff0000;">&quot;textview&quot;</span><span style="color: #339933;">,</span>
		hybrid_chat_set_textview_ops
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>
		NULL<span style="color: #339933;">,</span> NULL
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>运行时程序会根据用户的当前配置情况来选择使用哪种主题。</p>
<p><strong>二，WebKit遇到的问题</strong></p>
<p>关于WebKit有几点小问题，第一次用难免会碰到些小问题，不过幸好还是解决掉了。</p>
<p><strong>1. undefined @1: ReferenceError: Can&#8217;t find variable</strong></p>
<p>WebKit外部来操作DOM模型主要是通过从外部调用webkit_web_view_execute_script()来实现的，当然我看最新的GtkWebKit API里面已经支持直接操作DOM了，但貌似手头的系统上安装的版本都还没有这个API函数，为了兼容性的考虑还是采用了传统的方法。在HTML模块中用Javascript定义了函数appendMessage(html)，通过这个函数向WebKit中定义聊天信息，但当收到消息自动弹出的时候会提示undefined @1: ReferenceError: Can&#8217;t find variable appendMessage()，这种情况的原因很简单，函数是定义了但找不到，原因只能是因为模板字符串还没有加载完成便调用了appendMessage()函数，因此会出现这样的错误，因此在对WebKit进行脚本操作之前首先要等它初始化完成，也就是等它load_finished之后，WebKit提供了load_finished事件，但这个事件目前已经Deprecated了，替代的方法是使用load_status属性，属性和事件的使用方法明显不一样，关于WebKit deprecate load_finished事件的原因我没去仔细想，load_status属性的方法只能是轮询，在收到消息时检测load_status，或未加载完成，则调用g_timeout_add()将操作延时后执行，执行中再检测load_status，若仍未完成则继续延时，直到加载完成为止，实际上在实现的时候我给g_timeout_add的第一个参数写了0，事实证明在进入加调函数时需要的那个简单的HTML模板就已经加载完成了，这时候再去调用webkit_web_view_execute_script()去添加一条新消息。</p>
<p><strong>2. Message: console message: undefined @1: SyntaxError: Parse error</strong></p>
<p>同样是在调用webkit_web_view_execute_script()遇到了上面的错误，通过对比和分析发现我传入的字符串参数中夹带了\n字符，把这个字符换成空格后便没有这个问题了，但换行在HTML中应该是<br/>，于是需要把\n替换成<br/>，C语言以及glibc都没有提供形如replace()这种方便的函数，于是我用GString，把字符一个一个的贴过去，遇到\n就贴一个<br/>在后面，效率是低点，但实现起来比较方便，代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> gchar<span style="color: #339933;">*</span>
escape_string<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> gchar <span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	GString <span style="color: #339933;">*</span>res<span style="color: #339933;">;</span>
&nbsp;
	res <span style="color: #339933;">=</span> g_string_sized_new<span style="color: #009900;">&#40;</span>strlen<span style="color: #009900;">&#40;</span>str<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>str <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">switch</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">case</span> <span style="color: #0000dd;">13</span><span style="color: #339933;">:</span>
				res <span style="color: #339933;">=</span> g_string_append<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;&lt;br/&gt;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">case</span> <span style="color: #ff0000;">'<span style="color: #000099; font-weight: bold;">\&quot;</span>'</span><span style="color: #339933;">:</span>
				res <span style="color: #339933;">=</span> g_string_append<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\\</span><span style="color: #000099; font-weight: bold;">\&quot;</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">case</span> <span style="color: #ff0000;">'<span style="color: #000099; font-weight: bold;">\t</span>'</span><span style="color: #339933;">:</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">default</span><span style="color: #339933;">:</span>
				res <span style="color: #339933;">=</span> g_string_append_c<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		str <span style="color: #339933;">++;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> g_string_free<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> FALSE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>以上大致完成了文章开头图片的样式，已经可以正常使用，但字体在我这slackware系统上还有些不太完美，也可能是我系统字体设置有问题，这个等后期再处理。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/hybrid_dev_note_webkit_support_in_chat_window.html/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>新开源项目Hybrid开发手记</title>
		<link>http://basiccoder.com/new-opensource-project-hybrid-dev-note.html</link>
		<comments>http://basiccoder.com/new-opensource-project-hybrid-dev-note.html#comments</comments>
		<pubDate>Sat, 30 Jul 2011 07:21:30 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[GTK]]></category>
		<category><![CDATA[hybrid]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=891</guid>
		<description><![CDATA[博客有两个月没更新了吧，先说说这段时间都在做些什么吧，六月初的时候实验室项目验收完，以为可以轻松下来了，于是开了一个新的开源项目在做，这个稍后再说，没过多久就被拉去给新项目做设计文档去了，这活做起来比硬编码要麻烦得多，于是大多数时间都在为这个事情心烦，稍微有点闲下来的时间就去写点代码，接下来说下最近在做的这个开源项目。

之前的Openfetion虽然也受到了一些开源社区朋友的好评，但软件质量怎么想我心里比谁都明白，在twitter上我也公开承认过Openfetion的代码质量以及软件架构都非常差，说到代码质量，最初在开发Openfetion的时候没有想过会把它作为一个通用的软件拿出来给大家用，而是自己纯粹在写着玩，于是写得很随意，当然这也得怪我这种恶劣的编程习惯，专业的coder即便是写一个测试用的小代码也会严格按规范来，这是一种习惯，我承认之前做得不够好。再说到软件架构，对于IM软件我之前一样没有什么经验，在没有经过调研的情况就盲目开始编码，完全没有参考现有的开源软件架构，甚至没有个合适的事件循环，所以Openfetion目前的状态是勉强能用。

之前考虑过重构Openfetion，但其实重构的成本要远远高于重写，于是我决定<strong>终止Openfetion项目</strong>，不再为Openfetion增加/修改代码，也不鼓励其它人对它进行修改... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>博客有两个月没更新了吧，先说说这段时间都在做些什么吧，六月初的时候实验室项目验收完，以为可以轻松下来了，于是开了一个新的开源项目在做，这个稍后再说，没过多久就被拉去给实验室的新项目做设计文档去了，这活做起来比硬编码要麻烦得多，于是大多数时间都在为这个事情心烦，稍微有点闲下来的时间就去写点代码，接下来说下最近在做的这个开源项目。</p>
<p>之前的Openfetion虽然也受到了一些开源社区朋友的好评，但软件质量怎么样我心里比谁都明白，在twitter上我也公开承认过Openfetion的代码质量以及软件架构都非常差，说到代码质量，最初在开发Openfetion的时候没有想过会把它作为一个通用的软件拿出来给大家用，而是自己纯粹在写着玩，于是写得很随意，当然这也得怪我这种恶劣的编程习惯，专业的coder即便是写一个测试用的小代码也会严格按规范来，这是一种习惯，我承认之前做得不够好。再说到软件架构，对于IM软件我之前一样没有什么经验，在没有经过调研的情况就盲目开始编码，完全没有参考现有的开源软件架构，甚至没有个合适的事件循环，所以Openfetion目前的状态是勉强能用。</p>
<p>之前考虑过重构Openfetion，但其实重构的成本要远远高于重写，于是我决定<strong>终止Openfetion项目</strong>，不再为Openfetion增加/修改代码，也不鼓励其它人对它进行修改。</p>
<p>作为Openfetion的替代品，我发起了新的开源项目Hybrid（<a href="https://github.com/levin108/hybrid" target="_blank">https://github.com/levin108/hybrid</a>），这同样是一款IM软件，我把它定位为类似于Pidgin/Empathy的IM框架，但即没有使用libpurple也没有使用telepathy，这两个库使用起来都还是有很多限制，我喜欢自由自在的写代码，我不太想考虑太多关于Hybrid这个软件能走多远，能有多少人用，我需要一款这样的软件，我想让它支持飞信和Gtalk，这是我最需要的两种通信协议，这就够了。</p>
<p>Hybrid同样也使用了GTK作为UI库，纯C语言开发，用了OpenSSL处理加密逻辑，使用glib的GIOChannel来实现了异步IO事件循环，同时把libxml2的API做了封装，xml的处理代码看起来简洁多了（libxml2的API命名规则看起来实在太难受了，而且还需要经常对它的自定义数据类型进行转换）。软件的整体架构上参考了一个pidgin，之前的Openfetion用多线程来处理并发逻辑，线程之间的同步开销很大，现在经常还会出现的崩溃问题大多来源于线程同步没有做好。现在采用的方法是采用单线程，将处理逻辑都交给GUI线程来进行处理，当然所有的IO都必须是非阻塞IO，通过异步IO的方法来检测IO事件，当前所有的操作都必须是非阻塞的，包括SSL/TLS握手等，但目前还存在的一个问题是DNS解析过程仍然是阻塞的，我还没有去研究如何实现非阻塞DNS解析，这个会加入TODO List中。至于模块化也是采用了glib中的gmodule来实现的，没有直接使用dlopen的方法，这样应该会提高一点可移植性吧。</p>
<p>关于协议模块，飞信的协议模块在现有的pidgin插件的基础上重写了，使用了封装过的xml接口，同样也使用了一致的编码风格，但在之前的基础上写代码效率就高了很多，很快就完成了。另外关于Gtalk的协议模块也没有花太多时间，用了一个周末的时间就把基础框架弄好了，因为用的是XMPP，开放的协议实现起来非常简单，只需要读一遍协议然后照着开发就好了，一路写下来都很顺，没有遇到什么大的麻烦。</p>
<p>总之到目前为止，这款软件已经基本能满足我的需要了，但还是有很多需要完善的地方，如好友的搜索，webkit的聊天界面，以及自定义请求对话框等，这些等以后慢慢再更新，现在面临毕业找工作，时间相对也较少，有空的时候就更新一点。</p>
<p>还没有想好什么时候要发布一个正式版，有一点点追求完美，总想把它做得更好一点，有感兴趣的同学可以去把代码clone下来玩玩，有用过的同学欢迎反鐀意见。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/new-opensource-project-hybrid-dev-note.html/feed</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>武汉Deepin Linux DAU会议见闻</title>
		<link>http://basiccoder.com/wuhan-deepin-linux-meeting-info.html</link>
		<comments>http://basiccoder.com/wuhan-deepin-linux-meeting-info.html#comments</comments>
		<pubDate>Wed, 01 Jun 2011 08:23:10 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=880</guid>
		<description><![CDATA[上次在ubuntu11.04 release party北京站遇到deepin linux负责人刘文欢，他也就是deepin本人，邀请我去参加上个周末在武汉举行的deepin linux DAU大会，当时没有马上答应他，因为怕实验室太忙时间安排不开，后来跟领导商量了下领导表示支持，于是就趁周末跑了过去，一方面是希望能见到开源社区的朋友，另一方面也是很想看看deepin这个出自国人之手的linux发行版会做到什么水准。

会议到场人数很多，我作为特邀嘉宾很惭愧地坐在了前排，会议第一部分是deepin主题演讲，deepin操作系统创始人hiweed介绍深度的发展以及现状，接着是深度软件中心的主要开发者王勇介绍软件中心的相关情况，我个人感觉软件中心还是一个很不错的软件，虽然看上去很容易让人联想到360的软件中心，但不能不说这确实是一个很符合用户使用习惯的软件，从软件的搜索和安装都比ubuntu软件中心要舒服一些，当然这也是我个人的感受，据deepin介绍，软件中心的开发者王勇只有22岁，而且并没有读过大学，当时我就对他特别敬佩，像我们这些读了这么多年书现在还是这样技术平平的人真应该感到惭愧。最后由deepin本人介绍开源社区的管理与运营，让大家对深度这个社区有了一个比较深入的认识。

会议的第二部分是主题嘉宾演讲，第一位是国内开源界的名人袁萌教授，讲永中Office的事情，袁萌可以说是相当有争议的人物了，之前在ubuntu 10.10和11.04的release party现场都见过袁老出现，而且对于这种开源活动袁老似乎每次都不会缺席，这种活动每次出现的为数不多的老年人身影也就只有袁老了，但不管是他在新浪博客上还是在其它地方，大家对他的评论都是褒贬不一，我之前对袁老不熟悉，后来在twitter上打听袁老的事情，有人表示袁老人不错，但口头老挂着永中和U盘的事情就让人有点烦了，确实，袁老讲话我听不太明白（口音原因），但言谈中总会提到永中和U盘的事情，我对此到没什么特别的看法，可能年纪大了人会比较偏执，遇到自己觉得好的东西就会一直推崇。再看他的新浪博客，有人支持也有人很负面地骂，确实让人感觉到袁老真是中国开源界最有争议的人物，其实我觉得像袁老这样一位古希老... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>上次在ubuntu11.04 release party北京站遇到deepin linux负责人刘文欢，他也就是deepin本人，邀请我去参加上个周末在武汉举行的deepin linux DAU大会，当时没有马上答应他，因为怕实验室太忙时间安排不开，后来跟领导商量了下领导表示支持，于是就趁周末跑了过去，一方面是希望能见到开源社区的朋友，另一方面也是很想看看deepin这个出自国人之手的linux发行版会做到什么水准。</p>
<p>会议到场人数很多，我作为特邀嘉宾很惭愧地坐在了前排，会议第一部分是deepin主题演讲，deepin操作系统创始人hiweed介绍深度的发展以及现状，接着是深度软件中心的主要开发者王勇介绍软件中心的相关情况，我个人感觉软件中心还是一个很不错的软件，虽然看上去很容易让人联想到360的软件中心，但不能不说这确实是一个很符合用户使用习惯的软件，从软件的搜索和安装都比ubuntu软件中心要舒服一些，当然这也是我个人的感受，据deepin介绍，软件中心的开发者王勇只有22岁，而且并没有读过大学，当时我就对他特别敬佩，像我们这些读了这么多年书现在还是这样技术平平的人真应该感到惭愧。最后由deepin本人介绍开源社区的管理与运营，让大家对深度这个社区有了一个比较深入的认识。</p>
<p>会议的第二部分是主题嘉宾演讲，第一位是国内开源界的名人袁萌教授，讲永中Office的事情，袁萌可以说是相当有争议的人物了，之前在ubuntu 10.10和11.04的release party现场都见过袁老出现，而且对于这种开源活动袁老似乎每次都不会缺席，这种活动每次出现的为数不多的老年人身影也就只有袁老了，但不管是他在新浪博客上还是在其它地方，大家对他的评论都是褒贬不一，我之前对袁老不熟悉，后来在twitter上打听袁老的事情，有人表示袁老人不错，但口头老挂着永中和U盘的事情就让人有点烦了，确实，袁老讲话我听不太明白（口音原因），但言谈中总会提到永中和U盘的事情，我对此到没什么特别的看法，可能年纪大了人会比较偏执，遇到自己觉得好的东西就会一直推崇。再看他的新浪博客，有人支持也有人很负面地骂，确实让人感觉到袁老真是中国开源界最有争议的人物，其实我觉得像袁老这样一位古希老人，不图名利，仍然能对中国的开源事业如此热心，已经实属不易了，即便大家对他的观点不认同，也不至于用那么偏激的语言去骂他，除非你让我去相信这样一位风烛残年的退休老教授还在用某种低劣的手段去谋取私利，与袁老见面听他言谈，感觉他人非常正直，绝不是什么小人，所以我觉得大家可以不喜欢他，但真的没有必要去骂他。</p>
<p>会议袁老讲完后我也给大家就我的一些经历做了个小报告，简单介绍了一个飞信的开发过程以及我个人的Linux使用经验等，接下来是中科大的张成同学讲在校园同推广Linux，张成是mirrors.ustc.edu.cn的现任维护者，中科大开源协会的副会长，感觉中科大的开源协会做得蛮不错的，大家都很热心地在做一些很有意义的事情，张成技术也非常不错，再一次让我表示汗颜。再之后便是在场的用户提问，具体都问了哪些问题我也记不清了，我也没有刻意地去做会议记录。</p>
<p>会议结束后第二天晚上我和deepin在他们公司单独聊了两个多小时，deepin确实是想在Linux发行版这一块做些事情出来的，在中国做这样的事情难免会遇到种种阻力，包括舆论上的政策上的等等，红旗这样一个烂到根的公司似乎让国人对国产Linux发行版很是绝望，雨林木枫反对的人也很多，我到不太了解原因是什么，我个人不太喜欢它是因为它之前山寨了Windows的UI来适应中国用户的使用习惯，其实说起来如果没有什么版权协议的问题的话也没什么不好。做操作系统是个很漫长的过程，不可能在一两年内就得到用户的普遍认可，即便是ubuntu也是经历了一个长期的过程，Canonical公司也出资做了那么久的公益，说实话，哪家做Linux的公司纯粹做公益我们都没法相信，包括Canonical也是想用ubuntu做出一个品牌再去拓展其它的业务，如ubuntu server. 当然，深度的经济实力远不如Canonical，但做发行版在最开始是很难有经济回报的，我们所能期望的就是深度能坚持下去，毕竟深度和红旗是两家完全不同性质的公司，应该不会像红旗那样腐化，而做这样一个发行版也一直是hiweed和deepin的梦想，其实我个人还是对他们挺有信心的，即便中国人做不出来自己自主研发的操作系统，能出一个被国际认同的Linux发行版也确实是一件好事。</p>
<p>我是站在一个中立的立场，我支持我认为好的东西，如果有一天深度变成了红旗那我也会跟着去骂的。当时deepin在北京给我演示深度系统的时候我真地觉得很不错，起码它降低了新手的技术门坎，通过虚拟化的手段让OFFICE和QQ都跑在了上面，可以说这两个软件对很多新用户来说都是很大的壁垒，这个问题解决了就解决了很多新手的烦恼，我个人觉得任何一家第三方的OFFICE都很难与MS OFFICE兼容，即便是永中它的公式编辑器也完全跟不上。他们没有集成这两款软件，也就不涉及到版权协议之类的问题了。</p>
<p>总之这一行还是受益匪浅的，希望几年之后deepin能成为让国人为之骄傲的发行版，当然也希望它千万不要成为第二个红旗。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/wuhan-deepin-linux-meeting-info.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Apache MPM Prefork设计方法浅析</title>
		<link>http://basiccoder.com/apache-mpm-prefork-design-intro.html</link>
		<comments>http://basiccoder.com/apache-mpm-prefork-design-intro.html#comments</comments>
		<pubDate>Mon, 16 May 2011 15:33:34 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Apache]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=876</guid>
		<description><![CDATA[
最近几天翻阅了apache的<a href="http://httpd.apache.org/docs/2.0/mpm.html" target="_blank">MPM(Multi-Processing Module)</a>机制相关的代码，虽然还有很多细节没有搞明白，但对apache的服务器模型有了一个大体的概念，对于不同的操作系统，apache提供了不同的默认MPM模型，下表是不同操作系统默认的MPM模型：
<table> 
 
<tr><td>BeOS</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/beos.html">beos</a></code></td></tr> 
<tr><td>Netware</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_netware.html">mpm_netware</a></code></td></tr> 
<tr><td>OS/2</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpmt_os2.html">mpmt_os2</a></code></td></tr> 
<tr><td>Unix</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/prefork.html">prefork</a></code></td></tr> 
<tr><td>Windows</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_winnt.html">mpm_winnt</a></code></td></tr> 
</table> 

Unix平台则对应着prefork模型，prefork从名字上看意思是预先生成子进程，所以这种模型大致上是怎么工作的我们心里差不多有些认识了，prefork是一种很重要的服务器程序设计模型，对应的还有prethread，prefork一般应用在Unix平台上，因为在服务器启动时需要预告fork出一些空闲的子进程，由它们共同监听客户端的请求，这样来实现快速高并发的特性，这种机制之所以不适合Windows等平台，是因为在Windows等平台上进程的代价太高。

apache的进程管理中有一个叫做scoreboard(记分牌)的概念，主进程在进入MPM循环以前会先在进程池中创建一个scoreboard对象，该对象定义如下... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>最近几天翻阅了apache的<a href="http://httpd.apache.org/docs/2.0/mpm.html" target="_blank">MPM(Multi-Processing Module)</a>机制相关的代码，虽然还有很多细节没有搞明白，但对apache的服务器模型有了一个大体的概念，对于不同的操作系统，apache提供了不同的默认MPM模型，下表是不同操作系统默认的MPM模型：</p>
<table>
<tr>
<td>BeOS</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/beos.html">beos</a></code></td>
</tr>
<tr>
<td>Netware</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_netware.html">mpm_netware</a></code></td>
</tr>
<tr>
<td>OS/2</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpmt_os2.html">mpmt_os2</a></code></td>
</tr>
<tr>
<td>Unix</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/prefork.html">prefork</a></code></td>
</tr>
<tr>
<td>Windows</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_winnt.html">mpm_winnt</a></code></td>
</tr>
</table>
<p>Unix平台则对应着prefork模型，prefork从名字上看意思是预先生成子进程，所以这种模型大致上是怎么工作的我们心里差不多有些认识了，prefork是一种很重要的服务器程序设计模型，对应的还有prethread，prefork一般应用在Unix平台上，因为在服务器启动时需要预告fork出一些空闲的子进程，由它们共同监听客户端的请求，这样来实现快速高并发的特性，这种机制之所以不适合Windows等平台，是因为在Windows等平台上进程的代价太高。</p>
<p>apache的进程管理中有一个叫做scoreboard(记分牌)的概念，主进程在进入MPM循环以前会先在进程池中创建一个scoreboard对象，该对象定义如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
    global_score <span style="color: #339933;">*</span>global<span style="color: #339933;">;</span>
    process_score <span style="color: #339933;">*</span>parent<span style="color: #339933;">;</span>
    worker_score <span style="color: #339933;">**</span>servers<span style="color: #339933;">;</span>
    lb_score     <span style="color: #339933;">*</span>balancers<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> scoreboard<span style="color: #339933;">;</span></pre></div></div>

<p>global_score保存主进程的状态，process_score则是一个数组插槽，每个插槽保存一个子进程的状态，worker_score则是一个二维数组插槽，用来保存每个子进程创建的线程状态，根据这个结构主进程可以对子进程以及相关的线程进行管理，apache按照最大化的原则来分配内存，比如会按配置中允许最多的进程数目来为parent分配内存空间。</p>
<p>MPM初始化还有一个很重要的方面是创建一个进程锁，fork()出来的子进程与父进程并不共享内存空间，多进程之于多线程的优势在于多进程可以省去多线程进行线程同步的开销，而这里创建的进程锁，主要作用是为了给accept()加锁，为了避免<a href="http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html" target="_blank">thundering herd</a>问题。apache实现了五种类型的进程锁，使用flock()或fcntl()实现的文件锁，Posix信号量或System V信号量，以及使用pthread线程库实现的互斥锁。我理解的是文件锁的效率会低于其它类型的锁，因为文件锁要涉及到文件系统的IO操作。我只阅读了跟pthread相关的代码，和一般的多进程程序实现方式一致，因为子进程与父进程以及子进程之间不共享内存空间的，所以不可能像多线程程序一样将互斥锁定义为全局变量 ，因此使用共享内存机制，将互斥锁变量存放到共享内存里面，并设置共享属性。然后便可以使用该互斥锁对子进程中的accept()过程进行加锁。</p>
<p>初始化最后需要开始创建预定个数的子进程，调用startup_children()函数创建指定个数的子进程，该函数会检查scoreboard的空闲插槽，在空闲插槽上调用make_child()函数来在该插槽位置处创建一个子进程，该函数设置scoreboard中进程的状态，并fork()一个子进程，将子进程的pid写入到scoreboard对应的插槽处，子进程创建之后设置SIGHUP和SIGTERM信号，这两个信号对应的回调函数均为clean_child_exit()函数，该函数销毁内存池然后退出子进程。</p>
<p>make_child()函数执行成功后进行child_main()函数，即子进程的主循环。该函数看起来比较复杂，其实做的事情也很简单，首先是创建相关的内存池，对进程锁进行初始化（对于pthread进程锁对应是一个空函数，即无需进行初始化），将socket描述符加入到pollset中，这里的pollset也是apache抽象出来的概念，它的实现可以是kqueue/port/epoll/poll/select，具体采用哪种方式也是配置可选的。这里是我不太明白的地方，经常看到评论说nginx效率高于apache，当问起nginx效率高于apache的主要原因时，得到的答案很多都是nginx采用kqueue和epoll实现了高并发，其实感觉这个理由并不充分，我们可以看到apache同样也实现了kqueue和epoll的多路复用，如果这因为这个的话那apache没有理由会比nginx效率低多少的，另外也看到有说apache的进程管理机制占用内存过高，而且时常需要进行进程切换从而占用了CPU时间，这个说法可以接受，现在非常想去读下nginx的源码，想看看它到底是采用了什么样的机制带来了它如此之多的好评，下一步就可始阅读下nginx的源码，对比着apache，探索一些高并发服务器设计的最优方法。</p>
<p>子进程进入主循环之后会调用accept()方法，这个方法是需要进行加锁的，之后创建一个新的连接对象，并调用HOOK函数对连接进行处理，HOOK机制是apache模块化很重要的一种机制，在主程序中调用HOOK函数，具体的实现由具体的模块来定义。</p>
<p>父进程在创建完子进程之后也进行主循环，监控活动子进程的数目，并通过一定的调度使用子进程数目维护一个平衡，父进程使用waitpid()函数来检测子进程的退出情况，如果有进程退出，则创建一个新的进程来替代已结束的进程从而维持总数的一个平衡。</p>
<p>当然apache还有平稳启动机制，关于平衡启动的代码我暂时略过了，没有细读，以后有时间再回过头来仔细研究。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/apache-mpm-prefork-design-intro.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Openfetion for ubuntu messaging menu开发手记</title>
		<link>http://basiccoder.com/openfetion-for-ubuntu-messaging-menu-dev-note.html</link>
		<comments>http://basiccoder.com/openfetion-for-ubuntu-messaging-menu-dev-note.html#comments</comments>
		<pubDate>Tue, 03 May 2011 07:24:07 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[gnome]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=864</guid>
		<description><![CDATA[自从ubuntu11.04发布之后Openfetion就遇到了一个比较麻烦的问题，把Openfetion飞信最小化到托盘之后就找不到了，没用过ubuntu11.04，不过据说它的unity桌面貌似没有status icon这回事，所以把Openfetion塞进Messaging Menu也成了一个很重要的任务，在这里把开发过程和大家分享。

首先要感谢<a href="http://twitter.com/YunQiangSu">@YunQiangSu</a>提供的关于ubuntu messaging menu的资料，是在ubuntu wiki上关于Messaging Menu的介绍，链接在此：<a href="https://wiki.ubuntu.com/MessagingMenu/">https://wiki.ubuntu.com/MessagingMenu/</a>，这篇文章是关于Messaging Menu的一个介绍，以及它的行为和样式的一个指南，虽然没有涉及到具体的开发细则，但也不失为一个很重要的参考。很惭愧地说，在这之前我甚至不知道在ubuntu右上角看到的那个信封到底叫什么名字，后来知道它原来是一个Indicator，ubuntu的status icon区域很多软件都是用<a href="https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationIndicators">libappindicator</a>来实现的，所以它们的行为和其它的发行版不太一致，比如Dropbox和Transimission左键点击Status Icon就可以弹出菜单，而在Slackware里面就只能用右键才能弹出菜单，这就是它们的不同，一个是Indicator，一个是普通的Gnome Status Icon，而这里面提到的右上角的那个信封便是Indiactor的一种，名字叫做Messaging Menu。好吧，我相信很多ubuntu用户会过来鄙视我的，写下这些给那些和我一样的小白扫扫盲，有误请指正。

我们可以把软件安装到Messaging Menu里面，这样即使软件没有启动也可以在Messaging Menu里面找到该软件，并可以从那里启动该软件，方法很简单。下面是pidgin的做法：
<pre lang="bash">
mkdir -p debian/pidgin/usr/share/indicators/messages/applications
echo /usr/share/applications/pidgin.desktop > \
		 debian/pidgin/usr/share/indicators/messages/applications/pidgin
</pre>
这两句话的功能一眼就看地出来，无须解释了... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>自从ubuntu11.04发布之后Openfetion就遇到了一个比较麻烦的问题，把Openfetion飞信最小化到托盘之后就找不到了，没用过ubuntu11.04，不过据说它的unity桌面貌似没有status icon这回事，所以把Openfetion塞进Messaging Menu也成了一个很重要的任务，在这里把开发过程和大家分享。</p>
<p>首先要感谢<a href="http://twitter.com/YunQiangSu">@YunQiangSu</a>提供的关于ubuntu messaging menu的资料，是在ubuntu wiki上关于Messaging Menu的介绍，链接在此：<a href="https://wiki.ubuntu.com/MessagingMenu/">https://wiki.ubuntu.com/MessagingMenu/</a>，这篇文章是关于Messaging Menu的一个介绍，以及它的行为和样式的一个指南，虽然没有涉及到具体的开发细则，但也不失为一个很重要的参考。很惭愧地说，在这之前我甚至不知道在ubuntu右上角看到的那个信封到底叫什么名字，后来知道它原来是一个Indicator，ubuntu的status icon区域很多软件都是用<a href="https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationIndicators">libappindicator</a>来实现的，所以它们的行为和其它的发行版不太一致，比如Dropbox和Transimission左键点击Status Icon就可以弹出菜单，而在Slackware里面就只能用右键才能弹出菜单，这就是它们的不同，一个是Indicator，一个是普通的Gnome Status Icon，而这里面提到的右上角的那个信封便是Indiactor的一种，名字叫做Messaging Menu。好吧，我相信很多ubuntu用户会过来鄙视我的，写下这些给那些和我一样的小白扫扫盲，有误请指正。</p>
<p>我们可以把软件安装到Messaging Menu里面，这样即使软件没有启动也可以在Messaging Menu里面找到该软件，并可以从那里启动该软件，方法很简单。下面是pidgin的做法：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">mkdir</span> <span style="color: #660033;">-p</span> debian<span style="color: #000000; font-weight: bold;">/</span>pidgin<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>share<span style="color: #000000; font-weight: bold;">/</span>indicators<span style="color: #000000; font-weight: bold;">/</span>messages<span style="color: #000000; font-weight: bold;">/</span>applications
<span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>share<span style="color: #000000; font-weight: bold;">/</span>applications<span style="color: #000000; font-weight: bold;">/</span>pidgin.desktop <span style="color: #000000; font-weight: bold;">&gt;</span> \
		 debian<span style="color: #000000; font-weight: bold;">/</span>pidgin<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>share<span style="color: #000000; font-weight: bold;">/</span>indicators<span style="color: #000000; font-weight: bold;">/</span>messages<span style="color: #000000; font-weight: bold;">/</span>applications<span style="color: #000000; font-weight: bold;">/</span>pidgin</pre></div></div>

<p>这两句话的功能一眼就看地出来，无须解释了。</p>
<p>Messaging Menu相关的开发需要用到<a href="http://developer.ubuntu.com/api/devel/libindicate/">libindicate</a>这个库，首先得确保在系统里面安装了这个库的开发版本：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> libindicate-dev</pre></div></div>

<p>首先我们需要获取默认IndicateServer对象的引用，并对其进行初始化：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">IndicateServer <span style="color: #339933;">*</span>server <span style="color: #339933;">=</span> indicate_server_ref_default<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* 这句话给软件分类，主要是保存位置不同 */</span>
indicate_server_set_type<span style="color: #009900;">&#40;</span>server<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;message.openfetion&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* 这句话比较重要，它会让已安装在Messaging Menu里面的软件项前面带一个箭头，
 * 表示该软件当前正在运行，效果图见下面 */</span>
indicate_server_set_desktop_file<span style="color: #009900;">&#40;</span>server<span style="color: #339933;">,</span> DESKTOP_DIR<span style="color: #ff0000;">&quot;openfetion.desktop&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
indicate_server_show<span style="color: #009900;">&#40;</span>server<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
g_signal_connect<span style="color: #009900;">&#40;</span>G_OBJECT<span style="color: #009900;">&#40;</span>server<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> INDICATE_SERVER_SIGNAL_SERVER_DISPLAY<span style="color: #339933;">,</span> 
		G_CALLBACK<span style="color: #009900;">&#40;</span>server_display<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> fxmain<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>当收到好友发送过来的消息时可以在该项后面显示哪个好友发送过来的消息，以及显示未读消息的条数，效果图如下：<br />
<a href="http://basiccoder.com/wp-content/uploads/2011/05/2011-05-03-151159_1680x1050_scrot.png"><img src="http://basiccoder.com/wp-content/uploads/2011/05/2011-05-03-151159_1680x1050_scrot.png" alt="" title="2011-05-03-151159_1680x1050_scrot" width="230" height="277" class="aligncenter size-full wp-image-871" /></a><br />
这时候就需要给当前的这个IndicateServer对象添加一个IndicateIndicator对象，并为该对象设置相关的属性，如name（即显示Indicator上显示的名字），count(未读消息的数目)，time(消息发送的时间)，icon(像如发送消息好友的头像)，draw-attention(这个属性可以随便设置一个非空字符串，以使Indicator高亮提示用户有消息到达，同理设置空字符串可取消高亮)。<br />
上面的几个属性除了time和icon之外都可以用indicate_indicator_set_property()函数来搞定，该函数设置的属性值都是字符串，time是一个时间值，需要使用indicate_indicator_set_property_time()函数来进行设置，至于icon属性的设置需要用到另外一个库libindicate-gtk，这个库仅提供了两个函数，其中一个便是indicate_indicator_set_proper_icon()用来设置icon属性，属性值是一个GdkPixbuf对象。下面看Openfetion上收到一条消息时Messaging Menu所对的动作：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* no indicator found, create one :) */</span>
indicator <span style="color: #339933;">=</span> indicate_indicator_new<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* add it to the global indicator list */</span>
indicators <span style="color: #339933;">=</span> g_slist_append<span style="color: #009900;">&#40;</span>indicators<span style="color: #339933;">,</span> indicator<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
indicate_server_add_indicator<span style="color: #009900;">&#40;</span>server<span style="color: #339933;">,</span> indicator<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
indicate_indicator_set_property<span style="color: #009900;">&#40;</span>indicator<span style="color: #339933;">,</span> INDICATE_INDICATOR_MESSAGES_PROP_COUNT<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;0&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
indicate_indicator_set_property<span style="color: #009900;">&#40;</span>indicator<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;sid&quot;</span><span style="color: #339933;">,</span> sid<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* set icon */</span>
snprintf<span style="color: #009900;">&#40;</span>portrait<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>portrait<span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;%s/%s.jpg&quot;</span><span style="color: #339933;">,</span> fxmain<span style="color: #339933;">-&gt;</span>user<span style="color: #339933;">-&gt;</span>config<span style="color: #339933;">-&gt;</span>iconPath<span style="color: #339933;">,</span> sid<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
pixbuf <span style="color: #339933;">=</span> gdk_pixbuf_new_from_file<span style="color: #009900;">&#40;</span>portrait<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
indicate_indicator_set_property_icon<span style="color: #009900;">&#40;</span>indicator<span style="color: #339933;">,</span> INDICATE_INDICATOR_MESSAGES_PROP_ICON<span style="color: #339933;">,</span> pixbuf<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
g_object_unref<span style="color: #009900;">&#40;</span>pixbuf<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
g_signal_connect<span style="color: #009900;">&#40;</span>G_OBJECT<span style="color: #009900;">&#40;</span>indicator<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> INDICATE_INDICATOR_SIGNAL_DISPLAY<span style="color: #339933;">,</span>
		G_CALLBACK<span style="color: #009900;">&#40;</span>message_display<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
indicate_indicator_show<span style="color: #009900;">&#40;</span>indicator<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>首先我们需要确定在Messaging Menu中是否已经有该用户对应的Indicator存在，如果有就没有必要再创建一个新的造成重复，而检测重复性这个问题费了我不小的力气，libindicate没有提供获取列表的API，但查了一下它的源码，发现它确实实现了一个获取列表的函数，它把它定义为虚函数，推荐用户从这个类继承，我对GTK的C语言面向对象的机制不太了解，不知道该怎么去继承，但既然有这个函数就可以想办法拿来调用，能获取到Indicator列表，但这个列表简直让我抓狂，一个GArray列表，里面存放的是Indicator的id，一个毫无用处的数字，通过它根本获取不到Indicator对象，更不用说Indicator对象的属性了，因此我只能用自己的方法来实现了，其实很简单，就是每创建一个Indicator对象就把它塞到一个GSList里面，创建之前搜索该链表，如果其中有indicator的sid值与要创建的sid相同，则表示已存在，无需再重新创建，把它的count值加1即可。</p>
<p>有一点需要注意的是libindicate-gtk新旧版本的pc文件名是不同的（感谢<a href="http://twitter.com/riku">@riku</a>的测试），在10.04上是旧版pc文件名是indicate-gtk.pc，因此编译选项可以是</p>
<pre>pkg-config --cflags --libs indicate-gtk</pre>
<p>而在ubuntu 11.04中文件名是indicate-gtk-0.5.pc，编译选项便是：</p>
<pre>pkg-config --cflags --libs indicate-gtk-0.5</pre>
<p>我在CMakeLists.txt做了一些处理来让它自动识别这两个版本的不同。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/openfetion-for-ubuntu-messaging-menu-dev-note.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Apache基础数据结构(tables)代码浅析</title>
		<link>http://basiccoder.com/apache-tables-structure-sourcecode-intro.html</link>
		<comments>http://basiccoder.com/apache-tables-structure-sourcecode-intro.html#comments</comments>
		<pubDate>Fri, 29 Apr 2011 09:38:23 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Apache]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=856</guid>
		<description><![CDATA[提到Apache社区脑子里立马会呈现出一系列的Java项目集合,尽管@gnawux师兄教导我不要纠结于语言,但对于Java的抵制还是很难一下子就消失的，所幸Apache社区最重量级的项目Apache开源HTTP服务器httpd的源代码是完全使用C语言开发的，尽管近年来涌现出种种轻量级高性能Web服务器，Apache仍以它的功能广泛和真正的高性能而处于无可取代的位置。

Apache也经常受人诟病，矛头直指它的低效，我没有太多这方面的经验，因此没有过多的发言权，看到一些大牛们对这个问题的评论是觉得Apache低效是因为对Apache缺少了解。我个人也觉得这样一款多年风行Web服务器领域的软件肯定有它存在的理由，我们认为它不好可能是我们对它的了解不够。

在网上看到过关于Apache源码是否值得阅读的讨论，很多人认为Apache源码组织结构不好，不太适合阅读，更多人推荐nginx等源码，其实我觉得像Apache这种项目正是”重剑无锋，大巧不工“，Linux源码也有人评价说组织的不好，可读它的代码也仍然非常有意义。我个人认为阅读Apache源码一方面能加深对Apache的了解，在使用的时候真正发挥出它的高性能，另一方面也能借鉴它作为大型服务器软件设计的方法。当然，如果要编写扩展模块来实现一些附加功能的话那阅读源码甚至是必须的工作。

以上都是我个人的理解，最近简单地翻阅了部分代码，长时间没有更新博客，这里就写一些Apache里面最基本的tables数据结构（和其它模块关联较小，相对比较独立，当然我也觉得读任何源码都应该先搞清楚它最基础的数据结构）... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>提到Apache社区脑子里立马会呈现出一系列的Java项目集合,尽管@gnawux师兄教导我不要纠结于语言,但对于Java的抵制还是很难一下子就消失的，所幸Apache社区最重量级的项目Apache开源HTTP服务器httpd的源代码是完全使用C语言开发的，尽管近年来涌现出种种轻量级高性能Web服务器，Apache仍以它的功能广泛和真正的高性能而处于无可取代的位置。</p>
<p>Apache也经常受人诟病，矛头直指它的低效，我没有太多这方面的经验，因此没有过多的发言权，看到一些大牛们对这个问题的评论是觉得Apache低效是因为对Apache缺少了解。我个人也觉得这样一款多年风行Web服务器领域的软件肯定有它存在的理由，我们认为它不好可能是我们对它的了解不够。</p>
<p>在网上看到过关于Apache源码是否值得阅读的讨论，很多人认为Apache源码组织结构不好，不太适合阅读，更多人推荐nginx等源码，其实我觉得像Apache这种项目正是”重剑无锋，大巧不工“，Linux源码也有人评价说组织的不好，可读它的代码也仍然非常有意义。我个人认为阅读Apache源码一方面能加深对Apache的了解，在使用的时候真正发挥出它的高性能，另一方面也能借鉴它作为大型服务器软件设计的方法。当然，如果要编写扩展模块来实现一些附加功能的话那阅读源码甚至是必须的工作。</p>
<p>以上都是我个人的理解，最近简单地翻阅了部分代码，长时间没有更新博客，这里就写一些Apache里面最基本的tables数据结构（和其它模块关联较小，相对比较独立，当然我也觉得读任何源码都应该先搞清楚它最基础的数据结构）。</p>
<p>C语言里面没有类似于STL的vector这样的数据结构，动态数组肯定是需要自己来实现的。看一下apache里面关于动态数组结构的定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> apr_array_header_t <span style="color: #009900;">&#123;</span>
    <span style="color: #808080; font-style: italic;">/** The pool the array is allocated out of */</span>
    apr_pool_t <span style="color: #339933;">*</span>pool<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/** The amount of memory allocated for each element of the array */</span>
    <span style="color: #993333;">int</span> elt_size<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/** The number of active elements in the array */</span>
    <span style="color: #993333;">int</span> nelts<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/** The number of elements allocated in the array */</span>
    <span style="color: #993333;">int</span> nalloc<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/** The elements in the array */</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>elts<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>pool</strong>是内存池，apache有自己的内存管理机制，它在内存池中分配内存，而不是直接使用malloc分配，内存池由apache统一进行管理，这是后话，现在只需要知道在apache中如果要申请一块内存空间就必须有一个对应的内存池。</p>
<p><strong>elt_size</strong>是数组中每个元素的大小。</p>
<p><strong>nelts</strong>是数组中活跃元素的个数。何为活跃元素？相对于nalloc来看，nalloc是数组中分配空间的元素个数，它在数组创建的时候会先分配nalloc个元素的空间，而这些内存空间仅仅分配而未进行初始化，也就是nelts为0，当往数组中插入元素时，nelts会递增，nelts也就是数组中实际存在的元素个数。当nelts个数大于nalloc时，则表明数组中已分配的内存空间不够，则需要再从内存池中申请额外的空间来扩充数组的容量。</p>
<p><strong>elts</strong>便指向元素列表的起始地址。</p>
<p>创建一个数组：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">APR_DECLARE<span style="color: #009900;">&#40;</span>apr_array_header_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> apr_array_make<span style="color: #009900;">&#40;</span>apr_pool_t <span style="color: #339933;">*</span>p<span style="color: #339933;">,</span>
						<span style="color: #993333;">int</span> nelts<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> elt_size<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    apr_array_header_t <span style="color: #339933;">*</span>res<span style="color: #339933;">;</span>
&nbsp;
    res <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>apr_array_header_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> apr_palloc<span style="color: #009900;">&#40;</span>p<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>apr_array_header_t<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    make_array_core<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> p<span style="color: #339933;">,</span> nelts<span style="color: #339933;">,</span> elt_size<span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> res<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #993333;">static</span> <span style="color: #993333;">void</span> make_array_core<span style="color: #009900;">&#40;</span>apr_array_header_t <span style="color: #339933;">*</span>res<span style="color: #339933;">,</span> apr_pool_t <span style="color: #339933;">*</span>p<span style="color: #339933;">,</span>
			    <span style="color: #993333;">int</span> nelts<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> elt_size<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> clear<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #808080; font-style: italic;">/*
     * Assure sanity if someone asks for
     * array of zero elts.
     */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>nelts <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        nelts <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>clear<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        res<span style="color: #339933;">-&gt;</span>elts <span style="color: #339933;">=</span> apr_pcalloc<span style="color: #009900;">&#40;</span>p<span style="color: #339933;">,</span> nelts <span style="color: #339933;">*</span> elt_size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        res<span style="color: #339933;">-&gt;</span>elts <span style="color: #339933;">=</span> apr_palloc<span style="color: #009900;">&#40;</span>p<span style="color: #339933;">,</span> nelts <span style="color: #339933;">*</span> elt_size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    res<span style="color: #339933;">-&gt;</span>pool <span style="color: #339933;">=</span> p<span style="color: #339933;">;</span>
    res<span style="color: #339933;">-&gt;</span>elt_size <span style="color: #339933;">=</span> elt_size<span style="color: #339933;">;</span>
    res<span style="color: #339933;">-&gt;</span>nelts <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>		<span style="color: #808080; font-style: italic;">/* No active elements yet... */</span>
    res<span style="color: #339933;">-&gt;</span>nalloc <span style="color: #339933;">=</span> nelts<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* ...but this many allocated */</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>上面的两个函数功能都很清晰，没有什么需要解释的，只是分配内存用的是apr_palloc()和apr_pcalloc()两个函数从内存池中分配内存，这两个函数的区别是后者在分配了内存之后会先清零，这也就是那个clear参数的作用了。</p>
<p>往数组中压入元素：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">APR_DECLARE<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> apr_array_push<span style="color: #009900;">&#40;</span>apr_array_header_t <span style="color: #339933;">*</span>arr<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>arr<span style="color: #339933;">-&gt;</span>nelts <span style="color: #339933;">==</span> arr<span style="color: #339933;">-&gt;</span>nalloc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">int</span> new_size <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>arr<span style="color: #339933;">-&gt;</span>nalloc <span style="color: #339933;">&lt;=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> <span style="color: #0000dd;">1</span> <span style="color: #339933;">:</span> arr<span style="color: #339933;">-&gt;</span>nalloc <span style="color: #339933;">*</span> <span style="color: #0000dd;">2</span><span style="color: #339933;">;</span>
        <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>new_data<span style="color: #339933;">;</span>
&nbsp;
        new_data <span style="color: #339933;">=</span> apr_palloc<span style="color: #009900;">&#40;</span>arr<span style="color: #339933;">-&gt;</span>pool<span style="color: #339933;">,</span> arr<span style="color: #339933;">-&gt;</span>elt_size <span style="color: #339933;">*</span> new_size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        memcpy<span style="color: #009900;">&#40;</span>new_data<span style="color: #339933;">,</span> arr<span style="color: #339933;">-&gt;</span>elts<span style="color: #339933;">,</span> arr<span style="color: #339933;">-&gt;</span>nalloc <span style="color: #339933;">*</span> arr<span style="color: #339933;">-&gt;</span>elt_size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        memset<span style="color: #009900;">&#40;</span>new_data <span style="color: #339933;">+</span> arr<span style="color: #339933;">-&gt;</span>nalloc <span style="color: #339933;">*</span> arr<span style="color: #339933;">-&gt;</span>elt_size<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
               arr<span style="color: #339933;">-&gt;</span>elt_size <span style="color: #339933;">*</span> <span style="color: #009900;">&#40;</span>new_size <span style="color: #339933;">-</span> arr<span style="color: #339933;">-&gt;</span>nalloc<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        arr<span style="color: #339933;">-&gt;</span>elts <span style="color: #339933;">=</span> new_data<span style="color: #339933;">;</span>
        arr<span style="color: #339933;">-&gt;</span>nalloc <span style="color: #339933;">=</span> new_size<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #339933;">++</span>arr<span style="color: #339933;">-&gt;</span>nelts<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> arr<span style="color: #339933;">-&gt;</span>elts <span style="color: #339933;">+</span> <span style="color: #009900;">&#40;</span>arr<span style="color: #339933;">-&gt;</span>elt_size <span style="color: #339933;">*</span> <span style="color: #009900;">&#40;</span>arr<span style="color: #339933;">-&gt;</span>nelts <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>上面提到如果数组中活跃元素已经占满了所有的已分配的内存空间之后，便需要向内存池申请额外的内存才能便新元素得已插入，apache的做法也很简单，申请双陪的内存，将旧内存中的内容copy到新内存中的前半部分，后半部分清零，或不做处理（apr_array_push_noclear()函数）。然后就是增加活跃元素数，返回最新元素的内存地址。</p>
<p>还有一系列关于数据操作的函数，apr_array_pop()从数组中弹出元素，apr_array_cat()连接两个数组，apr_array_copy()拷贝一个数组，原理都很简单，不再一一赘述，详情可见srclib/arp/tables/apr_tables.c </p>
<p>接下来看apache里面另一个很重要的数据结构，一个类似于数据字典的table，但它允许有多个相同健的元素存在。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> apr_table_t <span style="color: #009900;">&#123;</span>
    <span style="color: #808080; font-style: italic;">/* This has to be first to promote backwards compatibility with
     * older modules which cast a apr_table_t * to an apr_array_header_t *...
     * they should use the apr_table_elts() function for most of the
     * cases they do this for.
     */</span>
    <span style="color: #808080; font-style: italic;">/** The underlying array for the table */</span>
    apr_array_header_t a<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/** Who created the array. */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>creator<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/* An index to speed up table lookups.  The way this works is:
     *   - Hash the key into the index:
     *     - index_first[TABLE_HASH(key)] is the offset within
     *       the table of the first entry with that key
     *     - index_last[TABLE_HASH(key)] is the offset within
     *       the table of the last entry with that key
     *   - If (and only if) there is no entry in the table whose
     *     key hashes to index element i, then the i'th bit
     *     of index_initialized will be zero.  (Check this before
     *     trying to use index_first[i] or index_last[i]!)
     */</span>
    apr_uint32_t index_initialized<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> index_first<span style="color: #009900;">&#91;</span>TABLE_HASH_SIZE<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> index_last<span style="color: #009900;">&#91;</span>TABLE_HASH_SIZE<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>数据表中的元素是存放在变量名为a的数组中的。creator是该数据表的创建者，通常是个字符串。最后的三个字段是比较有学问的字段。TABLE_HASH_SIZE宏的大小定义为32，index_initialized变量也是一个32位的常数，该变量用于对每个key的hash值进行一个映射，key的hash算法很简单：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define TABLE_HASH(key)  (TABLE_INDEX_MASK &amp; *(unsigned char *)(key))</span></pre></div></div>

<p>相当于把key的前8个字节转换成整形再与32求余，最后结果是一个小于32的整数。于是，当表中存在某个key时，假设key对应的hash值i，则index_initialized的第i位是被置位的，这样可以快速地检测某个key是否在表中存在。</p>
<p>至于index_first和index_last这两上数组中存储的是某个hash值所对应的元素在a数组中的索引范围（肯定是需要一个范围的，32个元素的hash表非常容易出现碰撞）。如果key对应的hash值为4，而index_first[4]=22，index_last[4]=26，则要查找的元素在a数组中第22个元素到第26个元素之间。至于index_last和index_first最面的值是怎么确定的接下来讨论。</p>
<p>需要提一下的是a数组中每一个元素都是一个apr_entry_t结构：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> apr_table_entry_t <span style="color: #009900;">&#123;</span>
    <span style="color: #808080; font-style: italic;">/** The key for the current table entry */</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>key<span style="color: #339933;">;</span>          <span style="color: #808080; font-style: italic;">/* maybe NULL in future;
                         * check when iterating thru table_elts
                         */</span>
    <span style="color: #808080; font-style: italic;">/** The value for the current table entry */</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>val<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/** A checksum for the key, for use by the apr_table internals */</span>
    apr_uint32_t key_checksum<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>我们看下是如何获取某个健值的：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">APR_DECLARE<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> apr_table_get<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> apr_table_t <span style="color: #339933;">*</span>t<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>key<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    apr_table_entry_t <span style="color: #339933;">*</span>next_elt<span style="color: #339933;">;</span>
    apr_table_entry_t <span style="color: #339933;">*</span>end_elt<span style="color: #339933;">;</span>
    apr_uint32_t checksum<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> hash<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>key <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    hash <span style="color: #339933;">=</span> TABLE_HASH<span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>TABLE_INDEX_IS_INITIALIZED<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">,</span> hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    COMPUTE_KEY_CHECKSUM<span style="color: #009900;">&#40;</span>key<span style="color: #339933;">,</span> checksum<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    next_elt <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>apr_table_entry_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">elts</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> t<span style="color: #339933;">-&gt;</span>index_first<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span><span style="color: #339933;">;;</span>
    end_elt <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>apr_table_entry_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">elts</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> t<span style="color: #339933;">-&gt;</span>index_last<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">;</span> next_elt <span style="color: #339933;">&lt;=</span> end_elt<span style="color: #339933;">;</span> next_elt<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>checksum <span style="color: #339933;">==</span> next_elt<span style="color: #339933;">-&gt;</span>key_checksum<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
            <span style="color: #339933;">!</span>strcasecmp<span style="color: #009900;">&#40;</span>next_elt<span style="color: #339933;">-&gt;</span>key<span style="color: #339933;">,</span> key<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	    <span style="color: #b1b100;">return</span> next_elt<span style="color: #339933;">-&gt;</span>val<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>程序遍历index_first[hash]和index_last[hash]区间内的链表，通过比对健值来获取要寻找的值。</p>
<p>接下来看设置某个健值的函数，稍微复杂一点：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">APR_DECLARE<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span> apr_table_set<span style="color: #009900;">&#40;</span>apr_table_t <span style="color: #339933;">*</span>t<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>key<span style="color: #339933;">,</span>
                                <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>val<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    apr_table_entry_t <span style="color: #339933;">*</span>next_elt<span style="color: #339933;">;</span>
    apr_table_entry_t <span style="color: #339933;">*</span>end_elt<span style="color: #339933;">;</span>
    apr_table_entry_t <span style="color: #339933;">*</span>table_end<span style="color: #339933;">;</span>
    apr_uint32_t checksum<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> hash<span style="color: #339933;">;</span>
&nbsp;
    COMPUTE_KEY_CHECKSUM<span style="color: #009900;">&#40;</span>key<span style="color: #339933;">,</span> checksum<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    hash <span style="color: #339933;">=</span> TABLE_HASH<span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>TABLE_INDEX_IS_INITIALIZED<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">,</span> hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        t<span style="color: #339933;">-&gt;</span>index_first<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">nelts</span><span style="color: #339933;">;</span>
        TABLE_SET_INDEX_INITIALIZED<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">,</span> hash<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">goto</span> add_new_elt<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    next_elt <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>apr_table_entry_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">elts</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> t<span style="color: #339933;">-&gt;</span>index_first<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span><span style="color: #339933;">;;</span>
    end_elt <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>apr_table_entry_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">elts</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> t<span style="color: #339933;">-&gt;</span>index_last<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    table_end <span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>apr_table_entry_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">elts</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">nelts</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">;</span> next_elt <span style="color: #339933;">&lt;=</span> end_elt<span style="color: #339933;">;</span> next_elt<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>checksum <span style="color: #339933;">==</span> next_elt<span style="color: #339933;">-&gt;</span>key_checksum<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
            <span style="color: #339933;">!</span>strcasecmp<span style="color: #009900;">&#40;</span>next_elt<span style="color: #339933;">-&gt;</span>key<span style="color: #339933;">,</span> key<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;">/* Found an existing entry with the same key, so overwrite it */</span>
&nbsp;
            <span style="color: #993333;">int</span> must_reindex <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            apr_table_entry_t <span style="color: #339933;">*</span>dst_elt <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
&nbsp;
            next_elt<span style="color: #339933;">-&gt;</span>val <span style="color: #339933;">=</span> apr_pstrdup<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">pool</span><span style="color: #339933;">,</span> val<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;">/* Remove any other instances of this key */</span>
            <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>next_elt<span style="color: #339933;">++;</span> next_elt <span style="color: #339933;">&lt;=</span> end_elt<span style="color: #339933;">;</span> next_elt<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>checksum <span style="color: #339933;">==</span> next_elt<span style="color: #339933;">-&gt;</span>key_checksum<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
                    <span style="color: #339933;">!</span>strcasecmp<span style="color: #009900;">&#40;</span>next_elt<span style="color: #339933;">-&gt;</span>key<span style="color: #339933;">,</span> key<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                    t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">nelts</span><span style="color: #339933;">--;</span>
                    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>dst_elt<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                        dst_elt <span style="color: #339933;">=</span> next_elt<span style="color: #339933;">;</span>
                    <span style="color: #009900;">&#125;</span>
                <span style="color: #009900;">&#125;</span>
                <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>dst_elt<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #339933;">*</span>dst_elt<span style="color: #339933;">++</span> <span style="color: #339933;">=</span> <span style="color: #339933;">*</span>next_elt<span style="color: #339933;">;</span>
                    must_reindex <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;">/* If we've removed anything, shift over the remainder
             * of the table (note that the previous loop didn't
             * run to the end of the table, just to the last match
             * for the index)
             */</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>dst_elt<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">;</span> next_elt <span style="color: #339933;">&lt;</span> table_end<span style="color: #339933;">;</span> next_elt<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #339933;">*</span>dst_elt<span style="color: #339933;">++</span> <span style="color: #339933;">=</span> <span style="color: #339933;">*</span>next_elt<span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
                must_reindex <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>must_reindex<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                table_reindex<span style="color: #009900;">&#40;</span>t<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
add_new_elt<span style="color: #339933;">:</span>
    t<span style="color: #339933;">-&gt;</span>index_last<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">nelts</span><span style="color: #339933;">;</span>
    next_elt <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>apr_table_entry_t <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> table_push<span style="color: #009900;">&#40;</span>t<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    next_elt<span style="color: #339933;">-&gt;</span>key <span style="color: #339933;">=</span> apr_pstrdup<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">pool</span><span style="color: #339933;">,</span> key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    next_elt<span style="color: #339933;">-&gt;</span>val <span style="color: #339933;">=</span> apr_pstrdup<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">-&gt;</span>a.<span style="color: #202020;">pool</span><span style="color: #339933;">,</span> val<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    next_elt<span style="color: #339933;">-&gt;</span>key_checksum <span style="color: #339933;">=</span> checksum<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>TABLE_INDEX_IS_INITIALIZED(t,i)这个宏是检测index_initialized这个变量中的第i位是否被置位。如果没有被置位，则表示该key对应的节点已经在a数组中存在。如果存在则index_first[hash]和index_last[hash]都是存在的，为什么呢？先假设key对应的hash位未被置位，则新插入该key时，我们会把这个key对应的apr_entry_t结构放到a数组的最后，它的索引值便是a.nelts，这时候我们也把index_first[hash]和index_last[hash]的值都设置为a.nelts。当插入另一个key时，它的hash值可能与上一个key出现碰撞，此时该hash值对应的index_first[hash]的索引值不变，将index_last[hash]设置为新索引值，这样这两个key所对应的apr_entry_t结构便都存在于a[index_first[hash]]和a[index_last[a]]之间，我们只需要遍历找到该结构并将其val属性设置为我们要设置的值即可。同时由于该表允许多个相同健的存在，我们在设置健值之后也需要从表中移除其它的具有相同健的对象。与table有关的函数也有很多，大原理都比较简单，也不再赘述了。</p>
<p>本文纯属个人见解，如有谬误烦请指出。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/apache-tables-structure-sourcecode-intro.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>浅析Linux Kernel 哈希路由表实现(二)</title>
		<link>http://basiccoder.com/intro-linux-kernel-hash-rt-2.html</link>
		<comments>http://basiccoder.com/intro-linux-kernel-hash-rt-2.html#comments</comments>
		<pubDate>Tue, 15 Mar 2011 08:00:40 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=845</guid>
		<description><![CDATA[在向外发送数据包的时候，首先需要查询路由表来确定路由包的路由，主要由ip_route_output_key()函数来完成，该函数又调用了ip_route_output_flow()，而这个函数最终又调用了__ip_route_output_key()这个函数来进行路由的查询，下面主要来看一下这个函数：

<pre lang="C">
int __ip_route_output_key(struct net *net, struct rtable **rp,
			  const struct flowi *flp)
{
	unsigned int hash;
	int res;
	struct rtable *rth;

	if (!rt_caching(net))
		goto slow_output;

	hash = rt_hash(flp->fl4_dst, flp->fl4_src, flp->oif, rt_genid(net));

	rcu_read_lock_bh();
	for (rth = rcu_dereference_bh(rt_hash_table[hash].chain); rth;
		rth = rcu_dereference_bh(rth->dst.rt_next)) {
		if (rth->fl.fl4_dst == flp->fl4_dst &#038;&
		    rth->fl.fl4_src == flp->fl4_src &#038;&
		    rth->fl.iif == 0 &#038;&
		    rth->fl.oif == flp->oif &#038;&
		    rth->fl.mark == flp->mark &#038;&
		    !((rth->fl.fl4_tos ^ flp->fl4_tos) &#038;
			    (IPTOS_RT_MASK &#124; RTO_ONLINK)) &#038;&
		    net_eq(dev_net(rth->dst.dev), net) &#038;&
		    !rt_is_expired(rth)) {
			dst_use(&#038;rth->dst, jiffies);
			RT_CACHE_STAT_INC(out_hit);
			rcu_read_unlock_bh();
			*rp = rth;
			return 0;
		}
		RT_CACHE_STAT_INC(out_hlist_search);
	}
	rcu_read_unlock_bh();

slow_output:
	rcu_read_lock();
	res = ip_route_output_slow(net, rp, flp);
	rcu_read_unlock();
	return res;
}
</pre... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>在向外发送数据包的时候，首先需要查询路由表来确定路由包的路由，主要由ip_route_output_key()函数来完成，该函数又调用了ip_route_output_flow()，而这个函数最终又调用了__ip_route_output_key()这个函数来进行路由的查询，下面主要来看一下这个函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> __ip_route_output_key<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> rtable <span style="color: #339933;">**</span>rp<span style="color: #339933;">,</span>
			  <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>flp<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> hash<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> res<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> rtable <span style="color: #339933;">*</span>rth<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rt_caching<span style="color: #009900;">&#40;</span>net<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> slow_output<span style="color: #339933;">;</span>
&nbsp;
	hash <span style="color: #339933;">=</span> rt_hash<span style="color: #009900;">&#40;</span>flp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #339933;">,</span> flp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #339933;">,</span> flp<span style="color: #339933;">-&gt;</span>oif<span style="color: #339933;">,</span> rt_genid<span style="color: #009900;">&#40;</span>net<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	rcu_read_lock_bh<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>rth <span style="color: #339933;">=</span> rcu_dereference_bh<span style="color: #009900;">&#40;</span>rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> rth<span style="color: #339933;">;</span>
		rth <span style="color: #339933;">=</span> rcu_dereference_bh<span style="color: #009900;">&#40;</span>rth<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>rth<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">fl4_dst</span> <span style="color: #339933;">==</span> flp<span style="color: #339933;">-&gt;</span>fl4_dst <span style="color: #339933;">&amp;&amp;</span>
		    rth<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">==</span> flp<span style="color: #339933;">-&gt;</span>fl4_src <span style="color: #339933;">&amp;&amp;</span>
		    rth<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">iif</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">&amp;&amp;</span>
		    rth<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">oif</span> <span style="color: #339933;">==</span> flp<span style="color: #339933;">-&gt;</span>oif <span style="color: #339933;">&amp;&amp;</span>
		    rth<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">mark</span> <span style="color: #339933;">==</span> flp<span style="color: #339933;">-&gt;</span>mark <span style="color: #339933;">&amp;&amp;</span>
		    <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>rth<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">fl4_tos</span> <span style="color: #339933;">^</span> flp<span style="color: #339933;">-&gt;</span>fl4_tos<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>
			    <span style="color: #009900;">&#40;</span>IPTOS_RT_MASK <span style="color: #339933;">|</span> RTO_ONLINK<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
		    net_eq<span style="color: #009900;">&#40;</span>dev_net<span style="color: #009900;">&#40;</span>rth<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">dev</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> net<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
		    <span style="color: #339933;">!</span>rt_is_expired<span style="color: #009900;">&#40;</span>rth<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			dst_use<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">-&gt;</span>dst<span style="color: #339933;">,</span> jiffies<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			RT_CACHE_STAT_INC<span style="color: #009900;">&#40;</span>out_hit<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			rcu_read_unlock_bh<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #339933;">*</span>rp <span style="color: #339933;">=</span> rth<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		RT_CACHE_STAT_INC<span style="color: #009900;">&#40;</span>out_hlist_search<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	rcu_read_unlock_bh<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
slow_output<span style="color: #339933;">:</span>
	rcu_read_lock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	res <span style="color: #339933;">=</span> ip_route_output_slow<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> rp<span style="color: #339933;">,</span> flp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> res<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Linux的路由表中的常用路由是存储在路由缓存中的，该路由缓存即是类型为struct rt_hash_bucket的全局列表rt_hash_table，该缓存列表在ip_rt_init()中初始化。</p>
<p>struct flowi结构中包含了查询路由表所需要的请求信息，是一个搜索健值。由代码可看出，首先在路由缓存列表rt_hash_table中查询精确匹配的未过期的路由表项struct rtable，(注，因为是出口路由，所以入口接口号是0)，若找到后增加路由表项的引用计数和后即刻返回。若未找到匹配的路由表项，则继续在路由表中查找匹配的路由表项，路由表中的查询速度会比路由缓存中慢，所以ip_route_output_slow()函数的命名就不难理解了，主动的路由解析工作都是在这个函数里面进行的，在看它的定义之前先看下服务类型和路由范围的相关 定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define IPTOS_TOS_MASK		0x1E</span>
<span style="color: #339933;">#define IPTOS_TOS(tos)		((tos)&amp;IPTOS_TOS_MASK)</span>
<span style="color: #339933;">#define	IPTOS_LOWDELAY		0x10	/* 最小延时 */</span>
<span style="color: #339933;">#define	IPTOS_THROUGHPUT	0x08	/* 最大吞吐量 */</span>
<span style="color: #339933;">#define	IPTOS_RELIABILITY	0x04	/* 最高可靠性 */</span>
<span style="color: #339933;">#define	IPTOS_MINCOST		0x02	/* 最小消费 */</span>
<span style="color: #339933;">#define RTO_ONLINK          0x01</span></pre></div></div>

<p>由掩码可知，服务类型实际上用了从第2位到第5位共四位的数据，表示四种服务类型，而最低位的RTO_ONLINK如果置位，则scope为RT_SCOPE_LINK，或没有，则scope为RT_SCOPE_UNIVERSE，接下来看看scope的相关定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">enum</span> rt_scope_t <span style="color: #009900;">&#123;</span>
	RT_SCOPE_UNIVERSE<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>		<span style="color: #808080; font-style: italic;">/* 表示在空间中的任何位置 */</span>
<span style="color: #808080; font-style: italic;">/* User defined values  */</span>
	RT_SCOPE_SITE<span style="color: #339933;">=</span><span style="color: #0000dd;">200</span><span style="color: #339933;">,</span>
	RT_SCOPE_LINK<span style="color: #339933;">=</span><span style="color: #0000dd;">253</span><span style="color: #339933;">,</span>			<span style="color: #808080; font-style: italic;">/* 与本地直接相连的地址 */</span>
	RT_SCOPE_HOST<span style="color: #339933;">=</span><span style="color: #0000dd;">254</span><span style="color: #339933;">,</span>			<span style="color: #808080; font-style: italic;">/* 本地地址 */</span>
	RT_SCOPE_NOWHERE<span style="color: #339933;">=</span><span style="color: #0000dd;">255</span>		<span style="color: #808080; font-style: italic;">/* 不可达的地址 */</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>其中值越大所表示的范围便越精确，实际上这也不是什么范围的意思，只不过是到目的地址的某种距离的表示。OK，接下来看ip_route_output_slow()函数的定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> ip_route_output_slow<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> rtable <span style="color: #339933;">**</span>rp<span style="color: #339933;">,</span>
				<span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>oldflp<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	u32 tos	<span style="color: #339933;">=</span> RT_FL_TOS<span style="color: #009900;">&#40;</span>oldflp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> flowi fl <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span> .<span style="color: #202020;">nl_u</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span> .<span style="color: #202020;">ip4_u</span> <span style="color: #339933;">=</span>
				      <span style="color: #009900;">&#123;</span> .<span style="color: #202020;">daddr</span> <span style="color: #339933;">=</span> oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #339933;">,</span>
					.<span style="color: #202020;">saddr</span> <span style="color: #339933;">=</span> oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #339933;">,</span>
					.<span style="color: #202020;">tos</span> <span style="color: #339933;">=</span> tos <span style="color: #339933;">&amp;</span> IPTOS_RT_MASK<span style="color: #339933;">,</span>
					.<span style="color: #202020;">scope</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>tos <span style="color: #339933;">&amp;</span> RTO_ONLINK<span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span>
						  RT_SCOPE_LINK <span style="color: #339933;">:</span>
						  RT_SCOPE_UNIVERSE<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
				      <span style="color: #009900;">&#125;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
			    .<span style="color: #202020;">mark</span> <span style="color: #339933;">=</span> oldflp<span style="color: #339933;">-&gt;</span>mark<span style="color: #339933;">,</span>
			    .<span style="color: #202020;">iif</span> <span style="color: #339933;">=</span> net<span style="color: #339933;">-&gt;</span>loopback_dev<span style="color: #339933;">-&gt;</span>ifindex<span style="color: #339933;">,</span>
			    .<span style="color: #202020;">oif</span> <span style="color: #339933;">=</span> oldflp<span style="color: #339933;">-&gt;</span>oif <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_result res<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> flags <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> net_device <span style="color: #339933;">*</span>dev_out <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
&nbsp;
&nbsp;
	res.<span style="color: #202020;">fi</span>		<span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
<span style="color: #339933;">#ifdef CONFIG_IP_MULTIPLE_TABLES</span>
	res.<span style="color: #202020;">r</span>		<span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #808080; font-style: italic;">/* 若源地址为组播地址，受限广播地址(255.255.255.255)或0地址，
		均不合法，即刻返回 */</span>
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ipv4_is_multicast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
		    ipv4_is_lbcast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
		    ipv4_is_zeronet<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>oif <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">&amp;&amp;</span>
		    <span style="color: #009900;">&#40;</span>ipv4_is_multicast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
		     ipv4_is_lbcast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #808080; font-style: italic;">/* 等价于inet_addr_type(saddr) == RTN_LOCAL, 
			__ip_dev_find()函数实际是搜索RT_TABLE_LOCAL
			路由表中的路由表项，如果未找到对应设备则返回，因为
			Linux不允许环回接口发组播或受限广播 */</span>
			dev_out <span style="color: #339933;">=</span> __ip_dev_find<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #339933;">,</span> <span style="color: #000000; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>dev_out <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
				<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #808080; font-style: italic;">/* 给外面接口赋值后转去创建路由缓存 */</span>
			fl.<span style="color: #202020;">oif</span> <span style="color: #339933;">=</span> dev_out<span style="color: #339933;">-&gt;</span>ifindex<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">goto</span> make_route<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>flags <span style="color: #339933;">&amp;</span> FLOWI_FLAG_ANYSRC<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #808080; font-style: italic;">/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>__ip_dev_find<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #339933;">,</span> <span style="color: #000000; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
				<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>oif<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		dev_out <span style="color: #339933;">=</span> dev_get_by_index_rcu<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">-&gt;</span>oif<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENODEV<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>dev_out <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #808080; font-style: italic;">/* 如果外出接口示启用或外出接口对应的IPv4数据不存在，则返回网络不可达 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>dev_out<span style="color: #339933;">-&gt;</span>flags <span style="color: #339933;">&amp;</span> IFF_UP<span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span> <span style="color: #339933;">!</span>__in_dev_get_rcu<span style="color: #009900;">&#40;</span>dev_out<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENETUNREACH<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #808080; font-style: italic;">/* 若是本地组播地址或受限广播地址则直接转去创建路由缓存 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ipv4_is_local_multicast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
		    ipv4_is_lbcast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fl.<span style="color: #202020;">fl4_src</span><span style="color: #009900;">&#41;</span>
				fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> inet_select_addr<span style="color: #009900;">&#40;</span>dev_out<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
							      RT_SCOPE_LINK<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">goto</span> make_route<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #808080; font-style: italic;">/* 若未指定源地址，则根据目地地址类型创建选择一个源地址 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fl.<span style="color: #202020;">fl4_src</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ipv4_is_multicast<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
				fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> inet_select_addr<span style="color: #009900;">&#40;</span>dev_out<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
							      fl.<span style="color: #202020;">fl4_scope</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #009900;">&#41;</span>
				fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> inet_select_addr<span style="color: #009900;">&#40;</span>dev_out<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
							      RT_SCOPE_HOST<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 如果目的地址不存在，则令目的地址等于源地址，若都不存在，则使用环回接口，
		路由类型为本地路由，转而创建路由缓存 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fl.<span style="color: #202020;">fl4_dst</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		fl.<span style="color: #202020;">fl4_dst</span> <span style="color: #339933;">=</span> fl.<span style="color: #202020;">fl4_src</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fl.<span style="color: #202020;">fl4_dst</span><span style="color: #009900;">&#41;</span>
			fl.<span style="color: #202020;">fl4_dst</span> <span style="color: #339933;">=</span> fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> htonl<span style="color: #009900;">&#40;</span>INADDR_LOOPBACK<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		dev_out <span style="color: #339933;">=</span> net<span style="color: #339933;">-&gt;</span>loopback_dev<span style="color: #339933;">;</span>
		fl.<span style="color: #202020;">oif</span> <span style="color: #339933;">=</span> net<span style="color: #339933;">-&gt;</span>loopback_dev<span style="color: #339933;">-&gt;</span>ifindex<span style="color: #339933;">;</span>
		res.<span style="color: #202020;">type</span> <span style="color: #339933;">=</span> RTN_LOCAL<span style="color: #339933;">;</span>
		flags <span style="color: #339933;">|=</span> RTCF_LOCAL<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">goto</span> make_route<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/*
OK, 走到这里先总结一下不需要查询路由表即可直接创建路由缓存的情况：
1. 指定了源地址，未指定外出接口，目的地址为组播地址或受限广播地址
2. 指定了外出接口，并且目的地址为本地组播地址或受限广播地址
3. 未指定目的地址。
&nbsp;
若以上三种情况均未满足，则需要进行路由表查询。
*/</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fib_lookup<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>fl<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>res<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		res.<span style="color: #202020;">fi</span> <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>oif<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #808080; font-style: italic;">/* 程序走到这里说明查询路由表失败，未找到对应的路由表项，
			但却指定了外出接口，这时候即便没有路由也是可以发送数据包的。
			当然，如果未指定外出接口，则只能返回网络不可达了。 */</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
				fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> inet_select_addr<span style="color: #009900;">&#40;</span>dev_out<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
							      RT_SCOPE_LINK<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			res.<span style="color: #202020;">type</span> <span style="color: #339933;">=</span> RTN_UNICAST<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">goto</span> make_route<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENETUNREACH<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 若为本地路由，则使用环回接口 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>res.<span style="color: #202020;">type</span> <span style="color: #339933;">==</span> RTN_LOCAL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fl.<span style="color: #202020;">fl4_src</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>res.<span style="color: #202020;">fi</span><span style="color: #339933;">-&gt;</span>fib_prefsrc<span style="color: #009900;">&#41;</span>
				fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> res.<span style="color: #202020;">fi</span><span style="color: #339933;">-&gt;</span>fib_prefsrc<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">else</span>
				fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> fl.<span style="color: #202020;">fl4_dst</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		dev_out <span style="color: #339933;">=</span> net<span style="color: #339933;">-&gt;</span>loopback_dev<span style="color: #339933;">;</span>
		fl.<span style="color: #202020;">oif</span> <span style="color: #339933;">=</span> dev_out<span style="color: #339933;">-&gt;</span>ifindex<span style="color: #339933;">;</span>
		res.<span style="color: #202020;">fi</span> <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
		flags <span style="color: #339933;">|=</span> RTCF_LOCAL<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">goto</span> make_route<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">/*  使用默认路由需要三个条件：
1. 若前缀为0，也即掩码长度为0，默认路由匹配所有的目的地址。
2. 路由类型为RTN_UNICAST，我们知道本地地址，组播地址和广播地址
3. 未指定出口设备，上面我们提到即便是没有路由的情况下提供了出口设备，
数据包也是可以发送的。
	这时候路由是默认路由，因此我们需要选择默认网关 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>res.<span style="color: #202020;">prefixlen</span> <span style="color: #339933;">&amp;&amp;</span> res.<span style="color: #202020;">type</span> <span style="color: #339933;">==</span> RTN_UNICAST <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span>fl.<span style="color: #202020;">oif</span><span style="color: #009900;">&#41;</span>
		fib_select_default<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>fl<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fl.<span style="color: #202020;">fl4_src</span><span style="color: #009900;">&#41;</span>
		fl.<span style="color: #202020;">fl4_src</span> <span style="color: #339933;">=</span> FIB_RES_PREFSRC<span style="color: #009900;">&#40;</span>res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	dev_out <span style="color: #339933;">=</span> FIB_RES_DEV<span style="color: #009900;">&#40;</span>res<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	fl.<span style="color: #202020;">oif</span> <span style="color: #339933;">=</span> dev_out<span style="color: #339933;">-&gt;</span>ifindex<span style="color: #339933;">;</span>
&nbsp;
make_route<span style="color: #339933;">:</span>
	<span style="color: #808080; font-style: italic;">/* 创建一条路由缓存 */</span>
	err <span style="color: #339933;">=</span> ip_mkroute_output<span style="color: #009900;">&#40;</span>rp<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>res<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>fl<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">,</span> dev_out<span style="color: #339933;">,</span> flags<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
out<span style="color: #339933;">:</span>	<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>接下来看下创建路由缓存的函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> ip_mkroute_output<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> rtable <span style="color: #339933;">**</span>rp<span style="color: #339933;">,</span>
			     <span style="color: #993333;">struct</span> fib_result <span style="color: #339933;">*</span>res<span style="color: #339933;">,</span>
			     <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>fl<span style="color: #339933;">,</span>
			     <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>oldflp<span style="color: #339933;">,</span>
			     <span style="color: #993333;">struct</span> net_device <span style="color: #339933;">*</span>dev_out<span style="color: #339933;">,</span>
			     <span style="color: #993333;">unsigned</span> flags<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> rtable <span style="color: #339933;">*</span>rth <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> err <span style="color: #339933;">=</span> __mkroute_output<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">,</span> res<span style="color: #339933;">,</span> fl<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">,</span> dev_out<span style="color: #339933;">,</span> flags<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> hash<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		hash <span style="color: #339933;">=</span> rt_hash<span style="color: #009900;">&#40;</span>oldflp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">-&gt;</span>fl4_src<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">-&gt;</span>oif<span style="color: #339933;">,</span>
			       rt_genid<span style="color: #009900;">&#40;</span>dev_net<span style="color: #009900;">&#40;</span>dev_out<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		err <span style="color: #339933;">=</span> rt_intern_hash<span style="color: #009900;">&#40;</span>hash<span style="color: #339933;">,</span> rth<span style="color: #339933;">,</span> rp<span style="color: #339933;">,</span> NULL<span style="color: #339933;">,</span> oldflp<span style="color: #339933;">-&gt;</span>oif<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>该函数首先调用__mkroute_output()函数生成一条路由缓存，然后再调用rt_intern_hash()函数写入到缓存列表中去。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">static</span> <span style="color: #993333;">int</span> rt_intern_hash<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> hash<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> rtable <span style="color: #339933;">*</span>rt<span style="color: #339933;">,</span>
			  <span style="color: #993333;">struct</span> rtable <span style="color: #339933;">**</span>rp<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> sk_buff <span style="color: #339933;">*</span>skb<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> ifindex<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> rtable	<span style="color: #339933;">*</span>rth<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>cand<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> rtable __rcu <span style="color: #339933;">**</span>rthp<span style="color: #339933;">,</span> <span style="color: #339933;">**</span>candp<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span>	now<span style="color: #339933;">;</span>
	u32 		min_score<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span>		chain_length<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> attempts <span style="color: #339933;">=</span> <span style="color: #339933;">!</span>in_softirq<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
restart<span style="color: #339933;">:</span>
	chain_length <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	min_score <span style="color: #339933;">=</span> ~<span style="color: #009900;">&#40;</span>u32<span style="color: #009900;">&#41;</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	cand <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	candp <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	now <span style="color: #339933;">=</span> jiffies<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rt_caching<span style="color: #009900;">&#40;</span>dev_net<span style="color: #009900;">&#40;</span>rt<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">dev</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #808080; font-style: italic;">/* 如果路由未进行缓存，那么把路由的DST标示设为DST_NOCACHE，调用者
	便会知道这条路由未进行缓存，使用完成之后可以根据该标志对路由进
	行释放。如果在这里把路由给丢掉的话，那么当没有进行路由缓存的情况
	下调用都就没办不法解析路由信息了。 */</span>
		rt<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">flags</span> <span style="color: #339933;">|=</span> DST_NOCACHE<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>rt<span style="color: #339933;">-&gt;</span>rt_type <span style="color: #339933;">==</span> RTN_UNICAST <span style="color: #339933;">||</span> rt<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">iif</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #993333;">int</span> err <span style="color: #339933;">=</span> arp_bind_neighbour<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rt<span style="color: #339933;">-&gt;</span>dst<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>net_ratelimit<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
					printk<span style="color: #009900;">&#40;</span>KERN_WARNING
					    <span style="color: #ff0000;">&quot;Neighbour table failure &amp; not caching routes.<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				ip_rt_put<span style="color: #009900;">&#40;</span>rt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #b1b100;">goto</span> skip_hashing<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
&nbsp;
	rthp <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #339933;">;</span>
&nbsp;
	spin_lock_bh<span style="color: #009900;">&#40;</span>rt_hash_lock_addr<span style="color: #009900;">&#40;</span>hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 开始遍历哈希链表 */</span>
	<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>rth <span style="color: #339933;">=</span> rcu_dereference_protected<span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>rthp<span style="color: #339933;">,</span>
			lockdep_is_held<span style="color: #009900;">&#40;</span>rt_hash_lock_addr<span style="color: #009900;">&#40;</span>hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #808080; font-style: italic;">/* 如果路由已过期，则直接从列表中删除并释放内存空间 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>rt_is_expired<span style="color: #009900;">&#40;</span>rth<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #339933;">*</span>rthp <span style="color: #339933;">=</span> rth<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span><span style="color: #339933;">;</span>
			rt_free<span style="color: #009900;">&#40;</span>rth<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #808080; font-style: italic;">/* 如果未过期，并在列表中找到了匹配的路由，则将该路由缓存项拿到
			链表的最新端，并增加引用计数，释放新建待插入的缓存项内存。 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>compare_keys<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">-&gt;</span>fl<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>rt<span style="color: #339933;">-&gt;</span>fl<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> compare_netns<span style="color: #009900;">&#40;</span>rth<span style="color: #339933;">,</span> rt<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #339933;">*</span>rthp <span style="color: #339933;">=</span> rth<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span><span style="color: #339933;">;</span>
			rcu_assign_pointer<span style="color: #009900;">&#40;</span>rth<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span><span style="color: #339933;">,</span>
					   rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			rcu_assign_pointer<span style="color: #009900;">&#40;</span>rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #339933;">,</span> rth<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			dst_use<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">-&gt;</span>dst<span style="color: #339933;">,</span> now<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			spin_unlock_bh<span style="color: #009900;">&#40;</span>rt_hash_lock_addr<span style="color: #009900;">&#40;</span>hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			rt_drop<span style="color: #009900;">&#40;</span>rt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>rp<span style="color: #009900;">&#41;</span>
				<span style="color: #339933;">*</span>rp <span style="color: #339933;">=</span> rth<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">else</span>
				skb_dst_set<span style="color: #009900;">&#40;</span>skb<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">-&gt;</span>dst<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>atomic_read<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">-&gt;</span>dst.__refcnt<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			u32 score <span style="color: #339933;">=</span> rt_score<span style="color: #009900;">&#40;</span>rth<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>score <span style="color: #339933;">&lt;=</span> min_score<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				cand <span style="color: #339933;">=</span> rth<span style="color: #339933;">;</span>
				candp <span style="color: #339933;">=</span> rthp<span style="color: #339933;">;</span>
				min_score <span style="color: #339933;">=</span> score<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		chain_length<span style="color: #339933;">++;</span>
&nbsp;
		rthp <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>rth<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cand<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #808080; font-style: italic;">/* ip_rt_gc_elasticity used to be average length of chain
		 * length, when exceeded gc becomes really aggressive.
		 *
		 * The second limit is less certain. At the moment it allows
		 * only 2 entries per bucket. We will see.
		 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>chain_length <span style="color: #339933;">&gt;</span> ip_rt_gc_elasticity<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #339933;">*</span>candp <span style="color: #339933;">=</span> cand<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span><span style="color: #339933;">;</span>
			rt_free<span style="color: #009900;">&#40;</span>cand<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
<span style="color: #808080; font-style: italic;">/* 如果某个哈希槽上的链表长度大于所能接受的链表的最大长度，
	则说明哈希碰撞太严重，需要重构哈希表，这个长度目前定义为20。
	如果需要重构则增加重构计数current_rt_cache_rebuild_count的值，
	rt_caching()函数就是简单地判断该值是否超过最大值来断定缓存是否
	正在进行的，最大值为4。 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>chain_length <span style="color: #339933;">&gt;</span> rt_chain_length_max <span style="color: #339933;">&amp;&amp;</span>
		    slow_chain_length<span style="color: #009900;">&#40;</span>rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> rt_chain_length_max<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net <span style="color: #339933;">=</span> dev_net<span style="color: #009900;">&#40;</span>rt<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">dev</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #993333;">int</span> num <span style="color: #339933;">=</span> <span style="color: #339933;">++</span>net<span style="color: #339933;">-&gt;</span>ipv4.<span style="color: #202020;">current_rt_cache_rebuild_count</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rt_caching<span style="color: #009900;">&#40;</span>net<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				printk<span style="color: #009900;">&#40;</span>KERN_WARNING <span style="color: #ff0000;">&quot;%s: %d rebuilds is over limit, route caching disabled<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span>
					rt<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">dev</span><span style="color: #339933;">-&gt;</span>name<span style="color: #339933;">,</span> num<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			<span style="color: #808080; font-style: italic;">/* 重建哈希列表，然后重新开始此函数 */</span>
			rt_emergency_hash_rebuild<span style="color: #009900;">&#40;</span>net<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			spin_unlock_bh<span style="color: #009900;">&#40;</span>rt_hash_lock_addr<span style="color: #009900;">&#40;</span>hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			hash <span style="color: #339933;">=</span> rt_hash<span style="color: #009900;">&#40;</span>rt<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">fl4_dst</span><span style="color: #339933;">,</span> rt<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">fl4_src</span><span style="color: #339933;">,</span>
					ifindex<span style="color: #339933;">,</span> rt_genid<span style="color: #009900;">&#40;</span>net<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">goto</span> restart<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 当路由为单播路由或者为外出路由(iif为0的情况即为外出路由)
	则需要把路由绑定到arp */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>rt<span style="color: #339933;">-&gt;</span>rt_type <span style="color: #339933;">==</span> RTN_UNICAST <span style="color: #339933;">||</span> rt<span style="color: #339933;">-&gt;</span>fl.<span style="color: #202020;">iif</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">int</span> err <span style="color: #339933;">=</span> arp_bind_neighbour<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>rt<span style="color: #339933;">-&gt;</span>dst<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			spin_unlock_bh<span style="color: #009900;">&#40;</span>rt_hash_lock_addr<span style="color: #009900;">&#40;</span>hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err <span style="color: #339933;">!=</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				rt_drop<span style="color: #009900;">&#40;</span>rt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
&nbsp;
			<span style="color: #808080; font-style: italic;">/* Neighbour tables are full and nothing
			   can be released. Try to shrink route cache,
			   it is most likely it holds some neighbour records.
			 */</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>attempts<span style="color: #339933;">--</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #993333;">int</span> saved_elasticity <span style="color: #339933;">=</span> ip_rt_gc_elasticity<span style="color: #339933;">;</span>
				<span style="color: #993333;">int</span> saved_int <span style="color: #339933;">=</span> ip_rt_gc_min_interval<span style="color: #339933;">;</span>
				ip_rt_gc_elasticity	<span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
				ip_rt_gc_min_interval	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
				<span style="color: #808080; font-style: italic;">/* 路由表进行垃圾回收，这个以后再写 */</span>
				rt_garbage_collect<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ipv4_dst_ops<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				ip_rt_gc_min_interval	<span style="color: #339933;">=</span> saved_int<span style="color: #339933;">;</span>
				ip_rt_gc_elasticity	<span style="color: #339933;">=</span> saved_elasticity<span style="color: #339933;">;</span>
				<span style="color: #b1b100;">goto</span> restart<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
&nbsp;
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>net_ratelimit<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
				printk<span style="color: #009900;">&#40;</span>KERN_WARNING <span style="color: #ff0000;">&quot;ipv4: Neighbour table overflow.<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			rt_drop<span style="color: #009900;">&#40;</span>rt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 将该表项放至哈希链表的头部 */</span>
	rt<span style="color: #339933;">-&gt;</span>dst.<span style="color: #202020;">rt_next</span> <span style="color: #339933;">=</span> rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/*
	 * Since lookup is lockfree, we must make sure
	 * previous writes to rt are comitted to memory
	 * before making rt visible to other CPUS.
	 */</span>
	rcu_assign_pointer<span style="color: #009900;">&#40;</span>rt_hash_table<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">chain</span><span style="color: #339933;">,</span> rt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	spin_unlock_bh<span style="color: #009900;">&#40;</span>rt_hash_lock_addr<span style="color: #009900;">&#40;</span>hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
skip_hashing<span style="color: #339933;">:</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>rp<span style="color: #009900;">&#41;</span>
		<span style="color: #339933;">*</span>rp <span style="color: #339933;">=</span> rt<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">else</span>
		skb_dst_set<span style="color: #009900;">&#40;</span>skb<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>rt<span style="color: #339933;">-&gt;</span>dst<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>简单注释了一下几个比较重要的函数，求大牛批评指正。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/intro-linux-kernel-hash-rt-2.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>浅析Linux Kernel 哈希路由表实现(一)</title>
		<link>http://basiccoder.com/intro-linux-kernel-hash-rt-1.html</link>
		<comments>http://basiccoder.com/intro-linux-kernel-hash-rt-1.html#comments</comments>
		<pubDate>Sun, 13 Mar 2011 15:50:03 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=829</guid>
		<description><![CDATA[<strong>1. 路由表</strong>

目前Linux内核中支持两种路由表，一种是Hash路由表，另一种是Trie路由表，Trie算法查找效率很高，但它也因极其消毫内存资源而闻名，因此一般用在大型机上，否则在路由项过多时很可能造成内存的耗尽。在一般的机器上最好还是使用Hash路由表，之前有争论说Linux使用Hash路由表相比于二叉树效率太低，不适合商用，其实只要设计良好的哈希算法，尽量减少哈希碰撞，Hash路由表的查找效率也是很高的，在最好的情况下算法复杂算可以达到O(1)，当然，最差也不过是O(n)，我们有理由相信Linux中存在各种优秀的哈希算法，这些都是值得我们学习的。

Linux内核中IPv4路由表采用了分层的结构，第一层是是fib_table，表示路由表，最多可以支持256张路由表，用户可以根据策略路由的需求来创建自己的路由表，系统默认的两张路由表为RT_TABLE_LOCAL和RT_TABLE_MAIN。下面是系统保留的路由表标识符：

<pre lang="C">
enum rt_class_t {
	RT_TABLE_UNSPEC=0,
/* User defined values */
	RT_TABLE_COMPAT=252,
	RT_TABLE_DEFAULT=253,
	RT_TABLE_MAIN=254,
	RT_TABLE_LOCAL=255,
	RT_TABLE_MAX=0xFFFFFFFF
};
</pre>
下面是路由表的定义... ]]></description>
			<content:encoded><![CDATA[<p><strong>1. 路由表</strong></p>
<p>目前Linux内核中支持两种路由表，一种是Hash路由表，另一种是Trie路由表，Trie算法查找效率很高，但它也因极其消毫内存资源而闻名，因此一般用在大型机上，否则在路由项过多时很可能造成内存的耗尽。在一般的机器上最好还是使用Hash路由表，之前有争论说Linux使用Hash路由表相比于二叉树效率太低，不适合商用，其实只要设计良好的哈希算法，尽量减少哈希碰撞，Hash路由表的查找效率也是很高的，在最好的情况下算法复杂算可以达到O(1)，当然，最差也不过是O(n)，我们有理由相信Linux中存在各种优秀的哈希算法，这些都是值得我们学习的。</p>
<p>Linux内核中IPv4路由表采用了分层的结构，第一层是是fib_table，表示路由表，最多可以支持256张路由表，用户可以根据策略路由的需求来创建自己的路由表，系统默认的两张路由表为RT_TABLE_LOCAL和RT_TABLE_MAIN。下面是系统保留的路由表标识符：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">enum</span> rt_class_t <span style="color: #009900;">&#123;</span>
	RT_TABLE_UNSPEC<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
<span style="color: #808080; font-style: italic;">/* User defined values */</span>
	RT_TABLE_COMPAT<span style="color: #339933;">=</span><span style="color: #0000dd;">252</span><span style="color: #339933;">,</span>
	RT_TABLE_DEFAULT<span style="color: #339933;">=</span><span style="color: #0000dd;">253</span><span style="color: #339933;">,</span>
	RT_TABLE_MAIN<span style="color: #339933;">=</span><span style="color: #0000dd;">254</span><span style="color: #339933;">,</span>
	RT_TABLE_LOCAL<span style="color: #339933;">=</span><span style="color: #0000dd;">255</span><span style="color: #339933;">,</span>
	RT_TABLE_MAX<span style="color: #339933;">=</span><span style="color: #208080;">0xFFFFFFFF</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>下面是路由表的定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fib_table <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> hlist_node tb_hlist<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 路由表的标识，即为上面提到的类型 */</span>
	u32		tb_id<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 该路由中默认路由条目在哈希路由表中的索引，
		在fib_table_select_default()函数中计算得出 */</span>
	<span style="color: #993333;">int</span>		tb_default<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* Linux保留内存空间一惯的做法，这块空间留给了struct fn_hash */</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span>	tb_data<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>1.1 根据路由表ID来查找路由表。</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>fib_get_table<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> u32 id<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>tb<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> hlist_node <span style="color: #339933;">*</span>node<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span>head<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> h<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>id <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
		id <span style="color: #339933;">=</span> RT_TABLE_MAIN<span style="color: #339933;">;</span>
	h <span style="color: #339933;">=</span> id <span style="color: #339933;">&amp;</span> <span style="color: #009900;">&#40;</span>FIB_TABLE_HASHSZ <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	rcu_read_lock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	head <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>net<span style="color: #339933;">-&gt;</span>ipv4.<span style="color: #202020;">fib_table_hash</span><span style="color: #009900;">&#91;</span>h<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
	hlist_for_each_entry_rcu<span style="color: #009900;">&#40;</span>tb<span style="color: #339933;">,</span> node<span style="color: #339933;">,</span> head<span style="color: #339933;">,</span> tb_hlist<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tb<span style="color: #339933;">-&gt;</span>tb_id <span style="color: #339933;">==</span> id<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">return</span> tb<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>当路由表标识为0时默认返回主路由表，这里的哈希算法也很简单，当支持多径路由时FIB_TABLE_HASHSZ定义为256，否则FIB_TABLE_HASHSZ定义为2。也就是说在不支持多径路由的情况下，fib_table_hash中只有两个元素。</p>
<p><strong>1.2 路由表的创建。</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>fib_new_table<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> u32 id<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>tb<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> h<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>id <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
		id <span style="color: #339933;">=</span> RT_TABLE_MAIN<span style="color: #339933;">;</span>
	tb <span style="color: #339933;">=</span> fib_get_table<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tb<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> tb<span style="color: #339933;">;</span>
&nbsp;
	tb <span style="color: #339933;">=</span> fib_hash_table<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>tb<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
	h <span style="color: #339933;">=</span> id <span style="color: #339933;">&amp;</span> <span style="color: #009900;">&#40;</span>FIB_TABLE_HASHSZ <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	hlist_add_head_rcu<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>tb<span style="color: #339933;">-&gt;</span>tb_hlist<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>net<span style="color: #339933;">-&gt;</span>ipv4.<span style="color: #202020;">fib_table_hash</span><span style="color: #009900;">&#91;</span>h<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> tb<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>在这个函数里面首先在已有的哈希表中根据给出的路由表标识查找已存在的路由表，若找到对应的路由表则直接返回，否则调用fib_hash_table()函数创建一个新的路由表，然后将其添加到fib_table_hash中去。接下来看一下fib_hash_table()这个函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>fib_hash_table<span style="color: #009900;">&#40;</span>u32 id<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>tb<span style="color: #339933;">;</span>
&nbsp;
	tb <span style="color: #339933;">=</span> kmalloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fib_table<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fn_hash<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
		     GFP_KERNEL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tb <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
&nbsp;
	tb<span style="color: #339933;">-&gt;</span>tb_id <span style="color: #339933;">=</span> id<span style="color: #339933;">;</span>
	tb<span style="color: #339933;">-&gt;</span>tb_default <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
&nbsp;
	memset<span style="color: #009900;">&#40;</span>tb<span style="color: #339933;">-&gt;</span>tb_data<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fn_hash<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> tb<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这个函数很简单，分配了一块内存空间，大小为sizeof(struct fib_table) + sizeof(struct fn_hash)，于是fib_table的最后一个字段tb_data便指向了这个struct fn_hash。接下来该说路由表的第二层了。</p>
<p><strong>2. 路由域struct fn_zone</strong></p>
<p>第二层是fn_zone，表示路由域，Linux根据路由掩码的长度将所有的路由分为32个域。我们先来看下位于fib_table末尾的struct fn_hash的定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fn_hash <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fn_zone		<span style="color: #339933;">*</span>fn_zones<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">33</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fn_zone __rcu	<span style="color: #339933;">*</span>fn_zone_list<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>如上路由表分为32个路由域，这32个跌幅域又连成了链表，其中fn_zone_list这个字段指向链表的头。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">struct</span> fn_zone <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fn_zone __rcu	<span style="color: #339933;">*</span>fz_next<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* Next not empty zone	*/</span>
	<span style="color: #993333;">struct</span> hlist_head __rcu	<span style="color: #339933;">*</span>fz_hash<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* Hash table pointer	*/</span>
	seqlock_t		fz_lock<span style="color: #339933;">;</span>
	u32			fz_hashmask<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* (fz_divisor - 1)	*/</span>
&nbsp;
	u8			fz_order<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* Zone order (0..32)	*/</span>
	u8			fz_revorder<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* 32 - fz_order	*/</span>
	__be32			fz_mask<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* inet_make_mask(order) */</span>
<span style="color: #339933;">#define FZ_MASK(fz)		((fz)-&gt;fz_mask)</span>
&nbsp;
	<span style="color: #993333;">struct</span> hlist_head	fz_embedded_hash<span style="color: #009900;">&#91;</span>EMBEDDED_HASH_SIZE<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #993333;">int</span>			fz_nent<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* Number of entries	*/</span>
	<span style="color: #993333;">int</span>			fz_divisor<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* Hash size (mask+1)	*/</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>2.1 fn_zone的创建</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">static</span> <span style="color: #993333;">struct</span> fn_zone <span style="color: #339933;">*</span>
fn_new_zone<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fn_hash <span style="color: #339933;">*</span>table<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> z<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> i<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fn_zone <span style="color: #339933;">*</span>fz <span style="color: #339933;">=</span> kzalloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fn_zone<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> GFP_KERNEL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fz<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
&nbsp;
	seqlock_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fz<span style="color: #339933;">-&gt;</span>fz_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	fz<span style="color: #339933;">-&gt;</span>fz_divisor <span style="color: #339933;">=</span> z <span style="color: #339933;">?</span> EMBEDDED_HASH_SIZE <span style="color: #339933;">:</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
	fz<span style="color: #339933;">-&gt;</span>fz_hashmask <span style="color: #339933;">=</span> fz<span style="color: #339933;">-&gt;</span>fz_divisor <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
	RCU_INIT_POINTER<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_hash<span style="color: #339933;">,</span> fz<span style="color: #339933;">-&gt;</span>fz_embedded_hash<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	fz<span style="color: #339933;">-&gt;</span>fz_order <span style="color: #339933;">=</span> z<span style="color: #339933;">;</span>
	fz<span style="color: #339933;">-&gt;</span>fz_revorder <span style="color: #339933;">=</span> <span style="color: #0000dd;">32</span> <span style="color: #339933;">-</span> z<span style="color: #339933;">;</span>
	fz<span style="color: #339933;">-&gt;</span>fz_mask <span style="color: #339933;">=</span> inet_make_mask<span style="color: #009900;">&#40;</span>z<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* Find the first not empty zone with more specific mask */</span>
	<span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">=</span> z <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;=</span> <span style="color: #0000dd;">32</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>table<span style="color: #339933;">-&gt;</span>fn_zones<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
			<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">32</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #808080; font-style: italic;">/* No more specific masks, we are the first. */</span>
		rcu_assign_pointer<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_next<span style="color: #339933;">,</span>
				   rtnl_dereference<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">-&gt;</span>fn_zone_list<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		rcu_assign_pointer<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">-&gt;</span>fn_zone_list<span style="color: #339933;">,</span> fz<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
		rcu_assign_pointer<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_next<span style="color: #339933;">,</span>
				   rtnl_dereference<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">-&gt;</span>fn_zones<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">-&gt;</span>fz_next<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		rcu_assign_pointer<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">-&gt;</span>fn_zones<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">-&gt;</span>fz_next<span style="color: #339933;">,</span> fz<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	table<span style="color: #339933;">-&gt;</span>fn_zones<span style="color: #009900;">&#91;</span>z<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> fz<span style="color: #339933;">;</span>
	fib_hash_genid<span style="color: #339933;">++;</span>
	<span style="color: #b1b100;">return</span> fz<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>首先在内存中给struct fn_zone分配内存空间，然后按规则对一些基本的变量进行初始化，之后就是安排各个fn_zone在fn_zone_list的位置了，fn_zone_list中的节点都是按照fz_order从大到小排列的，所以首先一个for循环找出比当前fn_zone的fz_order大的最小fn_zone，如果存在，就将当前节点插到该节点之后;如果不存在，则表示当前节点是目前zn_order最大的节点，则它会成为链表的头。</p>
<p><strong>3. 路由节点fib_node</strong></p>
<p>第三层为路由节点fib_node，在同一个路由域中的路由项有相同的掩码长度，但它们的网络号可以不同，则根据不同路由的网络号将同一个域划分为不同的路由节点，如到地址10.10.65.0和10.10.64.0的路由便是属于同一个路由域里面两个不同路由节点的两条路由。在路由节点中又有根据路由的服务类型，路由类型，路由寻址范围以前路由状态的不同将同一个路由节点中的路由划分成不同的路由</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fib_node <span style="color: #009900;">&#123;</span>
	<span style="color: #808080; font-style: italic;">/* 哈希链表指针，针向同一个Hash槽中的相临节点 */</span>
	<span style="color: #993333;">struct</span> hlist_node	fn_hash<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 属于该节点的alias链表的头 */</span>
	<span style="color: #993333;">struct</span> list_head	fn_alias<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 该节点对应的网络key */</span>
	__be32			fn_key<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 预先分配了空间的别名，在fib_fast_alloc()中使用 */</span>
	<span style="color: #993333;">struct</span> fib_alias        fn_embedded_alias<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>3.1 fib_alias结构</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fib_alias <span style="color: #009900;">&#123;</span>
	<span style="color: #808080; font-style: italic;">/* 指向链表中的相邻节点 */</span>
	<span style="color: #993333;">struct</span> list_head	fa_list<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* fa_info是最终的路由信息 */</span>
	<span style="color: #993333;">struct</span> fib_info		<span style="color: #339933;">*</span>fa_info<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 服务类型，对于一般服务取值为0 */</span>
	u8			fa_tos<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 路由类型 */</span>
	u8			fa_type<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 路由范围 */</span>
	u8			fa_scope<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 路由状态 */</span>
	u8			fa_state<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> rcu_head		rcu<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>路由类型和路由范围的对应关系如下结构：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">static</span> <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span>	error<span style="color: #339933;">;</span>
	u8	scope<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> fib_props<span style="color: #009900;">&#91;</span>RTN_MAX <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#91;</span>RTN_UNSPEC<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_NOWHERE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_UNICAST<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_UNIVERSE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_LOCAL<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_HOST<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_BROADCAST<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_LINK<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_ANYCAST<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_LINK<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_MULTICAST<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_UNIVERSE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_BLACKHOLE<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_UNIVERSE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_UNREACHABLE<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EHOSTUNREACH<span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_UNIVERSE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_PROHIBIT<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EACCES<span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_UNIVERSE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_THROW<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EAGAIN<span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_UNIVERSE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_NAT<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_NOWHERE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
	<span style="color: #009900;">&#91;</span>RTN_XRESOLVE<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
		.<span style="color: #202020;">error</span>	<span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">,</span>
		.<span style="color: #202020;">scope</span>	<span style="color: #339933;">=</span> RT_SCOPE_NOWHERE<span style="color: #339933;">,</span>
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>3.2 fib_info结构</strong></p>
<p>fib_alias中的fib_info为最终的路由信息，来看一下它的定义:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">struct</span> fib_info <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> hlist_node	fib_hash<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> hlist_node	fib_lhash<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> net		<span style="color: #339933;">*</span>fib_net<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span>			fib_treeref<span style="color: #339933;">;</span>
	atomic_t		fib_clntref<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span>			fib_dead<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span>		fib_flags<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span>			fib_protocol<span style="color: #339933;">;</span>
	__be32			fib_prefsrc<span style="color: #339933;">;</span>
	u32			fib_priority<span style="color: #339933;">;</span>
	u32			fib_metrics<span style="color: #009900;">&#91;</span>RTAX_MAX<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#define fib_mtu fib_metrics[RTAX_MTU-1]</span>
<span style="color: #339933;">#define fib_window fib_metrics[RTAX_WINDOW-1]</span>
<span style="color: #339933;">#define fib_rtt fib_metrics[RTAX_RTT-1]</span>
<span style="color: #339933;">#define fib_advmss fib_metrics[RTAX_ADVMSS-1]</span>
	<span style="color: #808080; font-style: italic;">/* 路由的跃点数，当支持多径路由时，fib_nhs为跃点数目，
	当不支持多径路由时，到达目前地址都只有一跳，则该值为1 */</span>
	<span style="color: #993333;">int</span>			fib_nhs<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> rcu_head		rcu<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 下一跳信息 */</span>
	<span style="color: #993333;">struct</span> fib_nh		fib_nh<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#define fib_dev		fib_nh[0].nh_dev</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>3.3 fib_info的创建</strong></p>
<p>struct fib_config中包含了创建一条路由条目所需要的信息。本文中不考虑路由多径的情况，即假设宏CONFIG_IP_ROUTE_MULTIPATH未定义。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> fib_info <span style="color: #339933;">*</span>fib_create_info<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fib_config <span style="color: #339933;">*</span>cfg<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_info <span style="color: #339933;">*</span>fi <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_info <span style="color: #339933;">*</span>ofi<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> nhs <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_nlinfo.<span style="color: #202020;">nl_net</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 检测该请求创建的路由信息范围与类型是否对应 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fib_props<span style="color: #009900;">&#91;</span>cfg<span style="color: #339933;">-&gt;</span>fc_type<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">scope</span> <span style="color: #339933;">&gt;</span> cfg<span style="color: #339933;">-&gt;</span>fc_scope<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> err_inval<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 路由信息除依附于上述所提到的几种数据结构外，同时系统会维护
		两个全局的哈希链表，一个用于保存路由信息，另一个用于保存本地地址，
		fib_info_cnt全局变量表示路由表项的数目，fib_hash_size表示哈希表
		的大小，当路由表项数目大于路由哈希表大小时，为了防止哈希碰撞导致的
		查找效率降低，需要扩大哈希表大小为原来的两倍 */</span>
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fib_info_cnt <span style="color: #339933;">&gt;=</span> fib_hash_size<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> new_size <span style="color: #339933;">=</span> fib_hash_size <span style="color: #339933;">&lt;&lt;</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
		<span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span>new_info_hash<span style="color: #339933;">;</span>
		<span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span>new_laddrhash<span style="color: #339933;">;</span>
		<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> bytes<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>new_size<span style="color: #009900;">&#41;</span>
			new_size <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
		<span style="color: #808080; font-style: italic;">/* 按新的哈希表尺寸为哈希表分配内存空间 */</span>
		bytes <span style="color: #339933;">=</span> new_size <span style="color: #339933;">*</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		new_info_hash <span style="color: #339933;">=</span> fib_hash_alloc<span style="color: #009900;">&#40;</span>bytes<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		new_laddrhash <span style="color: #339933;">=</span> fib_hash_alloc<span style="color: #009900;">&#40;</span>bytes<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>new_info_hash <span style="color: #339933;">||</span> <span style="color: #339933;">!</span>new_laddrhash<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			fib_hash_free<span style="color: #009900;">&#40;</span>new_info_hash<span style="color: #339933;">,</span> bytes<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			fib_hash_free<span style="color: #009900;">&#40;</span>new_laddrhash<span style="color: #339933;">,</span> bytes<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span>
			<span style="color: #808080; font-style: italic;">/* 如果内存空间分配成功，则将旧的列表中的内容移动到新的链表中，
			 	并释放旧列表的内存空间 */</span>
			fib_hash_move<span style="color: #009900;">&#40;</span>new_info_hash<span style="color: #339933;">,</span> new_laddrhash<span style="color: #339933;">,</span> new_size<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #808080; font-style: italic;">/* 列表大小溢出，则出错返回 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fib_hash_size<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> failure<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 为路由表项分配内存空间，大小为struct fib_info的大小加上nhs个下一跳信息
	struct fib_nh的大小，不支持多径路由的路由时nhs始终为1 */</span>
	fi <span style="color: #339933;">=</span> kzalloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span>nhs<span style="color: #339933;">*</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fib_nh<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> GFP_KERNEL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fi <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> failure<span style="color: #339933;">;</span>
	fib_info_cnt<span style="color: #339933;">++;</span>
&nbsp;
	fi<span style="color: #339933;">-&gt;</span>fib_net <span style="color: #339933;">=</span> hold_net<span style="color: #009900;">&#40;</span>net<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	fi<span style="color: #339933;">-&gt;</span>fib_protocol <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_protocol<span style="color: #339933;">;</span>
	fi<span style="color: #339933;">-&gt;</span>fib_flags <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_flags<span style="color: #339933;">;</span>
	fi<span style="color: #339933;">-&gt;</span>fib_priority <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_priority<span style="color: #339933;">;</span>
	fi<span style="color: #339933;">-&gt;</span>fib_prefsrc <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_prefsrc<span style="color: #339933;">;</span>
&nbsp;
	fi<span style="color: #339933;">-&gt;</span>fib_nhs <span style="color: #339933;">=</span> nhs<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 遍历fib_nh信息，此处仅执行一次，设置fib_nh的nh_parent */</span>
	change_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		nexthop_nh<span style="color: #339933;">-&gt;</span>nh_parent <span style="color: #339933;">=</span> fi<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> endfor_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 如果给出了路由属性信信，则通过遍历路由属性信息来确定fib_metrics的值 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_mx<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> nlattr <span style="color: #339933;">*</span>nla<span style="color: #339933;">;</span>
		<span style="color: #993333;">int</span> remaining<span style="color: #339933;">;</span>
&nbsp;
		nla_for_each_attr<span style="color: #009900;">&#40;</span>nla<span style="color: #339933;">,</span> cfg<span style="color: #339933;">-&gt;</span>fc_mx<span style="color: #339933;">,</span> cfg<span style="color: #339933;">-&gt;</span>fc_mx_len<span style="color: #339933;">,</span> remaining<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #993333;">int</span> type <span style="color: #339933;">=</span> nla_type<span style="color: #009900;">&#40;</span>nla<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>type<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>type <span style="color: #339933;">&gt;</span> RTAX_MAX<span style="color: #009900;">&#41;</span>
					<span style="color: #b1b100;">goto</span> err_inval<span style="color: #339933;">;</span>
				fi<span style="color: #339933;">-&gt;</span>fib_metrics<span style="color: #009900;">&#91;</span>type <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> nla_get_u32<span style="color: #009900;">&#40;</span>nla<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #993333;">struct</span> fib_nh <span style="color: #339933;">*</span>nh <span style="color: #339933;">=</span> fi<span style="color: #339933;">-&gt;</span>fib_nh<span style="color: #339933;">;</span>
&nbsp;
	nh<span style="color: #339933;">-&gt;</span>nh_oif <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_oif<span style="color: #339933;">;</span>
	nh<span style="color: #339933;">-&gt;</span>nh_gw <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_gw<span style="color: #339933;">;</span>
	nh<span style="color: #339933;">-&gt;</span>nh_flags <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_flags<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fib_props<span style="color: #009900;">&#91;</span>cfg<span style="color: #339933;">-&gt;</span>fc_type<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">error</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_gw <span style="color: #339933;">||</span> cfg<span style="color: #339933;">-&gt;</span>fc_oif <span style="color: #339933;">||</span> cfg<span style="color: #339933;">-&gt;</span>fc_mp<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> err_inval<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">goto</span> link_it<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_scope <span style="color: #339933;">&gt;</span> RT_SCOPE_HOST<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> err_inval<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_scope <span style="color: #339933;">==</span> RT_SCOPE_HOST<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> fib_nh <span style="color: #339933;">*</span>nh <span style="color: #339933;">=</span> fi<span style="color: #339933;">-&gt;</span>fib_nh<span style="color: #339933;">;</span>
		<span style="color: #808080; font-style: italic;">/* 当前添加的是本地路由信息，只可能有一跳，即便是开启了
			多径路由，下一跳数目不为1则报错，同时本地路由也不需要
			指定网关，如果指定则报错 */</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>nhs <span style="color: #339933;">!=</span> <span style="color: #0000dd;">1</span> <span style="color: #339933;">||</span> nh<span style="color: #339933;">-&gt;</span>nh_gw<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> err_inval<span style="color: #339933;">;</span>
		nh<span style="color: #339933;">-&gt;</span>nh_scope <span style="color: #339933;">=</span> RT_SCOPE_NOWHERE<span style="color: #339933;">;</span>
		nh<span style="color: #339933;">-&gt;</span>nh_dev <span style="color: #339933;">=</span> dev_get_by_index<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> fi<span style="color: #339933;">-&gt;</span>fib_nh<span style="color: #339933;">-&gt;</span>nh_oif<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENODEV<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>nh<span style="color: #339933;">-&gt;</span>nh_dev <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> failure<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #808080; font-style: italic;">/* 如果添加的不是本地路由信息，则检查下一跳信息 */</span>
		change_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			err <span style="color: #339933;">=</span> fib_check_nh<span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">,</span> fi<span style="color: #339933;">,</span> nexthop_nh<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
				<span style="color: #b1b100;">goto</span> failure<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> endfor_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fi<span style="color: #339933;">-&gt;</span>fib_prefsrc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_type <span style="color: #339933;">!=</span> RTN_LOCAL <span style="color: #339933;">||</span> <span style="color: #339933;">!</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst <span style="color: #339933;">||</span>
		    fi<span style="color: #339933;">-&gt;</span>fib_prefsrc <span style="color: #339933;">!=</span> cfg<span style="color: #339933;">-&gt;</span>fc_dst<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>inet_addr_type<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> fi<span style="color: #339933;">-&gt;</span>fib_prefsrc<span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> RTN_LOCAL<span style="color: #009900;">&#41;</span>
				<span style="color: #b1b100;">goto</span> err_inval<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
link_it<span style="color: #339933;">:</span>
	<span style="color: #808080; font-style: italic;">/* 查找路由条目，返回与当前路由条目精确匹配的条目，
		若存在，则释放当前创建的新条目，增加已找到的路由条目
		的引用计数，并返回已找到的旧路由条目 */</span>
	ofi <span style="color: #339933;">=</span> fib_find_info<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ofi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		fi<span style="color: #339933;">-&gt;</span>fib_dead <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
		free_fib_info<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		ofi<span style="color: #339933;">-&gt;</span>fib_treeref<span style="color: #339933;">++;</span>
		<span style="color: #b1b100;">return</span> ofi<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 当前路由表中未找到已存在的符合要求的路由条目, 则增加
		新建路由条目的引用计数 */</span>
	fi<span style="color: #339933;">-&gt;</span>fib_treeref<span style="color: #339933;">++;</span>
	atomic_inc<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fi<span style="color: #339933;">-&gt;</span>fib_clntref<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	spin_lock_bh<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fib_info_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 将新建的路由插入到全局路由列表中，其中fib_info_hashfh
		为散列函数 */</span>
	hlist_add_head<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fi<span style="color: #339933;">-&gt;</span>fib_hash<span style="color: #339933;">,</span>
		       <span style="color: #339933;">&amp;</span>fib_info_hash<span style="color: #009900;">&#91;</span>fib_info_hashfn<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 如果指定了源地址，则将源地址插入到全局本地地址列表中 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fi<span style="color: #339933;">-&gt;</span>fib_prefsrc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span>head<span style="color: #339933;">;</span>
		head <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>fib_info_laddrhash<span style="color: #009900;">&#91;</span>fib_laddr_hashfn<span style="color: #009900;">&#40;</span>fi<span style="color: #339933;">-&gt;</span>fib_prefsrc<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		hlist_add_head<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fi<span style="color: #339933;">-&gt;</span>fib_lhash<span style="color: #339933;">,</span> head<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #808080; font-style: italic;">/* 将下一跳信息写入全局列表中，由上述知本迭代只进行一次，
		散列函数为fib_devindex_hashfn() */</span>
	change_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span>head<span style="color: #339933;">;</span>
		<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> hash<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>nexthop_nh<span style="color: #339933;">-&gt;</span>nh_dev<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
		hash <span style="color: #339933;">=</span> fib_devindex_hashfn<span style="color: #009900;">&#40;</span>nexthop_nh<span style="color: #339933;">-&gt;</span>nh_dev<span style="color: #339933;">-&gt;</span>ifindex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		head <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>fib_info_devhash<span style="color: #009900;">&#91;</span>hash<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		hlist_add_head<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>nexthop_nh<span style="color: #339933;">-&gt;</span>nh_hash<span style="color: #339933;">,</span> head<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> endfor_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span>
	spin_unlock_bh<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fib_info_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> fi<span style="color: #339933;">;</span>
&nbsp;
err_inval<span style="color: #339933;">:</span>
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
&nbsp;
failure<span style="color: #339933;">:</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		fi<span style="color: #339933;">-&gt;</span>fib_dead <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
		free_fib_info<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> ERR_PTR<span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p><strong>3.4 向路由表中插入路由信息。</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">int</span> fib_table_insert<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>tb<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> fib_config <span style="color: #339933;">*</span>cfg<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fn_hash <span style="color: #339933;">*</span>table <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fn_hash <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> tb<span style="color: #339933;">-&gt;</span>tb_data<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_node <span style="color: #339933;">*</span>new_f <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_node <span style="color: #339933;">*</span>f<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_alias <span style="color: #339933;">*</span>fa<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>new_fa<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fn_zone <span style="color: #339933;">*</span>fz<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fib_info <span style="color: #339933;">*</span>fi<span style="color: #339933;">;</span>
	u8 tos <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_tos<span style="color: #339933;">;</span>
	__be32 key<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst_len <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">32</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 根据目的地址长度找出对应的路由域 */</span>
	fz <span style="color: #339933;">=</span> table<span style="color: #339933;">-&gt;</span>fn_zones<span style="color: #009900;">&#91;</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst_len<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 如果路由域不存在，则调用fn_new_zone()函数创建一个新的路由域 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fz <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>fz <span style="color: #339933;">=</span> fn_new_zone<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">,</span> cfg<span style="color: #339933;">-&gt;</span>fc_dst_len<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
&nbsp;
	key <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 如果指定了目的地址，如果目的地址主机位不为0，则出错返回 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst <span style="color: #339933;">&amp;</span> ~FZ_MASK<span style="color: #009900;">&#40;</span>fz<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
		key <span style="color: #339933;">=</span> fz_key<span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst<span style="color: #339933;">,</span> fz<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 创建一个新的fib_info对象 */</span>
	fi <span style="color: #339933;">=</span> fib_create_info<span style="color: #009900;">&#40;</span>cfg<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>IS_ERR<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> PTR_ERR<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 如果当前路由域中路由节点的数目大于散列表大小的两倍，
		并且相关数据都合法的情况下，需要重构散列表以减小
		哈希碰撞 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_nent <span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_divisor<span style="color: #339933;">&lt;&lt;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
	    fz<span style="color: #339933;">-&gt;</span>fz_divisor <span style="color: #339933;">&lt;</span> FZ_MAX_DIVISOR <span style="color: #339933;">&amp;&amp;</span>
	    <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_dst_len <span style="color: #339933;">==</span> <span style="color: #0000dd;">32</span> <span style="color: #339933;">||</span>
	     <span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span> <span style="color: #339933;">&lt;&lt;</span> cfg<span style="color: #339933;">-&gt;</span>fc_dst_len<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> fz<span style="color: #339933;">-&gt;</span>fz_divisor<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		fn_rehash_zone<span style="color: #009900;">&#40;</span>fz<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 通过网络号key找出对应的路由节点fn_node */</span>
	f <span style="color: #339933;">=</span> fib_find_node<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">,</span> key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>f<span style="color: #009900;">&#41;</span>
		fa <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">else</span>
		fa <span style="color: #339933;">=</span> fib_find_alias<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>f<span style="color: #339933;">-&gt;</span>fn_alias<span style="color: #339933;">,</span> tos<span style="color: #339933;">,</span> fi<span style="color: #339933;">-&gt;</span>fib_priority<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa <span style="color: #339933;">&amp;&amp;</span> fa<span style="color: #339933;">-&gt;</span>fa_tos <span style="color: #339933;">==</span> tos <span style="color: #339933;">&amp;&amp;</span>
	    fa<span style="color: #339933;">-&gt;</span>fa_info<span style="color: #339933;">-&gt;</span>fib_priority <span style="color: #339933;">==</span> fi<span style="color: #339933;">-&gt;</span>fib_priority<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> fib_alias <span style="color: #339933;">*</span>fa_first<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>fa_match<span style="color: #339933;">;</span>
&nbsp;
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EEXIST<span style="color: #339933;">;</span>
		<span style="color: #808080; font-style: italic;">/* 如果具有与新建路由项相同属性的fib_alias存在，并且添加路由项标志中
			设置了NLM_F_EXCL(排它选项)，则返回路由已存在 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlflags <span style="color: #339933;">&amp;</span> NLM_F_EXCL<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #808080; font-style: italic;">/* We have 2 goals:
		 * 1. Find exact match for type, scope, fib_info to avoid
		 * duplicate routes
		 * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
		 */</span>
		fa_match <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
		fa_first <span style="color: #339933;">=</span> fa<span style="color: #339933;">;</span>
		fa <span style="color: #339933;">=</span> list_entry<span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_list.<span style="color: #202020;">prev</span><span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> fib_alias<span style="color: #339933;">,</span> fa_list<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		list_for_each_entry_continue<span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>f<span style="color: #339933;">-&gt;</span>fn_alias<span style="color: #339933;">,</span> fa_list<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_tos <span style="color: #339933;">!=</span> tos<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_info<span style="color: #339933;">-&gt;</span>fib_priority <span style="color: #339933;">!=</span> fi<span style="color: #339933;">-&gt;</span>fib_priority<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_type <span style="color: #339933;">==</span> cfg<span style="color: #339933;">-&gt;</span>fc_type <span style="color: #339933;">&amp;&amp;</span>
			    fa<span style="color: #339933;">-&gt;</span>fa_scope <span style="color: #339933;">==</span> cfg<span style="color: #339933;">-&gt;</span>fc_scope <span style="color: #339933;">&amp;&amp;</span>
			    fa<span style="color: #339933;">-&gt;</span>fa_info <span style="color: #339933;">==</span> fi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				fa_match <span style="color: #339933;">=</span> fa<span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlflags <span style="color: #339933;">&amp;</span> NLM_F_REPLACE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			u8 state<span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #808080; font-style: italic;">/* 如果存在一条精确匹配的路由项fib_alias，并且在设置了NLM_F_REPLACE
				标志的情况下，不做处理直接返回 */</span>
			fa <span style="color: #339933;">=</span> fa_first<span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa_match<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa <span style="color: #339933;">==</span> fa_match<span style="color: #009900;">&#41;</span>
					err <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
				<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
&nbsp;
			<span style="color: #808080; font-style: italic;">/* 并没有精确匹配的路由项fib_alias，即便有匹配的fib_alias，也是
				仅tos和priority两个选项匹配，因此需要新建一个路由别名fib_alias */</span>
			err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
			new_fa <span style="color: #339933;">=</span> fib_fast_alloc<span style="color: #009900;">&#40;</span>f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>new_fa <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
				<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
			new_fa<span style="color: #339933;">-&gt;</span>fa_tos <span style="color: #339933;">=</span> fa<span style="color: #339933;">-&gt;</span>fa_tos<span style="color: #339933;">;</span>
			new_fa<span style="color: #339933;">-&gt;</span>fa_info <span style="color: #339933;">=</span> fi<span style="color: #339933;">;</span>
			new_fa<span style="color: #339933;">-&gt;</span>fa_type <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_type<span style="color: #339933;">;</span>
			new_fa<span style="color: #339933;">-&gt;</span>fa_scope <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_scope<span style="color: #339933;">;</span>
			state <span style="color: #339933;">=</span> fa<span style="color: #339933;">-&gt;</span>fa_state<span style="color: #339933;">;</span>
			new_fa<span style="color: #339933;">-&gt;</span>fa_state <span style="color: #339933;">=</span> state <span style="color: #339933;">&amp;</span> ~FA_S_ACCESSED<span style="color: #339933;">;</span>
			fib_hash_genid<span style="color: #339933;">++;</span>
			<span style="color: #808080; font-style: italic;">/* 因为设置了NLM_F_REPLACE选项，所以用新fib_alias对象替换掉
				列表中旧的fib_alias对象，并释放旧对象的内存 */</span>
			list_replace_rcu<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fa<span style="color: #339933;">-&gt;</span>fa_list<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>new_fa<span style="color: #339933;">-&gt;</span>fa_list<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			fn_free_alias<span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">,</span> f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>state <span style="color: #339933;">&amp;</span> FA_S_ACCESSED<span style="color: #009900;">&#41;</span>
				rt_cache_flush<span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlinfo.<span style="color: #202020;">nl_net</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #808080; font-style: italic;">/* 这里留做以后再讨论 */</span>
			rtmsg_fib<span style="color: #009900;">&#40;</span>RTM_NEWROUTE<span style="color: #339933;">,</span> key<span style="color: #339933;">,</span> new_fa<span style="color: #339933;">,</span> cfg<span style="color: #339933;">-&gt;</span>fc_dst_len<span style="color: #339933;">,</span>
				  tb<span style="color: #339933;">-&gt;</span>tb_id<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlinfo<span style="color: #339933;">,</span> NLM_F_REPLACE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #808080; font-style: italic;">/* Error if we find a perfect match which
		 * uses the same scope, type, and nexthop
		 * information.
		 */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa_match<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlflags <span style="color: #339933;">&amp;</span> NLM_F_APPEND<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
			fa <span style="color: #339933;">=</span> fa_first<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 对应的fib_alias对象并不存在，如果没有设置NLM_F_CREATE则返回出错 */</span>
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENOENT<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlflags <span style="color: #339933;">&amp;</span> NLM_F_CREATE<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 新建一个路由节点项fib_node并初始化 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>f<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		new_f <span style="color: #339933;">=</span> kmem_cache_zalloc<span style="color: #009900;">&#40;</span>fn_hash_kmem<span style="color: #339933;">,</span> GFP_KERNEL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>new_f <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
		INIT_HLIST_NODE<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>new_f<span style="color: #339933;">-&gt;</span>fn_hash<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		INIT_LIST_HEAD<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>new_f<span style="color: #339933;">-&gt;</span>fn_alias<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		new_f<span style="color: #339933;">-&gt;</span>fn_key <span style="color: #339933;">=</span> key<span style="color: #339933;">;</span>
		f <span style="color: #339933;">=</span> new_f<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 新建一个路由别名项fib_alias并初始化 */</span>
	new_fa <span style="color: #339933;">=</span> fib_fast_alloc<span style="color: #009900;">&#40;</span>f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>new_fa <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
	new_fa<span style="color: #339933;">-&gt;</span>fa_info <span style="color: #339933;">=</span> fi<span style="color: #339933;">;</span>
	new_fa<span style="color: #339933;">-&gt;</span>fa_tos <span style="color: #339933;">=</span> tos<span style="color: #339933;">;</span>
	new_fa<span style="color: #339933;">-&gt;</span>fa_type <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_type<span style="color: #339933;">;</span>
	new_fa<span style="color: #339933;">-&gt;</span>fa_scope <span style="color: #339933;">=</span> cfg<span style="color: #339933;">-&gt;</span>fc_scope<span style="color: #339933;">;</span>
	new_fa<span style="color: #339933;">-&gt;</span>fa_state <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* 将路由信息项，路由别名项，路由节点项按规则组织起来
		后刷新路由表 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>new_f<span style="color: #009900;">&#41;</span>
		fib_insert_node<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">,</span> new_f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	list_add_tail_rcu<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>new_fa<span style="color: #339933;">-&gt;</span>fa_list<span style="color: #339933;">,</span>
		 <span style="color: #009900;">&#40;</span>fa <span style="color: #339933;">?</span> <span style="color: #339933;">&amp;</span>fa<span style="color: #339933;">-&gt;</span>fa_list <span style="color: #339933;">:</span> <span style="color: #339933;">&amp;</span>f<span style="color: #339933;">-&gt;</span>fn_alias<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	fib_hash_genid<span style="color: #339933;">++;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>new_f<span style="color: #009900;">&#41;</span>
		fz<span style="color: #339933;">-&gt;</span>fz_nent<span style="color: #339933;">++;</span>
	rt_cache_flush<span style="color: #009900;">&#40;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlinfo.<span style="color: #202020;">nl_net</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	rtmsg_fib<span style="color: #009900;">&#40;</span>RTM_NEWROUTE<span style="color: #339933;">,</span> key<span style="color: #339933;">,</span> new_fa<span style="color: #339933;">,</span> cfg<span style="color: #339933;">-&gt;</span>fc_dst_len<span style="color: #339933;">,</span> tb<span style="color: #339933;">-&gt;</span>tb_id<span style="color: #339933;">,</span>
		  <span style="color: #339933;">&amp;</span>cfg<span style="color: #339933;">-&gt;</span>fc_nlinfo<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
out<span style="color: #339933;">:</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>new_f<span style="color: #009900;">&#41;</span>
		kmem_cache_free<span style="color: #009900;">&#40;</span>fn_hash_kmem<span style="color: #339933;">,</span> new_f<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	fib_release_info<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p><strong>3.5 查询路由信息</strong></p>
<p>这个这个函数先在RT_TABLE_LOCAL表中查询路由信息，成功则返回，如果失败则再从RT_TABLE_MAIN表中查询路由信息，如果失败则返回网络不可达，成功则返回0。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #000000; font-weight: bold;">inline</span> <span style="color: #993333;">int</span> fib_lookup<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>flp<span style="color: #339933;">,</span>
			     <span style="color: #993333;">struct</span> fib_result <span style="color: #339933;">*</span>res<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>table<span style="color: #339933;">;</span>
&nbsp;
	table <span style="color: #339933;">=</span> fib_get_table<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> RT_TABLE_LOCAL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fib_table_lookup<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">,</span> flp<span style="color: #339933;">,</span> res<span style="color: #339933;">,</span> FIB_LOOKUP_NOREF<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
	table <span style="color: #339933;">=</span> fib_get_table<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> RT_TABLE_MAIN<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>fib_table_lookup<span style="color: #009900;">&#40;</span>table<span style="color: #339933;">,</span> flp<span style="color: #339933;">,</span> res<span style="color: #339933;">,</span> FIB_LOOKUP_NOREF<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>ENETUNREACH<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>接下来再看fib_table_lookup()函数的定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> fib_table_lookup<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fib_table <span style="color: #339933;">*</span>tb<span style="color: #339933;">,</span>
		     <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>flp<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> fib_result <span style="color: #339933;">*</span>res<span style="color: #339933;">,</span>
		     <span style="color: #993333;">int</span> fib_flags<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fn_zone <span style="color: #339933;">*</span>fz<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> fn_hash <span style="color: #339933;">*</span>t <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> fn_hash <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span>tb<span style="color: #339933;">-&gt;</span>tb_data<span style="color: #339933;">;</span>
&nbsp;
	rcu_read_lock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>fz <span style="color: #339933;">=</span> rcu_dereference<span style="color: #009900;">&#40;</span>t<span style="color: #339933;">-&gt;</span>fn_zone_list<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	     fz <span style="color: #339933;">!=</span> NULL<span style="color: #339933;">;</span>
	     fz <span style="color: #339933;">=</span> rcu_dereference<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_next<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> hlist_head <span style="color: #339933;">*</span>head<span style="color: #339933;">;</span>
		<span style="color: #993333;">struct</span> hlist_node <span style="color: #339933;">*</span>node<span style="color: #339933;">;</span>
		<span style="color: #993333;">struct</span> fib_node <span style="color: #339933;">*</span>f<span style="color: #339933;">;</span>
		__be32 k<span style="color: #339933;">;</span>
		<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> seq<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">do</span> <span style="color: #009900;">&#123;</span>
			seq <span style="color: #339933;">=</span> read_seqbegin<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fz<span style="color: #339933;">-&gt;</span>fz_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			k <span style="color: #339933;">=</span> fz_key<span style="color: #009900;">&#40;</span>flp<span style="color: #339933;">-&gt;</span>fl4_dst<span style="color: #339933;">,</span> fz<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			head <span style="color: #339933;">=</span> rcu_dereference<span style="color: #009900;">&#40;</span>fz<span style="color: #339933;">-&gt;</span>fz_hash<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> fn_hash<span style="color: #009900;">&#40;</span>k<span style="color: #339933;">,</span> fz<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			hlist_for_each_entry_rcu<span style="color: #009900;">&#40;</span>f<span style="color: #339933;">,</span> node<span style="color: #339933;">,</span> head<span style="color: #339933;">,</span> fn_hash<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>f<span style="color: #339933;">-&gt;</span>fn_key <span style="color: #339933;">!=</span> k<span style="color: #009900;">&#41;</span>
					<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
&nbsp;
				err <span style="color: #339933;">=</span> fib_semantic_match<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>f<span style="color: #339933;">-&gt;</span>fn_alias<span style="color: #339933;">,</span>
						 flp<span style="color: #339933;">,</span> res<span style="color: #339933;">,</span>
						 fz<span style="color: #339933;">-&gt;</span>fz_order<span style="color: #339933;">,</span> fib_flags<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err <span style="color: #339933;">&lt;=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
					<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>read_seqretry<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>fz<span style="color: #339933;">-&gt;</span>fz_lock<span style="color: #339933;">,</span> seq<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	err <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
out<span style="color: #339933;">:</span>
	rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>该函数从fn_zone_list开始遍历fn_zone链表，上面创建fn_zone的过程中我们提到过，fn_zone在fn_zone_list中是按掩码长度从大到小排列的，即该搜索先匹配掩码最大的zone，若不匹配则转而去匹配下一个掩码稍小的路由域，其中fn_key是匹配的关键，如果key不匹配则该路由项肯定不匹配，key匹配之后还要再调用fib_semantic_match()函数再做进一步的检查，并在该函数中利用查询到的路由信息给查询结果对象fib_result初始化。当没有一个掩码长度不为0的zone中有路由项与其匹配时，函数最后一次循环会检测掩码长度为0的zone，该域便为默认路由域，在这里fn_key与k的值均为0，它们始终匹配，也就是说如果检查不到匹配的路由项，则交由默认路由来处理（不知道这样表达合不合理，大体就是这个意思），接下来再来检验证TOS，SCOPE等信息。接下来看fib_semantic_match()函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">int</span> fib_semantic_match<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> list_head <span style="color: #339933;">*</span>head<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> flowi <span style="color: #339933;">*</span>flp<span style="color: #339933;">,</span>
		       <span style="color: #993333;">struct</span> fib_result <span style="color: #339933;">*</span>res<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> prefixlen<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> fib_flags<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> fib_alias <span style="color: #339933;">*</span>fa<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> nh_sel <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
	list_for_each_entry_rcu<span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">,</span> head<span style="color: #339933;">,</span> fa_list<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_tos <span style="color: #339933;">&amp;&amp;</span>
		    fa<span style="color: #339933;">-&gt;</span>fa_tos <span style="color: #339933;">!=</span> flp<span style="color: #339933;">-&gt;</span>fl4_tos<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_scope <span style="color: #339933;">&lt;</span> flp<span style="color: #339933;">-&gt;</span>fl4_scope<span style="color: #009900;">&#41;</span>
			<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
&nbsp;
		fib_alias_accessed<span style="color: #009900;">&#40;</span>fa<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		err <span style="color: #339933;">=</span> fib_props<span style="color: #009900;">&#91;</span>fa<span style="color: #339933;">-&gt;</span>fa_type<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">error</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #993333;">struct</span> fib_info <span style="color: #339933;">*</span>fi <span style="color: #339933;">=</span> fa<span style="color: #339933;">-&gt;</span>fa_info<span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fi<span style="color: #339933;">-&gt;</span>fib_flags <span style="color: #339933;">&amp;</span> RTNH_F_DEAD<span style="color: #009900;">&#41;</span>
				<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">switch</span> <span style="color: #009900;">&#40;</span>fa<span style="color: #339933;">-&gt;</span>fa_type<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">case</span> RTN_UNICAST<span style="color: #339933;">:</span>
			<span style="color: #b1b100;">case</span> RTN_LOCAL<span style="color: #339933;">:</span>
			<span style="color: #b1b100;">case</span> RTN_BROADCAST<span style="color: #339933;">:</span>
			<span style="color: #b1b100;">case</span> RTN_ANYCAST<span style="color: #339933;">:</span>
			<span style="color: #b1b100;">case</span> RTN_MULTICAST<span style="color: #339933;">:</span>
				for_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
					<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>nh<span style="color: #339933;">-&gt;</span>nh_flags <span style="color: #339933;">&amp;</span> RTNH_F_DEAD<span style="color: #009900;">&#41;</span>
						<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
					<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>flp<span style="color: #339933;">-&gt;</span>oif <span style="color: #339933;">||</span> flp<span style="color: #339933;">-&gt;</span>oif <span style="color: #339933;">==</span> nh<span style="color: #339933;">-&gt;</span>nh_oif<span style="color: #009900;">&#41;</span>
						<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
				<span style="color: #009900;">&#125;</span>
				<span style="color: #b1b100;">goto</span> out_fill_res<span style="color: #339933;">;</span>
				endfor_nexthops<span style="color: #009900;">&#40;</span>fi<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #b1b100;">continue</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">default</span><span style="color: #339933;">:</span>
				pr_warning<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;fib_semantic_match bad type %#x<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span>
					   fa<span style="color: #339933;">-&gt;</span>fa_type<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
&nbsp;
out_fill_res<span style="color: #339933;">:</span>
	res<span style="color: #339933;">-&gt;</span>prefixlen <span style="color: #339933;">=</span> prefixlen<span style="color: #339933;">;</span>
	res<span style="color: #339933;">-&gt;</span>nh_sel <span style="color: #339933;">=</span> nh_sel<span style="color: #339933;">;</span>
	res<span style="color: #339933;">-&gt;</span>type <span style="color: #339933;">=</span> fa<span style="color: #339933;">-&gt;</span>fa_type<span style="color: #339933;">;</span>
	res<span style="color: #339933;">-&gt;</span>scope <span style="color: #339933;">=</span> fa<span style="color: #339933;">-&gt;</span>fa_scope<span style="color: #339933;">;</span>
	res<span style="color: #339933;">-&gt;</span>fi <span style="color: #339933;">=</span> fa<span style="color: #339933;">-&gt;</span>fa_info<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>fib_flags <span style="color: #339933;">&amp;</span> FIB_LOOKUP_NOREF<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		atomic_inc<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>res<span style="color: #339933;">-&gt;</span>fi<span style="color: #339933;">-&gt;</span>fib_clntref<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>该函数返回1时表示没有匹配的路由项，返回0时表示匹配成功，返回负值时表示管理失败，即fa->fa_type类型有问题，可查询fib_props数组来确定对应的fa_type是否有错误，如果类型为RTN_BLACKHOLE,RTN_UNREACHABLE,RTN_PROHIBIT,RTN_THROW,RTN_NAT,RTN_XRESOLVE时表示存在对应的错误。</p>
<p>函数先检验TOS，如果搜索健值中指定了TOS，则必须对TOS进行严格匹配，否则当前路由项即不符合要求。如果未指定TOS，则可以匹配任意TOS。</p>
<p>接下来检验scope，路由项的范围必须比请求的范围更”窄“才能符合要求，如果请求查找的scope为RTN_UNIVERSE，则RTN_LINK即不匹配;类似的，如果请求查找的是RTN_HOST，则RTN_LINK，RTN_UNIVERSE都是可以匹配的。</p>
<p>scope检查完成之后即开始检查路由类型，即刚才提到的fa->fa_type，不再说了。如果类型没有问题则接下来检查下一跳信息，首先得确保下一跳信息不是待删除的，其次如果指定了出口设备，则出口设备要和下一跳信息中的出口设备匹配。如果到这一步全都匹配，则函数跳到out_fill_res，填充fib_result，之后返回0，表示成功。</p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/intro-linux-kernel-hash-rt-1.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>浅析Linux Kernel中的那些链表</title>
		<link>http://basiccoder.com/intro-linux-kernel-list.html</link>
		<comments>http://basiccoder.com/intro-linux-kernel-list.html#comments</comments>
		<pubDate>Sun, 06 Mar 2011 07:14:20 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=816</guid>
		<description><![CDATA[链表是Linux内核中最重要的数据结构，但Linux内核中的链表与传统的数据结构书中看到的链表结构略有不同。这里简单写一下我对于Linux内核中链表的理解，不足之处欢迎路过的大牛给出批评意见。

<strong>1.传统形式的链表</strong>

数据结构书中的链表一般是下面这种形式：
<pre lang="C">
struct list {
	struct list *pre;
	struct list *next;
	void *data;
};
</pre>
每一个链表结构中都包括两个同类型的指针，分别指向链表的上一个节点和下一个节点。这样当该节点处于一个循环链表中时，链表的首节点一般不用于保存数据，但首节点也需要是一个struct list类型，当结构庞大时首节点也必须分配同样大小的空间，这样就造成了内存的浪费。

<strong>2. Linux内核中的双向循环链表</strong>

Linux内核中定义了一个struct list_head类型用于保存链表信息指针：
<pre lang="C">
struct list_head {
	struct list_head *next, *prev;
};
</pre>
如此在链表中链表头只需用一个struct list_head类型来表示即可，不管链表中节点的数据结构多么庞大，链表头只需要占8个字节，链表中的节点则如下定义：

<pre lang="C">
struct list {
	struct list_head *list_head;
	void *data;
};
</pre>
如此一来类似于list_add()和list_del()这些函数只需要接收struct list_head类型的参数即可，不失通用性。

在链表这种结构中最好玩的地方在于对于这种形式的链表的遍历，Linux中定义的以下的宏：
<pre lang="C">
#define __list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)
</pre>
这个宏其实没什么神奇的地方，很典型的对于循环链表的迭代，但对于Linux内核中的链表，迭代的指针只能是一个struct list_head类型，对于这个类型来说我们只能对它做一些移动或者添加的操作，并不能取出该list_head对应的节点。这个迭代过程对于节点的删除操作其实是不安全的，假设我们在迭代中移除了pos节点，则进入下一次迭代时，pos = pos->next这个就不知会指向哪里去了，Linux中也定义了对于删除安全的迭代宏：
<pre lang="C">
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)
</pre>
这个宏中使用了n这个struct list_head类型的临时... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>链表是Linux内核中最重要的数据结构，但Linux内核中的链表与传统的数据结构书中看到的链表结构略有不同。这里简单写一下我对于Linux内核中链表的理解，不足之处欢迎路过的大牛给出批评意见。</p>
<p><strong>1.传统形式的链表</strong></p>
<p>数据结构书中的链表一般是下面这种形式：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> list <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> list <span style="color: #339933;">*</span>pre<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> list <span style="color: #339933;">*</span>next<span style="color: #339933;">;</span>
	<span style="color: #993333;">void</span> <span style="color: #339933;">*</span>data<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>每一个链表结构中都包括两个同类型的指针，分别指向链表的上一个节点和下一个节点。这样当该节点处于一个循环链表中时，链表的首节点一般不用于保存数据，但首节点也需要是一个struct list类型，当结构庞大时首节点也必须分配同样大小的空间，这样就造成了内存的浪费。</p>
<p><strong>2. Linux内核中的双向循环链表</strong></p>
<p>Linux内核中定义了一个struct list_head类型用于保存链表信息指针：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> list_head <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> list_head <span style="color: #339933;">*</span>next<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>prev<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>如此在链表中链表头只需用一个struct list_head类型来表示即可，不管链表中节点的数据结构多么庞大，链表头只需要占8个字节，链表中的节点则如下定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> list <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> list_head list_head<span style="color: #339933;">;</span>
	<span style="color: #993333;">void</span> <span style="color: #339933;">*</span>data<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>如此一来类似于list_add()和list_del()这些函数只需要接收struct list_head类型的参数即可，不失通用性。</p>
<p>在链表这种结构中最好玩的地方在于对于这种形式的链表的遍历，Linux中定义的以下的宏：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define __list_for_each(pos, head) \
	for (pos = (head)-&gt;next; pos != (head); pos = pos-&gt;next)</span></pre></div></div>

<p>这个宏其实没什么神奇的地方，很典型的对于循环链表的迭代，但对于Linux内核中的链表，迭代的指针只能是一个struct list_head类型，对于这个类型来说我们只能对它做一些移动或者添加的操作，并不能取出该list_head对应的节点。这个迭代过程对于节点的删除操作其实是不安全的，假设我们在迭代中移除了pos节点，则进入下一次迭代时，pos = pos->next这个就不知会指向哪里去了，Linux中也定义了对于删除安全的迭代宏：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define list_for_each_safe(pos, n, head) \
	for (pos = (head)-&gt;next, n = pos-&gt;next; pos != (head); \
		pos = n, n = pos-&gt;next)</span></pre></div></div>

<p>这个宏中使用了n这个struct list_head类型的临时变量，每次迭代之后pos的下一个节点储存在临时变量n中，则在迭代中删除pos节点后，下一次迭代会重新给pos赋值为临时变量n，然后再做迭代。这样在迭代的过程中就可以安全地删除节点pos了。</p>
<p>刚才说过这种迭代每次迭代的变量都只是一个struct list_head类型，而更多时候我们遍历一个链表是为了读取或者修改链表的节点数据，这个时候我们就需要用到另外一种宏：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)-&gt;next, typeof(*pos), member);	\
	     prefetch(pos-&gt;member.next), &amp;pos-&gt;member != (head); 	\
	     pos = list_entry(pos-&gt;member.next, typeof(*pos), member))</span></pre></div></div>

<p>在这个宏里面每次迭代pos所指向的都是struct list_head所对应的节点对象，该节点是通过list_entry()这个宏来获取的，接下来看它的定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)</span></pre></div></div>

<p>继续看下去就是container_of()这个宏，该宏在/include/linux/kernel.h里面定义。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)-&gt;member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})</span></pre></div></div>

<p>该宏的作用就是返回一个结构体中的某成员变量对应的包含它的结构体的指针。在这里ptr这个参数便是一个struct list_head类型，type为包含这个struct list_head成员变量的结构体的类型，member为ptr这个参数作为成员变量的变量名。</p>
<p>第一句话的作用比较有意思，它跟offsetof()这个函数采用了同样的方法，通过欺骗编译器，告诉它在内存地址0处有一个type类型的结构，然后取出member的数据类型（当然，在这里我们要讨论member是个struct list_head类型，该函数不失通用性），定义一个该类型的临时指针让它指向ptr。第二句话将_mptr转换成一个char*指针（大家都知道char是一个字节，当我没说），通过offsetof()宏取出member在结构体中的节字偏移，也就是_mptr在它的父结构体中的字节偏移，于是_mptr减去它偏移的字节便是它的父结构体的内存地址，做一下指针类型转换便得到了父结构体的地址，很有趣哈。</p>
<p>既然说到了这里，不防也看一下offsetof()这个函数的实现，可通过man 3 offsetof命令查看offsetof函数的介绍，这里我们简单看下它的实现。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define offsetof(TYPE, MEMBER) ((size_t) &amp;((TYPE *)0)-&gt;MEMBER)</span></pre></div></div>

<p>这一句话就可以搞定，TYPE为父类型的类型名，MEMBER为子类型的变量名，通过欺骗编译器在内存地址0处有一个TYPE类型的对象，然后强制转换成TYPE类型，取出MEMBER的地址，这个内存地址便是相对于0地址的偏移字节数，刚才我们已经假设TYPE类型在0地址处，这样取出的内存地址便是子类型在父类型中的偏移字节数，整个过程实际上就是对编译器的欺骗，我们并没有对这些不合法的内存地址进行写操作，所以该宏是安全的。</p>
<p><strong>3. 用于散列表中的双向链表。</strong></p>
<p>众所周知，在哈希节点冲突的时候需要把冲突的节点存储在链表中，如此说来便会有大量的链表存在（例如HASH路由表），这样链表头的8个字节对于Linux开发者来说可能也会成为一种负担，于是他们设计了另外一种链表，用来将链表头缩减为只有一个字节，看来面这种定义：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* 链表头 */</span>
<span style="color: #993333;">struct</span> hlist_head <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> hlist_node <span style="color: #339933;">*</span>first<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* 链表节点 */</span>
<span style="color: #993333;">struct</span> hlist_node <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> hlist_node <span style="color: #339933;">*</span>next<span style="color: #339933;">,</span> <span style="color: #339933;">**</span>pprev<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>链表头只有一个指向第一个节点的指针，链表节点分别有指向下一个和上一个节点的指针，如此上一个节点便不能和下一个节点使用相同的类型，因为第一个节点的上一个节点是一个struct hlist_head类型而不是hlist_node类型，于是这里巧妙地使用了指向上一个节点的next指针的地址作为上一个节点的指针，我们知道在获取上一个节点的时候一般是为了对它进行插入操作，而插入操作只需要操作上一个节点的next指针（hlist_head和hlist_node的指向下个节点的指针类型相同，这样便可以在插入和删除操作对于首节点和普通节点不失通用性了），而不需要操作pre指针，于是这种设计便足够使用了，为上一个节点的next指针赋值时只需要为*(node->pprev)赋值始可。参考如下函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* next must be != NULL */</span>
<span style="color: #993333;">static</span> <span style="color: #000000; font-weight: bold;">inline</span> <span style="color: #993333;">void</span> hlist_add_before<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> hlist_node <span style="color: #339933;">*</span>n<span style="color: #339933;">,</span>
					<span style="color: #993333;">struct</span> hlist_node <span style="color: #339933;">*</span>next<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	n<span style="color: #339933;">-&gt;</span>pprev <span style="color: #339933;">=</span> next<span style="color: #339933;">-&gt;</span>pprev<span style="color: #339933;">;</span>
	n<span style="color: #339933;">-&gt;</span>next <span style="color: #339933;">=</span> next<span style="color: #339933;">;</span>
	next<span style="color: #339933;">-&gt;</span>pprev <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>n<span style="color: #339933;">-&gt;</span>next<span style="color: #339933;">;</span>
	<span style="color: #339933;">*</span><span style="color: #009900;">&#40;</span>n<span style="color: #339933;">-&gt;</span>pprev<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> n<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>对于hlist链表操作的方法与list操作的方法大同小异，只不过在include/linux/list.h中没有定义对hlist逆向迭代的方法。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/intro-linux-kernel-list.html/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Tweets2PDF开发手记</title>
		<link>http://basiccoder.com/tweets2pdf-dev-notes.html</link>
		<comments>http://basiccoder.com/tweets2pdf-dev-notes.html#comments</comments>
		<pubDate>Tue, 01 Mar 2011 15:30:24 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[google code]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=804</guid>
		<description><![CDATA[之前一直想学一下Python却迟迟未曾下手，直到过年前几天好不容易闲下来，突然有个想法想把Twitter上发的tweets备份成PDF，我这个人一有了什么想法就会立马动手去做，于是正好借这个机会学下Python，没有太多时间，用Python比用C会节省很多开发时间，于是折腾了几天就有了这样一个小工具，<a href="http://code.google.com/p/tweets2pdf/" target="_blank">Tweets2PDF</a>，趁着还没把看过的东西忘光就先记录下写开发过程。

Twitter使用OAuth认证，其实之前对OAuth的原理也不是很了解，正好也借这个机会简单看了一下它的原理。

首先，程序需要向Twitter官网申请一个consumer token，每个客户端需要持有这个token，OAuth开始的时候向Twitter的Request Token URL发送请求来获取一个Request Token，当然HTTP请求中需要加上一些参数，这些参数中包含了Consumer Token相关的值，当然不止这些，具体的参数内容就去参考Twitter上OAuth的介绍或者去读OAuth的RFC。

获取到Request Token之后再根据这个Token生成一个URL，在浏览器中打开这个URL就可以对应用程序进行认证，有一个请求参数是Callback参数，它可以是一个URL，表示验证成功之后程序跳转到该URL，这个URL是Consumer的URL，这适合Web形式的第三方程序，桌面应用程序大家如果用过肯定都知道是需要一个PIN码的，如果要让Twitter返回这个PIN码，只需要把Callback的值设为oob即可，这样在认证之后Twitter后给出一个PIN码。

将这个PIN码由用户交回桌面应用程序，应用程序便再利用这个PIN码和Request Token再生成一个HTTP请求，这个请求成功之后会获取一个Access Token，这个Token是需要用户保存的，之后对于Twitter的访问只需要在请求中根据这个Token生成添加相应的HTTP请求参数即可，Twitter就会知道这个应用程序是经过认证的... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>之前一直想学一下Python却迟迟未曾下手，直到过年前几天好不容易闲下来，突然有个想法想把Twitter上发的tweets备份成PDF，我这个人一有了什么想法就会立马动手去做，于是正好借这个机会学下Python，没有太多时间，用Python比用C会节省很多开发时间，于是折腾了几天就有了这样一个小工具，<a href="http://code.google.com/p/tweets2pdf/" target="_blank">Tweets2PDF</a>，趁着还没把看过的东西忘光就先记录下写开发过程。</p>
<p>Twitter使用OAuth认证，其实之前对OAuth的原理也不是很了解，正好也借这个机会简单看了一下它的原理。</p>
<p>首先，程序需要向Twitter官网申请一个consumer token，每个客户端需要持有这个token，OAuth开始的时候向Twitter的Request Token URL发送请求来获取一个Request Token，当然HTTP请求中需要加上一些参数，这些参数中包含了Consumer Token相关的值，当然不止这些，具体的参数内容就去参考Twitter上OAuth的介绍或者去读OAuth的RFC。</p>
<p>获取到Request Token之后再根据这个Token生成一个URL，在浏览器中打开这个URL就可以对应用程序进行认证，有一个请求参数是Callback参数，它可以是一个URL，表示验证成功之后程序跳转到该URL，这个URL是Consumer的URL，这适合Web形式的第三方程序，桌面应用程序大家如果用过肯定都知道是需要一个PIN码的，如果要让Twitter返回这个PIN码，只需要把Callback的值设为oob即可，这样在认证之后Twitter后给出一个PIN码。</p>
<p>将这个PIN码由用户交回桌面应用程序，应用程序便再利用这个PIN码和Request Token再生成一个HTTP请求，这个请求成功之后会获取一个Access Token，这个Token是需要用户保存的，之后对于Twitter的访问只需要在请求中根据这个Token生成添加相应的HTTP请求参数即可，Twitter就会知道这个应用程序是经过认证的。</p>
<p>关于Twitter OAuth认证的详细资料见：<a href="http://dev.twitter.com/pages/auth" target="_blank">Authenticating Requests with OAuth</a> </p>
<p>OAuth认证过程中设计到对请求参数的处理比较繁琐，这方面的工作已经有人做过了，我没有必要做无用功，用了这个OAuth库： <a href="http://oauth.googlecode.com/svn/code/python/" target="_blank">http://oauth.googlecode.com/svn/code/python/</a> 不过这个库貌似有些问题，用它的Demo跑的时候会出问题，我对它做了一些修改，并在它之上完成了Twitter的OAuth认证。</p>
<p>不得不说OAuth这种认证机制是Web2.0的开放平台的必然产物，也是这种开放平台所必不可少的，它可以让不同的服务或者程序访问彼此开放的资源，这种开放平台策略也是互联网发展的趋势，可不明白的是为什么国内这么多SNS服务却没有一个能把开放地平台给做好，Twitter有非常多优秀的第三方客户端，可没有听说谁觉得有哪个什么第三方的新浪微博客户端好用，可能他们认为开放会带来风险，但开放更多地会带来商机，更多的人使用Twitter的第三方官户端去发推，但Twitter却越做越好，这是开放平台的魅力。</p>
<p>晕，扯远了，接着说Tweet2PDF，经过认证之后就可以很简单地通过Twitter提供的API来获取相关的数据了，Twitter的API非常丰富，用官方版本能做的事情用API都可以做，它的API除了每小时有访问限制（可能出于服务器负载的考虑）之外，它的API非常完整，当然说到API的限制我也遇到了问题，对于一般的第三方官户端程序来说API是够用的，但我要做的是抓取用户发过的所有Tweets将其生成PDF，这个过程中会一直不间段地大量抓取，难免会遇到API受限，这种情况下我只能进行重试，由最初的3次重试调整为现在的10次，基本可以保证用户正常抓取完所有的推后中止，但还有一个问题，Twitter的API只允许抓取最新的3200条Tweets，对于再旧的Tweets就抓取不到了，这便降低了这个软件的可用性，因此它只能用来给用户进行定期备份了。</p>
<p>抓取到数据之后便要生成PDF文件了，生成PDF用的是<a href="http://www.reportlab.com/software/opensource/" target="_blank">Reportlab</a>这个库，很幸运，它是用Python写的，用Python对它进行调用很完美，而且它有开源版本，Reportlab需要<a href="http://www.pythonware.com/products/pil/" target="_blank">PIL(Python Imaging Library)</a>的支持，来进行图片的处理，实现的细节就不说了，官方有很详细的文档，遇到几个小问题：</p>
<p>因为每生成一条Tweet都会在前面附上发推人的Profile Image，Reportlab对JPEG格式的图片支持很好，但如果图片是其它格式，例如PNG，当生成页数过多时在调用Build函数进行生成时便可能出错，这个错误是无法挽回的，也就是说前出抓取到的Tweet可能全部都前功尽弃了，我尝试了很多方法都没有解决这个问题，不确定是不是Reportlab自身的BUG，但我也有自己的解决方法，既然JPEG格式完美支持，那就在抓取到Image之后将先用PIL转换成JPEG再进行Build，这样便不会有问题了，不过对效率有少许的影响，因为转换的过程会花一些时间，图片一多的话会比较慢，但这个可以忽略，别人不会天天都拿来用，而且小数据量不会感觉到慢，偶尔过个几个月用一次多花点时间备份也无所谓了，只要保证可以正确生成就好了。</p>
<p>PDF可以不出错地生成之后我便发布了第一个Beta版本，虽然用的人不多，不过用过的人评价还不错，这也让我这个Python新手很受鼓舞，于是用PyGTK开发了GUI版本，之前用GTK给OPENFETION作的UI，对GTK比较熟悉，PyGTK就更简单不过了，花了一个晚上就把界面给做好了，当然很多细节的功能花了我一天多去完善，最后一天完全扔在了Windows打包上，太痛苦了。。。</p>
<p>接下来记录几个写Python几个小的Tips吧</p>
<p><strong>1. 给PDF页面加脚注和页码：</strong></p>
<p>Reportlab 对生首页和后续页面的处理，定义两个回调函数，如下：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> on_first_page<span style="color: black;">&#40;</span>canvas, doc<span style="color: black;">&#41;</span>:   
	canvas.<span style="color: black;">saveState</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>   
	canvas.<span style="color: black;">setFont</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'song'</span>, <span style="color: #ff4500;">9</span><span style="color: black;">&#41;</span>
	canvas.<span style="color: black;">setFillColor</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'grey'</span><span style="color: black;">&#41;</span>
	footer = <span style="color: #483d8b;">'Generated by Tweets2pdf at %s'</span> <span style="color: #66cc66;">%</span> \
	<span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%a %b %d %H:%M:%S %Y'</span>, <span style="color: #dc143c;">time</span>.<span style="color: black;">localtime</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
	footer += <span style="color: #483d8b;">' Powered by @levin108'</span>
	canvas.<span style="color: black;">drawCentredString</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>PAGE_WIDTH<span style="color: black;">&#41;</span>/<span style="color: #ff4500;">2</span>, <span style="color: #ff4500;">25</span>, footer<span style="color: black;">&#41;</span>
	canvas.<span style="color: black;">restoreState</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> 
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> on_later_pages<span style="color: black;">&#40;</span>canvas, doc<span style="color: black;">&#41;</span>:   
	canvas.<span style="color: black;">saveState</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>   
	canvas.<span style="color: black;">setFont</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'song'</span>, <span style="color: #ff4500;">9</span><span style="color: black;">&#41;</span>   
	canvas.<span style="color: black;">drawString</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>PAGE_WIDTH/<span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>-<span style="color: #ff4500;">5</span>, <span style="color: #ff4500;">25</span>, u<span style="color: #483d8b;">&quot;%d&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>doc.<span style="color: black;">page</span> - <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>   
	canvas.<span style="color: black;">restoreState</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
pdfdoc.<span style="color: black;">build</span><span style="color: black;">&#40;</span>elements, onFirstPage = on_first_page, onLaterPages = on_later_pages<span style="color: black;">&#41;</span></pre></div></div>

<p>两个回调函数分别表示对首页和后续页面的处理，然后将这两个回调函数传给Build函数进行生成，在这里我给首页加了脚标，给后续页底部加了页码，最终Build在生成PDF的过程中会给每个页面做相应的处理。</p>
<p><strong>2. 中文自动换行的设置</strong></p>
<p>下面这段代码是我从网上搜到的，具体我没去研究，给Paragraph这个Flowable控件重定wrap方法便可以Paragraph中的内容实现自动换行：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> wrap<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, availWidth, availHeight<span style="color: black;">&#41;</span>:
	<span style="color: #808080; font-style: italic;"># work out widths array for breaking</span>
	<span style="color: #008000;">self</span>.<span style="color: black;">width</span> = availWidth
	leftIndent = <span style="color: #008000;">self</span>.<span style="color: black;">style</span>.<span style="color: black;">leftIndent</span>
	first_line_width = availWidth - <span style="color: black;">&#40;</span>leftIndent+<span style="color: #008000;">self</span>.<span style="color: black;">style</span>.<span style="color: black;">firstLineIndent</span><span style="color: black;">&#41;</span> - <span style="color: #008000;">self</span>.<span style="color: black;">style</span>.<span style="color: black;">rightIndent</span>
	later_widths = availWidth - leftIndent - <span style="color: #008000;">self</span>.<span style="color: black;">style</span>.<span style="color: black;">rightIndent</span>
	<span style="color: #ff7700;font-weight:bold;">try</span>:
		<span style="color: #008000;">self</span>.<span style="color: black;">blPara</span> = <span style="color: #008000;">self</span>.<span style="color: black;">breakLinesCJK</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>first_line_width, later_widths<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
	<span style="color: #ff7700;font-weight:bold;">except</span>:
		<span style="color: #008000;">self</span>.<span style="color: black;">blPara</span> = <span style="color: #008000;">self</span>.<span style="color: black;">breakLines</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span>first_line_width, later_widths<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
	<span style="color: #008000;">self</span>.<span style="color: black;">height</span> = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">blPara</span>.<span style="color: black;">lines</span><span style="color: black;">&#41;</span> <span style="color: #66cc66;">*</span> <span style="color: #008000;">self</span>.<span style="color: black;">style</span>.<span style="color: black;">leading</span>
	<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">width</span>, <span style="color: #008000;">self</span>.<span style="color: black;">height</span><span style="color: black;">&#41;</span>
Paragraph.<span style="color: black;">wrap</span> = wrap</pre></div></div>

<p><strong>3. PIL转换转换图片格式</strong></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">img = Image.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">if</span> img.<span style="color: black;">mode</span> <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">'RGB'</span>:
	img = img.<span style="color: black;">convert</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'RGB'</span><span style="color: black;">&#41;</span>
img.<span style="color: black;">save</span><span style="color: black;">&#40;</span>outname, <span style="color: #483d8b;">'JPEG'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>其实只要第一行和最后一行便可以实现格式转换，但在其它格式转成JPEG的时候可能会有异常，所以需要先转换成RGB模式，然后再保存在JPEG。</p>
<p><strong>4. 关于在浏览器中打开URL</strong></p>
<p>之前一直都是用system(&#8216;xdg-open &#8230;&#8217;)来在浏览器中打开URL的，但这种方法不能保证跨平台，Python有自己的相关模块Webbrowser</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">webbrowser</span>
<span style="color: #dc143c;">webbrowser</span>.<span style="color: black;">open_new_tab</span><span style="color: black;">&#40;</span>URL<span style="color: black;">&#41;</span></pre></div></div>

<p>上面的语句表示在新标签页中打开URL</p>
<p><strong>5. Windows移植</strong><br />
关于在Windows系统中的移植到是没花太多功夫，主要是一些关于路径的设置，但打包折腾了近一天的时间对弄了个勉强可以在Win7下面跑的版本，p2exe问题太多了，不搞了，还是等有兴趣有经验的牛人来搞吧。</p>
<p>以上是我开发过程的总结，新手欢迎拍砖~~~</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/tweets2pdf-dev-notes.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>浅析linux kernel network之socket创建</title>
		<link>http://basiccoder.com/linux-kernel-network-socket-creation.html</link>
		<comments>http://basiccoder.com/linux-kernel-network-socket-creation.html#comments</comments>
		<pubDate>Sun, 20 Feb 2011 11:12:58 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=755</guid>
		<description><![CDATA[去年受<a href="http://twitter.com/colyli" target="_blank">@colyli</a>指点，决定花些时间读一些linux kernel network部分的代码，准备把阅读代码的过程记录下来，也希望能有大牛前来指点，下面就先写一下创建socket对象的过程：

首先，创建socket需要执行socket系统调用:
<pre lang="C">
int socket(int domain, int type, int protocol);
</pre>
该系统调用有3个参数，在内核中由SYSCALL_DEFINE3定义，具体代码如下：
<pre lang="C">
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
	int retval;
	struct socket *sock;
	int flags;

	flags = type &#038; ~SOCK_TYPE_MASK;
	if (flags &#038; ~(SOCK_CLOEXEC &#124; SOCK_NONBLOCK))
		return -EINVAL;
	type &#038;= SOCK_TYPE_MASK;

	if (SOCK_NONBLOCK != O_NONBLOCK &#038;& (flags &#038; SOCK_NONBLOCK))
		flags = (flags &#038; ~SOCK_NONBLOCK) &#124; O_NONBLOCK;

	retval = sock_create(family, type, protocol, &#038;sock);
	if (retval < 0)
		goto out;

	retval = sock_map_fd(sock, flags &#038; (O_CLOEXEC &#124; O_NONBLOCK));
	if (retval < 0)
		goto out_release;

out:
	/* It may be already another descriptor 8) Not kernel problem. */
	return retval;

out_release:
	sock_release(sock);
	return retval;
}
</pre... ]]></description>
			<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>去年受<a href="http://twitter.com/colyli" target="_blank">@colyli</a>指点，决定花些时间读一些linux kernel network部分的代码，准备把阅读代码的过程记录下来，也希望能有大牛前来指点，下面就先写一下创建socket对象的过程：</p>
<p>首先，创建socket需要执行socket系统调用:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> socket<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> domain<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> type<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> protocol<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>该系统调用有3个参数，在内核中由SYSCALL_DEFINE3定义，具体代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">SYSCALL_DEFINE3<span style="color: #009900;">&#40;</span>socket<span style="color: #339933;">,</span> <span style="color: #993333;">int</span><span style="color: #339933;">,</span> family<span style="color: #339933;">,</span> <span style="color: #993333;">int</span><span style="color: #339933;">,</span> type<span style="color: #339933;">,</span> <span style="color: #993333;">int</span><span style="color: #339933;">,</span> protocol<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> retval<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> socket <span style="color: #339933;">*</span>sock<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> flags<span style="color: #339933;">;</span>
&nbsp;
	flags <span style="color: #339933;">=</span> type <span style="color: #339933;">&amp;</span> ~SOCK_TYPE_MASK<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> ~<span style="color: #009900;">&#40;</span>SOCK_CLOEXEC <span style="color: #339933;">|</span> SOCK_NONBLOCK<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
	type <span style="color: #339933;">&amp;=</span> SOCK_TYPE_MASK<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>SOCK_NONBLOCK <span style="color: #339933;">!=</span> O_NONBLOCK <span style="color: #339933;">&amp;&amp;</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> SOCK_NONBLOCK<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		flags <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> ~SOCK_NONBLOCK<span style="color: #009900;">&#41;</span> <span style="color: #339933;">|</span> O_NONBLOCK<span style="color: #339933;">;</span>
&nbsp;
	retval <span style="color: #339933;">=</span> sock_create<span style="color: #009900;">&#40;</span>family<span style="color: #339933;">,</span> type<span style="color: #339933;">,</span> protocol<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>sock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>retval <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out<span style="color: #339933;">;</span>
&nbsp;
	retval <span style="color: #339933;">=</span> sock_map_fd<span style="color: #009900;">&#40;</span>sock<span style="color: #339933;">,</span> flags <span style="color: #339933;">&amp;</span> <span style="color: #009900;">&#40;</span>O_CLOEXEC <span style="color: #339933;">|</span> O_NONBLOCK<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>retval <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_release<span style="color: #339933;">;</span>
&nbsp;
out<span style="color: #339933;">:</span>
	<span style="color: #808080; font-style: italic;">/* It may be already another descriptor 8) Not kernel problem. */</span>
	<span style="color: #b1b100;">return</span> retval<span style="color: #339933;">;</span>
&nbsp;
out_release<span style="color: #339933;">:</span>
	sock_release<span style="color: #009900;">&#40;</span>sock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> retval<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>上述代码中首先做了一个类型检查，type即我们所熟知的SOCK_STREAM，SOCK_DGRAM等sock_type枚举，其取值范围为1-10，均位于type字段的低八位，而SOCK_CLOEXEC 和SOCK_NONBLOCK为于高位，SOCK_TYPE_MASK宏的值为0xF，该检查的作用是检查除了基本的socket类型和SOCK_CLOEXEC和SOCK_NONBLOCK选项之外是否设置了其它选项，如果有，则返回INVLAID。</p>
<p>之后的检查我理解为是一个兼容性检查，如果设置了SOCK_NONBLOCK选项，则不管SOCK_NONBLOCK的值是否定义为与O_NONBLOCK相同，均将socket的O_NONBLOCK位置位，而将SOCK_NONBLOCK位复位。</p>
<p>随便调用sock_create创建一个新的socket对象，之后再调用sock_map_fd将该socket对象影射为文件描述符retval，随即将其返回，从而函数得到一个socket描述符。sock_create函数直接调用了__sock_create函数来创建socket对象，具体看__sock_create函数的实现：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> __sock_create<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> family<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> type<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> protocol<span style="color: #339933;">,</span>
			 <span style="color: #993333;">struct</span> socket <span style="color: #339933;">**</span>res<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> kern<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> socket <span style="color: #339933;">*</span>sock<span style="color: #339933;">;</span>
	<span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> net_proto_family <span style="color: #339933;">*</span>pf<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>family <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span> family <span style="color: #339933;">&gt;=</span> NPROTO<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>EAFNOSUPPORT<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>type <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span> type <span style="color: #339933;">&gt;=</span> SOCK_MAX<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>EINVAL<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>family <span style="color: #339933;">==</span> PF_INET <span style="color: #339933;">&amp;&amp;</span> type <span style="color: #339933;">==</span> SOCK_PACKET<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">static</span> <span style="color: #993333;">int</span> warned<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>warned<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			warned <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
			printk<span style="color: #009900;">&#40;</span>KERN_INFO <span style="color: #ff0000;">&quot;%s uses obsolete (PF_INET,SOCK_PACKET)<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span>
			       current<span style="color: #339933;">-&gt;</span>comm<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		family <span style="color: #339933;">=</span> PF_PACKET<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	err <span style="color: #339933;">=</span> security_socket_create<span style="color: #009900;">&#40;</span>family<span style="color: #339933;">,</span> type<span style="color: #339933;">,</span> protocol<span style="color: #339933;">,</span> kern<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
&nbsp;
	sock <span style="color: #339933;">=</span> sock_alloc<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>sock<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>net_ratelimit<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
			printk<span style="color: #009900;">&#40;</span>KERN_WARNING <span style="color: #ff0000;">&quot;socket: no more sockets<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>ENFILE<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* Not exactly a match, but its the
				   closest posix thing */</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	sock<span style="color: #339933;">-&gt;</span>type <span style="color: #339933;">=</span> type<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #339933;">#ifdef CONFIG_MODULES</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>net_families<span style="color: #009900;">&#91;</span>family<span style="color: #009900;">&#93;</span> <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
		request_module<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;net-pf-%d&quot;</span><span style="color: #339933;">,</span> family<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span>
&nbsp;
	rcu_read_lock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	pf <span style="color: #339933;">=</span> rcu_dereference<span style="color: #009900;">&#40;</span>net_families<span style="color: #009900;">&#91;</span>family<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EAFNOSUPPORT<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>pf<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_release<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/*
	 * We will call the -&gt;create function, that possibly is in a loadable
	 * module, so we have to bump that loadable module refcnt first.
	 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>try_module_get<span style="color: #009900;">&#40;</span>pf<span style="color: #339933;">-&gt;</span>owner<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_release<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* Now protected by module ref count */</span>
	rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	err <span style="color: #339933;">=</span> pf<span style="color: #339933;">-&gt;</span>create<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> sock<span style="color: #339933;">,</span> protocol<span style="color: #339933;">,</span> kern<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_module_put<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/*
	 * Now to bump the refcnt of the [loadable] module that owns this
	 * socket at sock_release time we decrement its refcnt.
	 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>try_module_get<span style="color: #009900;">&#40;</span>sock<span style="color: #339933;">-&gt;</span>ops<span style="color: #339933;">-&gt;</span>owner<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_module_busy<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/*
	 * Now that we're done with the -&gt;create function, the [loadable]
	 * module can have its refcnt decremented
	 */</span>
	module_put<span style="color: #009900;">&#40;</span>pf<span style="color: #339933;">-&gt;</span>owner<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	err <span style="color: #339933;">=</span> security_socket_post_create<span style="color: #009900;">&#40;</span>sock<span style="color: #339933;">,</span> family<span style="color: #339933;">,</span> type<span style="color: #339933;">,</span> protocol<span style="color: #339933;">,</span> kern<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_sock_release<span style="color: #339933;">;</span>
	<span style="color: #339933;">*</span>res <span style="color: #339933;">=</span> sock<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
out_module_busy<span style="color: #339933;">:</span>
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EAFNOSUPPORT<span style="color: #339933;">;</span>
out_module_put<span style="color: #339933;">:</span>
	sock<span style="color: #339933;">-&gt;</span>ops <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	module_put<span style="color: #009900;">&#40;</span>pf<span style="color: #339933;">-&gt;</span>owner<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
out_sock_release<span style="color: #339933;">:</span>
	sock_release<span style="color: #009900;">&#40;</span>sock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
&nbsp;
out_release<span style="color: #339933;">:</span>
	rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">goto</span> out_sock_release<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>开始先进行安全性检查和兼容性检查，security_socket_create()是个空函数，可以忽略。之后调用sock_alloc()函数在VFS上分配一个struct socket对象，所有的协议类型创建socket时创建的均为这个对象，可以理解为是所有网络层socket的模板或者说父类，上层协议栈在初始化socket时会根据这个已创建好的struct socket对象创建并初始化一个struct sock对象，这个对象包含更多上层协议栈的详细信息。</p>
<p>接下来的net_families数据是一个全局变量，在系统初始化时在inet_init()函数内进行初始化，其定义如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> net_proto_family <span style="color: #339933;">*</span>net_families<span style="color: #009900;">&#91;</span>NPROTO<span style="color: #009900;">&#93;</span> __read_mostly<span style="color: #339933;">;</span></pre></div></div>

<p>每个协议族都会在该数据中对应一个net_proto_family结构体，当然，未实现的协议族中对应位置的结构体指针为空，我们只关心最常用的协议族即AF_INET，其值为2，而NPROTO的值等于AF_MAX，在2.6.37内核中值为38。刚才提到该数组在inet_init()中被初始化，查看该函数相关代码可知它调用sock_register()函数注册INET协议族，代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span>sock_register<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>inet_family_ops<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>其中inet_family_ops是一个全局静态变量，其定义如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> net_proto_family inet_family_ops <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	.<span style="color: #202020;">family</span> <span style="color: #339933;">=</span> PF_INET<span style="color: #339933;">,</span>
	.<span style="color: #202020;">create</span> <span style="color: #339933;">=</span> inet_create<span style="color: #339933;">,</span> <span style="color: #808080; font-style: italic;">/* 该函数在我们创建socket的过程中起着很关键的作用 */</span>
	.<span style="color: #202020;">owner</span>	<span style="color: #339933;">=</span> THIS_MODULE<span style="color: #339933;">,</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>再继续查看sock_register()函数，其代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> sock_register<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> net_proto_family <span style="color: #339933;">*</span>ops<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ops<span style="color: #339933;">-&gt;</span>family <span style="color: #339933;">&gt;=</span> NPROTO<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		printk<span style="color: #009900;">&#40;</span>KERN_CRIT <span style="color: #ff0000;">&quot;protocol %d &gt;= NPROTO(%d)<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> ops<span style="color: #339933;">-&gt;</span>family<span style="color: #339933;">,</span>
		       NPROTO<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	spin_lock<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>net_family_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>net_families<span style="color: #009900;">&#91;</span>ops<span style="color: #339933;">-&gt;</span>family<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EEXIST<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
		net_families<span style="color: #009900;">&#91;</span>ops<span style="color: #339933;">-&gt;</span>family<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> ops<span style="color: #339933;">;</span>
		err <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	spin_unlock<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>net_family_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	printk<span style="color: #009900;">&#40;</span>KERN_INFO <span style="color: #ff0000;">&quot;NET: Registered protocol family %d<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span> ops<span style="color: #339933;">-&gt;</span>family<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> err<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>代码很简单，即将net_proto_family对象插入到net_families数据对应的位置中，由此来完成对net_families的初始化。</p>
<p>OK,继续返回刚才的__sock_create()函数，看下面的代码：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#ifdef CONFIG_MODULES</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>net_families<span style="color: #009900;">&#91;</span>family<span style="color: #009900;">&#93;</span> <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
		request_module<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;net-pf-%d&quot;</span><span style="color: #339933;">,</span> family<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span></pre></div></div>

<p>如果编译时支持可安装模块，则首先检测net_families数组中对应的family是否存在，假设此处为AF_INET，如果不存在，即在系统初始化时没有通过sock_register()函数对该元素进行注册，则调用request_module()动态地安装模块net-pf-2。</p>
<p>再往下通过RCU机制获取net_families中对应的net_proto_family对象，RCU机制主要用于网络层和VFS中，关于RCU机制的更多细节可以参考<a href="http://twitter.com/gnawux" target="_blank">@gnawux</a>师兄的译文：<a href="http://wangxu.me/blog/?p=122" target="_blank">What is RCU, Fundamentally? </a></p>
<p>之后由注释可知增加该模块的引用计数，重要的函数为pf->create()，当协议族为AF_INET时，由以上可知，create函数为inet_create()，由这个函数进一步创建该socket，我们再看inet_create()函数的实现：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> inet_create<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> net <span style="color: #339933;">*</span>net<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> socket <span style="color: #339933;">*</span>sock<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> protocol<span style="color: #339933;">,</span>
		       <span style="color: #993333;">int</span> kern<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> sock <span style="color: #339933;">*</span>sk<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> inet_protosw <span style="color: #339933;">*</span>answer<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> inet_sock <span style="color: #339933;">*</span>inet<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> proto <span style="color: #339933;">*</span>answer_prot<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span> answer_flags<span style="color: #339933;">;</span>
	<span style="color: #993333;">char</span> answer_no_check<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> try_loading_module <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> err<span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;">/* 检查是否有加密字符串，如果没有则查检socket类型，
           只有TCP需要加密字符串，如果协议类型不是SOCK_RAW和SOCK_DGRAM的话，
            调用build_ehash_secret()函数创建一个加密字符串 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>unlikely<span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>inet_ehash_secret<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>sock<span style="color: #339933;">-&gt;</span>type <span style="color: #339933;">!=</span> SOCK_RAW <span style="color: #339933;">&amp;&amp;</span> sock<span style="color: #339933;">-&gt;</span>type <span style="color: #339933;">!=</span> SOCK_DGRAM<span style="color: #009900;">&#41;</span>
			build_ehash_secret<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;">/* 将socket状态设置为未连接 */</span>
	sock<span style="color: #339933;">-&gt;</span>state <span style="color: #339933;">=</span> SS_UNCONNECTED<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* Look for the requested type/protocol pair. */</span>
lookup_protocol<span style="color: #339933;">:</span>
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ESOCKTNOSUPPORT<span style="color: #339933;">;</span>
	rcu_read_lock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #808080; font-style: italic;">/* 遍历inetsw数组对应请求类型的链表元素，
           如果protocol不是通配符类型，即IPPROTO_IP，
           该宏的值为0，则在链表中搜索该协议类型，
           如未找到则返回协议类型不支持，如果是通配符类型，
           则选择一个适合的协议类型，至于inetsw数组，见下面分析 */</span>
	list_for_each_entry_rcu<span style="color: #009900;">&#40;</span>answer<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>inetsw<span style="color: #009900;">&#91;</span>sock<span style="color: #339933;">-&gt;</span>type<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> list<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
		err <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #808080; font-style: italic;">/* Check the non-wild match. */</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>protocol <span style="color: #339933;">==</span> answer<span style="color: #339933;">-&gt;</span>protocol<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>protocol <span style="color: #339933;">!=</span> IPPROTO_IP<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #808080; font-style: italic;">/* Check for the two wild cases. */</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>IPPROTO_IP <span style="color: #339933;">==</span> protocol<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				protocol <span style="color: #339933;">=</span> answer<span style="color: #339933;">-&gt;</span>protocol<span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>IPPROTO_IP <span style="color: #339933;">==</span> answer<span style="color: #339933;">-&gt;</span>protocol<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EPROTONOSUPPORT<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
        <span style="color: #808080; font-style: italic;">/* 动态加载相关协议模块 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>unlikely<span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>try_loading_module <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">2</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #808080; font-style: italic;">/*
			 * Be more specific, e.g. net-pf-2-proto-132-type-1
			 * (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)
			 */</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">++</span>try_loading_module <span style="color: #339933;">==</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span>
				request_module<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;net-pf-%d-proto-%d-type-%d&quot;</span><span style="color: #339933;">,</span>
					       PF_INET<span style="color: #339933;">,</span> protocol<span style="color: #339933;">,</span> sock<span style="color: #339933;">-&gt;</span>type<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #808080; font-style: italic;">/*
			 * Fall back to generic, e.g. net-pf-2-proto-132
			 * (net-pf-PF_INET-proto-IPPROTO_SCTP)
			 */</span>
			<span style="color: #b1b100;">else</span>
				request_module<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;net-pf-%d-proto-%d&quot;</span><span style="color: #339933;">,</span>
					       PF_INET<span style="color: #339933;">,</span> protocol<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">goto</span> lookup_protocol<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span>
			<span style="color: #b1b100;">goto</span> out_rcu_unlock<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EPERM<span style="color: #339933;">;</span>
        <span style="color: #808080; font-style: italic;">/* 众所周知，只有root权限用户才可以创始原始套接字，
           此处检查是否有权限创建SOCK_RAW类型的套接字，
           如果无权限，则返回-EPERM */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>sock<span style="color: #339933;">-&gt;</span>type <span style="color: #339933;">==</span> SOCK_RAW <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span>kern <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span>capable<span style="color: #009900;">&#40;</span>CAP_NET_RAW<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_rcu_unlock<span style="color: #339933;">;</span> 
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EAFNOSUPPORT<span style="color: #339933;">;</span>
       <span style="color: #808080; font-style: italic;">/* 检查协议类型是否支持 */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>inet_netns_ok<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> protocol<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto</span> out_rcu_unlock<span style="color: #339933;">;</span>
&nbsp;
	sock<span style="color: #339933;">-&gt;</span>ops <span style="color: #339933;">=</span> answer<span style="color: #339933;">-&gt;</span>ops<span style="color: #339933;">;</span>
	answer_prot <span style="color: #339933;">=</span> answer<span style="color: #339933;">-&gt;</span>prot<span style="color: #339933;">;</span>
	answer_no_check <span style="color: #339933;">=</span> answer<span style="color: #339933;">-&gt;</span>no_check<span style="color: #339933;">;</span>
	answer_flags <span style="color: #339933;">=</span> answer<span style="color: #339933;">-&gt;</span>flags<span style="color: #339933;">;</span>
	rcu_read_unlock<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	WARN_ON<span style="color: #009900;">&#40;</span>answer_prot<span style="color: #339933;">-&gt;</span>slab <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	err <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>ENOBUFS<span style="color: #339933;">;</span>
	sk <span style="color: #339933;">=</span> sk_alloc<span style="color: #009900;">&#40;</span>net<span style="color: #339933;">,</span> PF_INET<span style="color: #339933;">,</span> GFP_KERNEL<span style="color: #339933;">,</span> answer_prot<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>sk <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span>
		<span style="color: #b1b100;">goto
