<?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>Erlang非业余研究 &#187; Erlang探索</title>
	<atom:link href="http://blog.yufeng.info/archives/category/erlang/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.yufeng.info</link>
	<description>Erlang系统深度探索和应用</description>
	<lastBuildDate>Tue, 17 Jan 2012 06:05:54 +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>Erlang R15B 全新的observer</title>
		<link>http://blog.yufeng.info/archives/1969</link>
		<comments>http://blog.yufeng.info/archives/1969#comments</comments>
		<pubDate>Fri, 16 Dec 2011 19:37:33 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[observer]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1969</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Erlang R15B 全新的observer 新发布的R15B在亮点里面提到： There is a new GUI tool in the observer application which integrates pman, etop, appmon and tv into one tool. The tool does also contain functions for activating tracing in an easy way. 这个observer完全用wx重新改写过，界面操作速度非常块，整合了几个常用的观察工具，很方便用户，我们来尝鲜下： 上截图： 系统信息: Appmon: Etop: Tv: Pman: 界面看起来很清爽！ 祝玩得开心！ Post Footer automatically generated by wp-posturl [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1969">Erlang R15B 全新的observer</a></p>
</div>
<p>新发布的R15B在<a href="http://www.erlang.org/news/28">亮点</a>里面提到： </p>
<blockquote><p>There is a new GUI tool in the observer application which integrates pman, etop, appmon and tv into one tool. The tool does also contain functions for activating tracing in an easy way.</p></blockquote>
<p>这个observer完全用wx重新改写过，界面操作速度非常块，整合了几个常用的观察工具，很方便用户，我们来尝鲜下：</p>
<pre class="brush: bash; title: ; notranslate">
$ erl
Erlang R15B (erts-5.9) 1 [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9  (abort with ^G)
1&gt; observer:start().
ok
</pre>
<p>上截图：<br />
<span id="more-1969"></span><br />
系统信息:<br />
<a href="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-561.png"><img src="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-561.png" alt="" title="Picture 561" width="851" height="600" class="alignnone size-full wp-image-1970" /></a></p>
<p>Appmon:<br />
<a href="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-562.png"><img src="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-562.png" alt="" title="Picture 562" width="852" height="597" class="alignnone size-full wp-image-1971" /></a></p>
<p>Etop:<br />
<a href="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-563.png"><img src="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-563.png" alt="" title="Picture 563" width="851" height="603" class="alignnone size-full wp-image-1972" /></a></p>
<p>Tv:<br />
<a href="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-564.png"><img src="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-564.png" alt="" title="Picture 564" width="853" height="603" class="alignnone size-full wp-image-1973" /></a></p>
<p>Pman:<br />
<a href="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-565.png"><img src="http://blog.yufeng.info/wp-content/uploads/2011/12/Picture-565.png" alt="" title="Picture 565" width="856" height="603" class="alignnone size-full wp-image-1974" /></a></p>
<p>界面看起来很清爽！</p>
<p>祝玩得开心！</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1969/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Erlang虚拟机内存使用问题以及监控</title>
		<link>http://blog.yufeng.info/archives/452</link>
		<comments>http://blog.yufeng.info/archives/452#comments</comments>
		<pubDate>Tue, 06 Dec 2011 13:12:59 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[memsup]]></category>
		<category><![CDATA[os_mon]]></category>
		<category><![CDATA[rabbitmq]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=452</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Erlang虚拟机内存使用问题以及监控 Erlang虽然号称N个9的稳定性，但是在实际使用中还是有很多机会看到Erlang Crash了的，其中和VM相关的Crash 十有八九是由于内存使用过量，导致系统服务分配内存导致的。Erlang的内存分配测量是集中批发，零售给各个VM部件，包括用户进程和ETS数据库等内存消费大户。VM的内存增长是以fib方式上升的，一旦你的内存使用到G级别，那么之后的大量内存分配会以超过你预想的速度消费。 其中用户进程的消息队列是其中的罪魁祸首。 Erlang的虚拟机实现和设计上都没有阻止用户往一个进程的消息队里面扔消息，当消息的生产速度过快，超过进程的处理能力，这些消息就堆积起来，占用越来愈多的内存，最终导致VM崩溃。 那么我们如何来避免这种事情呢？既然不能阻止，那我们绕着走，通过监控来避免： 1. 监控消息队列的增长。 2. 监控VM整个内存的使用量。 一旦发现上面的二个情况的发生，我们可以采用的措施有以下几种： 1. 收紧内存。 在模块编写的时候，对于内存消费大户的模块，比如说cache等，注册接收内存紧张的信息。一旦收到此类威胁信号，马上释放掉不必要的内存，保命要紧。 2. 切断或者限制输入源，大部分的服务器程序都是网络服务，输入都是从用户的请求开始。 那我们可以暂时不接收或者减半处理用户的网络封包请求，使得工作进程能够及时处理滞留的消息。 3. 重新审视设计，为什么会有工作进程处理不过来的情况？是不是设计上服务有单点，能不能scale来处理。 这里重点看下如何发现内存紧张。Erts本事提供很多的信息源让用户来知道目前VM内部模块的内存使用情况，但是经常身在此山中，无法从更高的角度来看这个问题。 Erlang提供了memsup模块来让用户从操作系统的角度了解整个VM占用的VM情况。文档参看这里 memsup is a process which supervises the memory usage for the system and for individual processes. It is part of the OS_Mon application, see os_mon(6). Available for Unix, Windows and [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/452">Erlang虚拟机内存使用问题以及监控</a></p>
</div>
<p>Erlang虽然号称N个9的稳定性，但是在实际使用中还是有很多机会看到Erlang Crash了的，其中和VM相关的Crash 十有八九是由于内存使用过量，导致系统服务分配内存导致的。Erlang的内存分配测量是集中批发，零售给各个VM部件，包括用户进程和ETS数据库等内存消费大户。VM的内存增长是以fib方式上升的，一旦你的内存使用到G级别，那么之后的大量内存分配会以超过你预想的速度消费。</p>
<p>其中用户进程的消息队列是其中的罪魁祸首。 Erlang的虚拟机实现和设计上都没有阻止用户往一个进程的消息队里面扔消息，当消息的生产速度过快，超过进程的处理能力，这些消息就堆积起来，占用越来愈多的内存，最终导致VM崩溃。</p>
<p>那么我们如何来避免这种事情呢？既然不能阻止，那我们绕着走，通过监控来避免：<br />
1. 监控消息队列的增长。<br />
2. 监控VM整个内存的使用量。<br />
<span id="more-452"></span></p>
<p>一旦发现上面的二个情况的发生，我们可以采用的措施有以下几种：<br />
1. 收紧内存。 在模块编写的时候，对于内存消费大户的模块，比如说cache等，注册接收内存紧张的信息。一旦收到此类威胁信号，马上释放掉不必要的内存，保命要紧。</p>
<p>2. 切断或者限制输入源，大部分的服务器程序都是网络服务，输入都是从用户的请求开始。 那我们可以暂时不接收或者减半处理用户的网络封包请求，使得工作进程能够及时处理滞留的消息。</p>
<p>3. 重新审视设计，为什么会有工作进程处理不过来的情况？是不是设计上服务有单点，能不能scale来处理。</p>
<p>这里重点看下如何发现内存紧张。Erts本事提供很多的信息源让用户来知道目前VM内部模块的内存使用情况，但是经常身在此山中，无法从更高的角度来看这个问题。</p>
<p>Erlang提供了memsup模块来让用户从操作系统的角度了解整个VM占用的VM情况。文档参看<a href="http://www.erlang.org/doc/man/memsup.html">这里</a></p>
<blockquote><p>memsup is a process which supervises the memory usage for the system and for individual processes. It is part of the OS_Mon application, see os_mon(6). Available for Unix, Windows and VxWorks.</p>
<p>Periodically performs a memory check:<br />
    *If more than a certain amount of available system memory is allocated, as reported by the underlying operating system, the alarm {system_memory_high_watermark, []} is set.<br />
    *If any Erlang process Pid in the system has allocated more than a certain amount of total system memory, the alarm {process_memory_high_watermark, Pid} is set.</p></blockquote>
<p>通过预设内存使用的上限，当使用过度的时候，我们就可以收到这种报警消息，我们的应用就有了进一步处理的依据了。<br />
这方面 rabbitmq 处理的非常好，所以rabbitmq很稳健，代码也写得很清爽，各位可以参考下。</p>
<p>祝玩得开心！</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/452/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>gen_tcp接受链接时enfile的问题分析及解决</title>
		<link>http://blog.yufeng.info/archives/1851</link>
		<comments>http://blog.yufeng.info/archives/1851#comments</comments>
		<pubDate>Mon, 05 Dec 2011 04:16:50 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[网络编程]]></category>
		<category><![CDATA[enfile]]></category>
		<category><![CDATA[gdb]]></category>
		<category><![CDATA[systemtap]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1851</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: gen_tcp接受链接时enfile的问题分析及解决 最近我们为了安全方面的原因，在RDS服务器上做了个代理程序把普通的MYSQL TCP连接变成了SSL链接，在测试的时候，皓庭同学发现Tsung发起了几千个TCP链接后Erlang做的SSL PROXY老是报告gen_tcp:accept返回{error, enfile}错误。针对这个问题，我展开了如下的调查： 首先man accept手册，确定enfile的原因，因为gen_tcp肯定是调用accept系统调用的: EMFILE The per-process limit of open file descriptors has been reached. ENFILE The system limit on the total number of open files has been reached. 从文档来看是由于系统的文件句柄数用完了，我们顺着来调查下： 由于我们微调了系统的文件句柄，具体参考这里 老生常谈: ulimit问题及其影响， 这些参数看起来非常的正常。 先看下net/socket.c代码： 从代码来看，会返回ENFILE都是由于socket句柄分配不出来了，我们还是本着怀疑的态度来写个stap脚本来再次验证下： gen_tcp:accept报告{error, enfile}的时候，也没看到stap报异常，基本上可以排除操作系统的原因了，那么我们现在回到gen_tcp的实现来看。 gen_tcp是个port, 具体实现在erts/emulator/drivers/common/inet_drv.c，我们来看下有ENFILE的地方： 当 driver_create_port 失败的时候，gen_tcp返回ENFILE,看起来这次找对地方了。我们继续看下 driver_create_port的实现： 看下erts/emulator/beam/io.c： get_free_port() Basic Applications -> [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1851">gen_tcp接受链接时enfile的问题分析及解决</a></p>
</div>
<p>最近我们为了安全方面的原因，在RDS服务器上做了个代理程序把普通的MYSQL TCP连接变成了SSL链接，在测试的时候，皓庭同学发现Tsung发起了几千个TCP链接后Erlang做的SSL PROXY老是报告gen_tcp:accept返回{error, enfile}错误。针对这个问题，我展开了如下的调查：</p>
<p>首先man accept手册，确定enfile的原因，因为gen_tcp肯定是调用accept系统调用的:</p>
<blockquote><p>
EMFILE The per-process limit of open file descriptors has been reached.<br />
ENFILE The system limit on the total number of open files has been reached.
</p></blockquote>
<p>从文档来看是由于系统的文件句柄数用完了，我们顺着来调查下：</p>
<pre class="brush: bash; title: ; notranslate">
$ uname -r
2.6.18-164.el5
$ cat /proc/sys/fs/file-nr
2040    0       2417338
$ ulimit -n
65535
</pre>
<p>由于我们微调了系统的文件句柄，具体参考<a href=" http://blog.yufeng.info/archives/1380">这里</a> 老生常谈: ulimit问题及其影响， 这些参数看起来非常的正常。<br />
先看下net/socket.c代码：<br />
<span id="more-1851"></span></p>
<pre class="brush: cpp; title: ; notranslate">
static int sock_alloc_fd(struct file **filep)
{
        int fd;

        fd = get_unused_fd();
        if (likely(fd &gt;= 0)) {
                struct file *file = get_empty_filp();

                *filep = file;
                if (unlikely(!file)) {
                        put_unused_fd(fd);
                        return -ENFILE;
                }
        } else
                *filep = NULL;
        return fd;
}

static int __sock_create(int family, int type, int protocol, struct socket **res, int kern)
{
...
/*
 *      Allocate the socket and allow the family to set things up. if
 *      the protocol is 0, the family is instructed to select an appropriate
 *      default.
 */

        if (!(sock = sock_alloc())) {
                if (net_ratelimit())
                        printk(KERN_WARNING &quot;socket: no more sockets\n&quot;);
                err = -ENFILE;          /* Not exactly a match, but its the
                                           closest posix thing */
                goto out;
        }
...
}

asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen)
{
        struct socket *sock, *newsock;
        struct file *newfile;
        int err, len, newfd, fput_needed;
        char address[MAX_SOCK_ADDR];

        sock = sockfd_lookup_light(fd, &amp;err, &amp;fput_needed);
        if (!sock)
                goto out;

        err = -ENFILE;
        if (!(newsock = sock_alloc()))
                goto out_put;
...
}
</pre>
<p>从代码来看，会返回ENFILE都是由于socket句柄分配不出来了，我们还是本着怀疑的态度来写个stap脚本来再次验证下：</p>
<pre class="brush: bash; title: ; notranslate">
$ cat enfile.stp
probe kernel.function(&quot;kmem_cache_alloc&quot;).return,
          kernel.function(&quot;get_empty_filp&quot;).return{
  if($return == 0) { print_backtrace();exit();}
}
probe kernel.function(&quot;sock_alloc_fd&quot;).return {
  if($return &lt; 0) { print_backtrace(); exit();}
}
probe syscall.accept.return {
  if($return == -23) {print_backtrace(); exit();}
}
probe begin {
println(&quot;:~&quot;);
}
$ sudo stap enfile.stp
:~
</pre>
<p>gen_tcp:accept报告{error, enfile}的时候，也没看到stap报异常，基本上可以排除操作系统的原因了，那么我们现在回到gen_tcp的实现来看。<br />
gen_tcp是个port, 具体实现在erts/emulator/drivers/common/inet_drv.c，我们来看下有ENFILE的地方：</p>
<pre class="brush: cpp; title: ; notranslate">
/* Copy a descriptor, by creating a new port with same settings
 * as the descriptor desc.
 * return NULL on error (ENFILE no ports avail)
 */
static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
                                     ErlDrvTermData owner, int* err)
{
...
    /* The new port will be linked and connected to the original caller */
    port = driver_create_port(port, owner, &quot;tcp_inet&quot;, (ErlDrvData) copy_desc);
    if ((long)port == -1) {
        *err = ENFILE;
        FREE(copy_desc);
        return NULL;
    }
...
}
</pre>
<p>当 driver_create_port 失败的时候，gen_tcp返回ENFILE,看起来这次找对地方了。我们继续看下 driver_create_port的实现：<br />
看下erts/emulator/beam/io.c：</p>
<pre class="brush: cpp; title: ; notranslate">
/*
 * Driver function to create new instances of a driver
 * Historical reason: to be used with inet_drv for creating
 * accept sockets inorder to avoid a global table.
 */
ErlDrvPort
driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */
                   ErlDrvTermData pid,    /* Owner/Caller */
                   char* name,            /* Driver name */
                   ErlDrvData drv_data)   /* Driver data */
{
...
    rp = erts_pid2proc(NULL, 0, pid, ERTS_PROC_LOCK_LINK);
    if (!rp) {
        erts_smp_mtx_unlock(&amp;erts_driver_list_lock);
        return (ErlDrvTermData) -1;   /* pid does not exist */
    }
    if ((port_num = get_free_port()) &lt; 0) {
        errno = ENFILE;
        erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK);
        erts_smp_mtx_unlock(&amp;erts_driver_list_lock);
        return (ErlDrvTermData) -1;
    }

    port_id = make_internal_port(port_num);
    port = &amp;erts_port[port_num &amp; erts_port_tab_index_mask];
...
}
</pre>
<p>get_free_port()<0的时候就返回ENFILE错误。<br />
那我们看下port总的数目是如何设定的：</p>
<pre class="brush: cpp; title: ; notranslate">
/* initialize the port array */
void init_io(void)
{
&#8230;
 if (erts_sys_getenv(&quot;ERL_MAX_PORTS&quot;, maxports, &amp;maxportssize) == 0)
        erts_max_ports = atoi(maxports);
    else
        erts_max_ports = sys_max_files();

    if (erts_max_ports &gt; ERTS_MAX_PORTS)
        erts_max_ports = ERTS_MAX_PORTS;
    if (erts_max_ports &lt; 1024)
        erts_max_ports = 1024;

    if (erts_use_r9_pids_ports) {
        ports_bits = ERTS_R9_PORTS_BITS;
        if (erts_max_ports &gt; ERTS_MAX_R9_PORTS)
            erts_max_ports = ERTS_MAX_R9_PORTS;
    }

    port_extra_shift = erts_fit_in_bits(erts_max_ports &#8211; 1);
    port_num_mask = (1 &lt;&lt; ports_bits) &#8211; 1;
&#8230;
}
</pre>
<p>第一步：如果设定了ERL_MAX_PORTS环境变量，那么就按照用户设定的，否则就和ulimit -n 一样大。<br />
第二部：这个值不能大于ERTS_MAX_PORTS或者小于1024.</p>
<p>好了，我们基本上明白这个问题的原因了： erts_max_ports设定的太小.</p>
<p>我们再来验证下：<br />
gdb attach到我们的进程下<br />
(gdb) p erts_max_ports<br />
$1 = 4096<br />
原来是port设置有问题，导致上面的现象，看起来很绕的，Erlang的设计者认为PORT资源（相当于操作系统的IO资源）短缺如同操作系统的文件句柄短缺一样，达到system_limit就应该出ENFILE错误！</p>
<p>解决方案是: erl -env ERTS_MAX_PORTS NNNN  搞大点就好。</p>
<p>顺便再来强调下Erlang服务器几个关键的参数，来源：http://www.ejabberd.im/tuning，对服务器的设置很有帮助。</p>
<blockquote><p>This page lists several tricks to tune your ejabberd and Erlang installation for maximum performance gains. Remark that some of the described options are experimental.</p>
<p>Erlang Ports Limit: ERL_MAX_PORTS<br />
    Erlang consumes one port for every connection, either from a client or from another Jabber server. The option ERL_MAX_PORTS limits the number of concurrent connections and can be specified when starting ejabberd:</p>
<p>    erl -s ejabberd -env ERL_MAX_PORTS 5000 ...</p>
<p>Maximum Number of Erlang Processes: +P<br />
    Erlang consumes a lot of lightweight processes. If there is a lot of activity on ejabberd so that the maximum number of proccesses is reached, people will experiment greater latency times. As these processes are implemented in Erlang, and therefore not related to the operating system processes, you do not have to worry about allowing a huge number of them.</p>
<p>    erl -s ejabberd +P 250000 ...</p>
<p>ERL_FULLSWEEP_AFTER: Maximum number of collections before a forced fullsweep<br />
    The ERL_FULLSWEEP_AFTER option shrinks the size of the Erlang process after RAM intensive events. Note that this option may downgrade performance. Hence this option is only interesting on machines that host other services (webserver, mail) on which ejabberd does not receive constant load.</p>
<p>    erl -s ejabberd -env ERL_FULLSWEEP_AFTER 0 ...</p>
<p>Kernel Polling: +K true</p>
<p>    The kernel polling option requires that you have support for it in your kernel. By default, Erlang currently supports kernel polling under FreeBSD, Mac OS X, and Solaris. If you use Linux, check this newspost. Additionaly, you need to enable this feature while compiling Erlang.</p>
<p>    From Erlang documentation -> Basic Applications -> erts -> erl -> System Flags:</p>
<p>        +K true|false</p>
<p>        Enables or disables the kernel poll functionality if the emulator has kernel poll support. By default the kernel poll; functionality is disabled. If the emulator doesn't have kernel poll support and the +K flag is passed to the emulator, a warning is issued at startup.</p>
<p>    If you meet all requirements, you can enable it in this way:</p>
<p>    erl -s ejabberd +K true ...</p>
<p>Mnesia Tables to Disk<br />
    By default, ejabberd uses Mnesia as its database. In Mnesia you can configure each table in the database to be stored on RAM, on RAM and on disk, or only on disk. You can configure this in the web interface: Nodes -> 'mynode' -> DB Management. Modification of this option will consume some memory and CPU time.<br />
Number of Concurrent ETS and Mnesia Tables: ERL_MAX_ETS_TABLES<br />
    The number of concurrent ETS and Mnesia tables is limited. When the limit is reached, errors will appear in the logs:</p>
<p>    ** Too many db tables **</p>
<p>    You can safely increase this limit when starting ejabberd. It impacts memory consumption but the difference will be quite small.</p>
<p>    erl -s ejabberd -env ERL_MAX_ETS_TABLES 20000 ...</p>
</blockquote>
<p>小结：很多问题是很绕的，要多方面考虑验证。</p>
<p>祝玩得开心！</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1851/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>很容易忽略的ETS表个数限制问题</title>
		<link>http://blog.yufeng.info/archives/1847</link>
		<comments>http://blog.yufeng.info/archives/1847#comments</comments>
		<pubDate>Wed, 30 Nov 2011 07:26:44 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[ets]]></category>
		<category><![CDATA[system_limit]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1847</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: 很容易忽略的ETS表个数限制问题 最近经常碰到ets表使用的数目超过系统的限制导致Erlang应用异常的案例。 比如说神锋同学报告说在ssh模块里面，最多只能打开500个左右链接，系统空闲的很，但是无法继续加大链接。 浩庭同学报告说mnesia的事务只能开1千多，多了就上不去了。这些问题看起来没有关联。但是其实和ets都有很大的关系,而且会报system_limit错误。 Erlang系统的限制见这里： http://www.erlang.org/doc/efficiency_guide/advanced.html#id215064 其中和ets相关的： Ets table 内存消耗 Initially 768 words + the size of each element (6 words + size of Erlang data). The table will grow when necessary. Ets-tables The default is 1400, can be changed with the environment variable ERL_MAX_ETS_TABLES. 这个值非常的偏保守，我们通常的服务器都有几十G的内存，因为ETS基本是消耗内存的，所以我们不介意都开大点。 回到前面的问题，ssh出问题的原因是它每个链接需要3个ets, 而mnesia一个事务也要消耗1个ets表。 知道了问题的本质就很容易解决问题： erl -env ERL_MAX_ETS_TABLES [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1847">很容易忽略的ETS表个数限制问题</a></p>
</div>
<p>最近经常碰到ets表使用的数目超过系统的限制导致Erlang应用异常的案例。 比如说神锋同学报告说在ssh模块里面，最多只能打开500个左右链接，系统空闲的很，但是无法继续加大链接。 浩庭同学报告说mnesia的事务只能开1千多，多了就上不去了。这些问题看起来没有关联。但是其实和ets都有很大的关系,而且会报system_limit错误。</p>
<p>Erlang系统的限制见<a href=" http://www.erlang.org/doc/efficiency_guide/advanced.html#id215064">这里</a>： http://www.erlang.org/doc/efficiency_guide/advanced.html#id215064<br />
其中和ets相关的：</p>
<blockquote><p>Ets table 内存消耗<br />
	Initially 768 words + the size of each element (6 words + size of Erlang data). The table will grow when necessary.</p></blockquote>
<blockquote><p>Ets-tables<br />
The default is 1400, can be changed with the environment variable ERL_MAX_ETS_TABLES. </p></blockquote>
<p>这个值非常的偏保守，我们通常的服务器都有几十G的内存，因为ETS基本是消耗内存的，所以我们不介意都开大点。</p>
<p>回到前面的问题，ssh出问题的原因是它每个链接需要3个ets, 而mnesia一个事务也要消耗1个ets表。<br />
<span id="more-1847"></span><br />
知道了问题的本质就很容易解决问题：<br />
erl -env ERL_MAX_ETS_TABLES NNNNN就好了。</p>
<p>再来顺手看下ejabberd的配置文件的说明：</p>
<blockquote><p># ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables<br />
#<br />
# The number of concurrent ETS and Mnesia tables is limited. When the limit is<br />
# reached, errors will appear in the logs:<br />
#   ** Too many db tables **<br />
# You can safely increase this limit when starting ejabberd. It impacts memory<br />
# consumption but the difference will be quite small.<br />
#<br />
# Default: 1400<br />
#<br />
#ERL_MAX_ETS_TABLES=1400
</p></blockquote>
<p>但是如何知道N设成多大比较合适呢？</p>
<p>erl shell下按下CTRL+C 再按下i就告诉你现在这些核心资源包括ets的使用情况,具体见以下代码。 你一看用的差不多了，就搞大点。</p>
<pre class="brush: cpp; title: ; notranslate">
void
info(int to, void *to_arg)
{
    erts_memory(&amp;to, to_arg, NULL, THE_NON_VALUE);
    atom_info(to, to_arg);
    module_info(to, to_arg);
    export_info(to, to_arg);
    register_info(to, to_arg);
    erts_fun_info(to, to_arg);
    erts_node_table_info(to, to_arg);
    erts_dist_table_info(to, to_arg);
    erts_allocated_areas(&amp;to, to_arg, NULL);
    erts_allocator_info(to, to_arg);
}
</pre>
<p>其实还有一个更简单的方法用crashdump viewer看下实际系统中有多少进程，每个进程消耗多少port和ets表。<br />
我来说下大概的步骤：<br />
1. 产生crashdump： 在实际运行的系统中按下CTRL+C再按大写的A，看到系统退出，生成我们系统运行期的快照。<br />
2. 运行webtool: webtool:start()  他会告诉我们web地址,打开浏览器，打开该地址。<br />
3. 在webtool中打开crashdump viewer模块，加载我们的crashdump文件进行分析，得到系统运行期的友好的解释。<br />
4. 在进程这一栏里面可以看到每个进程的状态和使用的资源。</p>
<p>根据这些信息稍微估算下系统需要开多少process和port，提前规划好。</p>
<p>祝玩的开心！</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1847/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Erlang open_port极度影响性能的因素</title>
		<link>http://blog.yufeng.info/archives/1783</link>
		<comments>http://blog.yufeng.info/archives/1783#comments</comments>
		<pubDate>Tue, 22 Nov 2011 03:30:42 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[调优]]></category>
		<category><![CDATA[ERL_NO_VFORK]]></category>
		<category><![CDATA[open_port]]></category>
		<category><![CDATA[port]]></category>
		<category><![CDATA[vfork]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1783</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Erlang open_port极度影响性能的因素 Erlang的port相当于系统的IO，打开了Erlang世界通往外界的通道，可以很方便的执行外部程序。 但是open_port的性能对整个系统来讲非常的重要，我就带领大家看看open_port影响性能的因素。 首先看下open_port的文档： {spawn, Command} Starts an external program. Command is the name of the external program which will be run. Command runs outside the Erlang work space unless an Erlang driver with the name Command is found. If found, that driver will be started. A driver runs in the [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1783">Erlang open_port极度影响性能的因素</a></p>
</div>
<p>Erlang的port相当于系统的IO，打开了Erlang世界通往外界的通道，可以很方便的执行外部程序。 但是open_port的性能对整个系统来讲非常的重要，我就带领大家看看open_port影响性能的因素。</p>
<p>首先看下open_port的文档：</p>
<blockquote><p>{spawn, Command}</p>
<p>    Starts an external program. Command is the name of the external program which will be run. Command runs outside the Erlang work space unless an Erlang driver with the name Command is found. If found, that driver will be started. A driver runs in the Erlang workspace, which means that it is linked with the Erlang runtime system.</p>
<p>    When starting external programs on Solaris, the system call vfork is used in preference to fork for performance reasons, although it has a history of being less robust. If there are problems with using vfork, setting the environment variable ERL_NO_VFORK to any value will cause fork to be used instead.</p>
<p>    For external programs, the PATH is searched (or an equivalent method is used to find programs, depending on operating system). This is done by invoking the shell och certain platforms. The first space separated token of the command will be considered as the name of the executable (or driver). This (among other things) makes this option unsuitable for running programs having spaces in file or directory names. Use {spawn_executable, Command} instead if spaces in executable file names is desired.
</p></blockquote>
<p>open_port一个外部程序的时候流程大概是这样的：beam.smp先vfork, 子进程调用child_setup程序，做进一步的清理操作。 清理完成后才真正exec我们的外部程序。</p>
<p>再来看下open_port实现的代码：<br />
<span id="more-1783"></span></p>
<pre class="brush: cpp; title: ; notranslate">
// sys.c:L1352
static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
{
...
#if !DISABLE_VFORK
    int no_vfork;
    size_t no_vfork_sz = sizeof(no_vfork);

    no_vfork = (erts_sys_getenv(&quot;ERL_NO_VFORK&quot;,
                                (char *) &amp;no_vfork,
                                &amp;no_vfork_sz) &gt;= 0);
#endif
...
else { /* Use vfork() */
        char **cs_argv= erts_alloc(ERTS_ALC_T_TMP,(CS_ARGV_NO_OF_ARGS + 1)*
                                   sizeof(char *));
        char fd_close_range[44];                  /* 44 bytes are enough to  */
        char dup2_op[CS_ARGV_NO_OF_DUP2_OPS][44]; /* hold any &quot;%d:%d&quot; string */
                                                  /* on a 64-bit machine.    */

        /* Setup argv[] for the child setup program (implemented in
           erl_child_setup.c) */
        i = 0;
        if (opts-&gt;use_stdio) {
            if (opts-&gt;read_write &amp; DO_READ){
                /* stdout for process */
                sprintf(&amp;dup2_op[i++][0], &quot;%d:%d&quot;, ifd[1], 1);
                if(opts-&gt;redir_stderr)
                    /* stderr for process */
                    sprintf(&amp;dup2_op[i++][0], &quot;%d:%d&quot;, ifd[1], 2);
            }
            if (opts-&gt;read_write &amp; DO_WRITE)
                /* stdin for process */
                sprintf(&amp;dup2_op[i++][0], &quot;%d:%d&quot;, ofd[0], 0);
        } else {        /* XXX will fail if ofd[0] == 4 (unlikely..) */
            if (opts-&gt;read_write &amp; DO_READ)
                sprintf(&amp;dup2_op[i++][0], &quot;%d:%d&quot;, ifd[1], 4);
            if (opts-&gt;read_write &amp; DO_WRITE)
                sprintf(&amp;dup2_op[i++][0], &quot;%d:%d&quot;, ofd[0], 3);
        }
        for (; i &lt; CS_ARGV_NO_OF_DUP2_OPS; i++)
            strcpy(&amp;dup2_op[i][0], &quot;-&quot;);
        sprintf(fd_close_range, &quot;%d:%d&quot;, opts-&gt;use_stdio ? 3 : 5, max_files-1);

        cs_argv[CS_ARGV_PROGNAME_IX] = child_setup_prog;
        cs_argv[CS_ARGV_WD_IX] = opts-&gt;wd ? opts-&gt;wd : &quot;.&quot;;
        cs_argv[CS_ARGV_UNBIND_IX] = erts_sched_bind_atvfork_child(unbind);
        cs_argv[CS_ARGV_FD_CR_IX] = fd_close_range;
        for (i = 0; i &lt; CS_ARGV_NO_OF_DUP2_OPS; i++)
            cs_argv[CS_ARGV_DUP2_OP_IX(i)] = &amp;dup2_op[i][0];
        if (opts-&gt;spawn_type == ERTS_SPAWN_EXECUTABLE) {
            int num = 0;
            int j = 0;
            if (opts-&gt;argv != NULL) {
                for(; opts-&gt;argv[num] != NULL; ++num)
                    ;
            }
            cs_argv = erts_realloc(ERTS_ALC_T_TMP,cs_argv, (CS_ARGV_NO_OF_ARGS + 1 + num + 1) * sizeof(char *));
            cs_argv[CS_ARGV_CMD_IX] = &quot;-&quot;;
            cs_argv[CS_ARGV_NO_OF_ARGS] = cmd_line;
            if (opts-&gt;argv != NULL) {
                for (;opts-&gt;argv[j] != NULL; ++j) {
                    if (opts-&gt;argv[j] == erts_default_arg0) {
                        cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = cmd_line;
                    } else {
                        cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = opts-&gt;argv[j];
                    }
                }
            }
            cs_argv[CS_ARGV_NO_OF_ARGS + 1 + j] = NULL;
        } else {
            cs_argv[CS_ARGV_CMD_IX] = cmd_line; /* Command */
            cs_argv[CS_ARGV_NO_OF_ARGS] = NULL;
        }
        DEBUGF((&quot;Using vfork\n&quot;));
        pid = vfork();

	if (pid == 0) {
	    /* The child! */

	    /* Observe!
             * OTP-4389: The child setup program (implemented in
             * erl_child_setup.c) will perform the necessary setup of the
             * child before it execs to the user program. This because
             * vfork() only allow an *immediate* execve() or _exit() in the
             * child.
             */
            execve(child_setup_prog, cs_argv, new_environ);
	    _exit(1);
        }
        erts_free(ERTS_ALC_T_TMP,cs_argv);
...
}
</pre>
<p>在支持vfork的系统下，比如说linux，除非禁止，默认会采用vfork来执行child_setup来调用外部程序。<br />
看下vfork的文档：</p>
<blockquote><p>       vfork() differs from fork() in that the parent is suspended until the child makes a call to execve(2) or _exit(2).  The child shares all  memory<br />
       with  its  parent,  including  the  stack,  until  execve() is issued by the child.  The child must not return from the current function or call<br />
       exit(), but may call _exit().</p></blockquote>
<p>vfork的时候beam.smp整个进程会被阻塞，所以这里是个很重要的性能影响点。</p>
<p>我们再看下erl_child_setup.c的代码：</p>
<pre class="brush: cpp; title: ; notranslate">
// erl_child_setup.c:111
// 1.  取消绑定
if (strcmp(&quot;false&quot;, argv[CS_ARGV_UNBIND_IX]) != 0)
	if (erts_unbind_from_cpu_str(argv[CS_ARGV_UNBIND_IX]) != 0)
            return 1;
// 2.  复制句柄
 for (i = 0; i &lt; CS_ARGV_NO_OF_DUP2_OPS; i++) {
        if (argv[CS_ARGV_DUP2_OP_IX(i)][0] == '-'
            &amp;&amp; argv[CS_ARGV_DUP2_OP_IX(i)][1] == '&#92;&#48;')
            break;
        if (sscanf(argv[CS_ARGV_DUP2_OP_IX(i)], &quot;%d:%d&quot;, &amp;from, &amp;to) != 2)
            return 1;
        if (dup2(from, to) &lt; 0)
            return 1;
    }
// 3. 关闭句柄
if (sscanf(argv[CS_ARGV_FD_CR_IX], &quot;%d:%d&quot;, &amp;from, &amp;to) != 2)
        return 1;
    for (i = from; i &lt;= to; i++)
        (void) close(i);

// 4. 调用外部程序
if (erts_spawn_executable) {
        if (argv[CS_ARGV_NO_OF_ARGS + 1] == NULL) {
            execl(argv[CS_ARGV_NO_OF_ARGS],argv[CS_ARGV_NO_OF_ARGS],
                  (char *) NULL);
        } else {
            execv(argv[CS_ARGV_NO_OF_ARGS],&amp;(argv[CS_ARGV_NO_OF_ARGS + 1]));
        }
    } else {
        execl(&quot;/bin/sh&quot;, &quot;sh&quot;, &quot;-c&quot;, argv[CS_ARGV_CMD_IX], (char *) NULL);
    }
...
</pre>
<p>这是一个非常流程多的过程，而且1，2，3这三个步骤都非常的耗时。 特别是3对于一个繁忙的IO服务器来讲，会打开大量的句柄，可能都有几十万，关闭这么多的句柄会是个灾难。</p>
<p>我们来演习下这个流程和具体的性能数字：<br />
首先我们设计个open_port的场景，服务器打开768个socke句柄，再运行cat外部程序。</p>
<pre class="brush: erlang; title: ; notranslate">
$ cat demo.erl
-module(demo).
-compile(export_all).

start()-&gt;
    _ = [gen_udp:open(0) || _ &lt;- lists:seq(1,768)],
    Port = open_port({spawn, &quot;/bin/cat&quot;}, [in, out, {line, 128}]),
    port_close(Port),
    ok.
</pre>
<p>我们再准备个stap脚本，用来分析这些行为和性能数字：</p>
<pre class="brush: bash; title: ; notranslate">
$ cat demo.stp
global t0, t1, t2

probe process(&quot;beam.smp&quot;).function(&quot;spawn_start&quot;) {
        printf(&quot;spawn %\s\n&quot;, user_string($name))
        t0 = gettimeofday_us()
}

probe process(&quot;beam.smp&quot;).statement(&quot;*@sys.c:1607&quot;) {
        t1 = gettimeofday_ns()
}

probe process(&quot;beam.smp&quot;).statement(&quot;*@sys.c:1627&quot;) {
        printf(&quot;vfork take %d ns\n&quot;, gettimeofday_ns() - t1);
}

probe process(&quot;child_setup&quot;).function(&quot;main&quot;) {
        t2 = gettimeofday_us()
}

probe process(&quot;child_setup&quot;).statement(&quot;*@erl_child_setup.c:111&quot;) {
        t3 = gettimeofday_us()
        printf(&quot;spawn take %d us, child_setup take %d us\n&quot;, t3 - t0, t3 - t2)
}

probe syscall.execve {
        printf(&quot;%s, arg %s\n&quot;, name, argstr)
}

probe syscall.fork {
        printf(&quot;%s, arg %s\n&quot;, name, argstr)
}

probe begin {
        println(&quot;)&quot;);
</pre>
<p>我们在一个终端下运行stap脚本观察行为：</p>
<pre class="brush: bash; title: ; notranslate">
$ erlc demo.erl
$ PATH=otp/bin/x86_64-unknown-linux-gnu/:$PATH sudo stap demo.stp
)
fork, arg
execve, arg otp/bin/erl
fork, arg
fork, arg
fork, arg
execve, arg /bin/sed &quot;s/.*\\///&quot;
execve, arg /home/chuba/otp/bin/x86_64-unknown-linux-gnu/erlexec
execve, arg /home/chuba/otp/bin/x86_64-unknown-linux-gnu/beam.smp &quot;--&quot; &quot;-root&quot; &quot;/home/chuba/otp&quot; &quot;-progname&quot; &quot;erl&quot; &quot;--&quot; &quot;-home&quot; &quot;/home/chuba&quot; &quot;--&quot;
clone, arg .
..
clone, arg CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
spawn inet_gethost 4
fork, arg
execve, arg /home/chuba/otp/bin/x86_64-unknown-linux-gnu/child_setup &quot;FFFF&quot; &quot;.&quot; &quot;exec inet_gethost 4 &quot; &quot;3:327679&quot; &quot;8:1&quot; &quot;9:0&quot; &quot;-&quot;
vfork take 8487 ns
spawn take 173707 us, child_setup take 94535 us
execve, arg /bin/sh &quot;-c&quot; &quot;exec inet_gethost 4 &quot;
execve, arg /home/chuba/otp/bin/x86_64-unknown-linux-gnu/inet_gethost &quot;4&quot;
fork, arg
clone, arg CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
clone, arg CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
spawn /bin/cat
fork, arg
execve, arg /home/chuba/otp/bin/x86_64-unknown-linux-gnu/child_setup &quot;FFFF&quot; &quot;.&quot; &quot;exec /bin/cat&quot; &quot;3:327679&quot; &quot;2312:1&quot; &quot;2313:0&quot; &quot;-&quot;
vfork take 5298 ns
spawn take 180974 us, child_setup take 101646 us
execve, arg /bin/sh &quot;-c&quot; &quot;exec /bin/cat&quot;
execve, arg /bin/cat
spawn /bin/cat
fork, arg
execve, arg /home/chuba/otp/bin/x86_64-unknown-linux-gnu/child_setup &quot;FFFF&quot; &quot;.&quot; &quot;exec /bin/cat&quot; &quot;3:327679&quot; &quot;3080:1&quot; &quot;3081:0&quot; &quot;-&quot;
vfork take 8929 ns
spawn take 169569 us, child_setup take 90163 us
execve, arg /bin/sh &quot;-c&quot; &quot;exec /bin/cat&quot;
execve, arg /bin/cat
...
</pre>
<p>在另外一个终端下运行我们的测试案例：</p>
<pre class="brush: bash; title: ; notranslate">
$ otp/bin/erl
Erlang R14B04 (erts-5.8.5) 1[/source] [64-bit] [smp:16:16] [rq:16] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.8.5  (abort with ^G)
1&gt; demo:start().
ok
2&gt; demo:start().
ok
3&gt;
</pre>
<p>我们可以看到二次执行的开销差不多：<br />
vfork take 8929 ns<br />
spawn take 169569 us, child_setup take 90163 us</p>
<p>从实验得来的数字来看：<br />
vfork需要阻塞beam.smp 8个us时间，而整个spawn下来要169ms, 其中 child_setup关闭句柄等等花了90ms， 数字无情的告诉我们这些性能杀手不容忽视。</p>
<p>解决方案：<br />
1. 改用fork避免阻塞beam.smp, erl -env ERL_NO_VFORK 1<br />
2. 减少文件句柄,如果确实需要大量的open_port让另外一个专注的节点来做。</p>
<p>祝玩得开心!</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1783/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Erlang R15的内存delayed dealloc特性对消息密集型程序的影响</title>
		<link>http://blog.yufeng.info/archives/1806</link>
		<comments>http://blog.yufeng.info/archives/1806#comments</comments>
		<pubDate>Mon, 21 Nov 2011 09:00:43 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[调优]]></category>
		<category><![CDATA[delayed dealloc]]></category>
		<category><![CDATA[R15]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1806</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Erlang R15的内存delayed dealloc特性对消息密集型程序的影响 在新的NUMA体系结构下，每个CPU都有自己的本地内存，如果要访问其他CPU的内存，那算remote了，要走CPU之间的QPI通道，通常这样速度会有40%的下降。 那么对于多线程的程序来讲，这个硬件的变化对软件也有很大的影响。在多线程程序里面，通常一个线程会为一个对象分配内存，然后把这个对象传递到不同的线程去使用，最后由其他线程释放内存。而这二个线程可能在不同的CPU上运行，这个场景很普遍，比如说Erlang的消息机制。如果谁创建谁释放对象，提高内存倒腾的效率，那么对于消息密集型程序会有很多帮助。 R15的最大的运行期优化见： 这里 这个特性也就是之前声称的delayed dealloc特性 对照下OTP团队之前的规划： 目前规划里面的1，2，3，4在R15里面都已经实现了。 Optimize memory allocation A number of memory allocation optimizations have been implemented. Most optimizations reduce contention caused by synchronization between threads during allocation and deallocation of memory. Most notably: * Synchronization of memory management in scheduler specific allocator instances has been [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1806">Erlang R15的内存delayed dealloc特性对消息密集型程序的影响</a></p>
</div>
<p>在新的NUMA体系结构下，每个CPU都有自己的本地内存，如果要访问其他CPU的内存，那算remote了，要走CPU之间的QPI通道，通常这样速度会有40%的下降。<br />
那么对于多线程的程序来讲，这个硬件的变化对软件也有很大的影响。在多线程程序里面，通常一个线程会为一个对象分配内存，然后把这个对象传递到不同的线程去使用，最后由其他线程释放内存。而这二个线程可能在不同的CPU上运行，这个场景很普遍，比如说Erlang的消息机制。如果谁创建谁释放对象，提高内存倒腾的效率，那么对于消息密集型程序会有很多帮助。 </p>
<p>R15的最大的运行期优化见： <a href="https://github.com/erlang/otp/commit/a67e91e658bdbba24fcc3c79b06fdf10ff830bc9">这里</a><br />
这个特性也就是之前声称的delayed dealloc特性<br />
对照下OTP团队之前的规划：<a href="http://blog.yufeng.info/wp-content/uploads/2011/11/sn3.jpg"><img src="http://blog.yufeng.info/wp-content/uploads/2011/11/sn3.jpg" alt="" title="Next steps with SMP and Erlang" width="607" height="415" class="alignnone size-full wp-image-1811" /></a></p>
<p>目前规划里面的1，2，3，4在R15里面都已经实现了。</p>
<blockquote><p>
Optimize memory allocation</p>
<p>A number of memory allocation optimizations have been implemented. Most<br />
optimizations reduce contention caused by synchronization between<br />
threads during allocation and deallocation of memory. Most notably:<br />
* Synchronization of memory management in scheduler specific allocator<br />
  instances has been rewritten to use lock-free synchronization.<br />
* Synchronization of memory management in scheduler specific<br />
  pre-allocators has been rewritten to use lock-free synchronization.<br />
* The &#8216;mseg_alloc&#8217; memory segment allocator now use scheduler specific<br />
  instances instead of one instance. Apart from reducing contention<br />
  this also ensures that memory allocators always create memory<br />
  segments on the local NUMA node on a NUMA system.
</p></blockquote>
<p>我们来尝鲜演示下，首先设计个消息密集型的程序：<br />
<span id="more-1806"></span><br />
抱歉由于刚才的POST测试案例设计不合理，重新设计过,敬请期待。。。</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1806/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux下pipe使用注意事项</title>
		<link>http://blog.yufeng.info/archives/1485</link>
		<comments>http://blog.yufeng.info/archives/1485#comments</comments>
		<pubDate>Wed, 09 Nov 2011 12:52:43 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[网络编程]]></category>
		<category><![CDATA[调优]]></category>
		<category><![CDATA[fcntl]]></category>
		<category><![CDATA[O_NOATIME]]></category>
		<category><![CDATA[pipe]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1485</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Linux下pipe使用注意事项 Linux下的pipe使用非常广泛, shell本身就大量用pipe来粘合生产者和消费者的. 我们的服务器程序通常会用pipe来做线程间的ipc通讯. 由于unix下的任何东西都是文件,只要是文件,在读取的时候,,就会设置last access time, 所以pipe也不例外., 但是这个时间对我们没有意义 如果pipe使用的非常频繁的时候会碰到由于设置访问时间导致的性能问题. 这个开销远比pipe读写的本身开销大. 相比文件读写的开销, atime微不足道,但是对pipe来讲就不同了. 这个事情是上次和多隆同学在把玩他的网络框架的时候,无意发现的. 我们来分析下pipe的这部分代码: 我们可以看到在pipe读的时候要设置 file_accessed时间的,接着: 如果文件没设置 O_NOATIME就真正动手设置atime,接着: 我们可以看出上面的流程还是比较复杂的,开销也很大. 我们来演示下: 我们可以看到touch_atime的开销很大,远比pipe的读写大. 这次把这行注释去掉: fcntl(fds[0], F_SETFL, O_NOATIME); 指示pipe在读的时候不更新atime,看下效果: 这下看不到touch_atime了,开销省了,对于高性能服务器是很重要的. 小结: 细节很重要,记得开文件open的时候设置O_NOATIME或者用fcntl搞定它. 祝玩得开心! Post Footer automatically generated by wp-posturl plugin for wordpress.]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1485">Linux下pipe使用注意事项</a></p>
</div>
<p>Linux下的pipe使用非常广泛, shell本身就大量用pipe来粘合生产者和消费者的. 我们的服务器程序通常会用pipe来做线程间的ipc通讯. 由于unix下的任何东西都是文件,只要是文件,在读取的时候,,就会设置last access time, 所以pipe也不例外., 但是这个时间对我们没有意义  如果pipe使用的非常频繁的时候会碰到由于设置访问时间导致的性能问题. 这个开销远比pipe读写的本身开销大. 相比文件读写的开销, atime微不足道,但是对pipe来讲就不同了.<br />
这个事情是上次和多隆同学在把玩他的网络框架的时候,无意发现的.</p>
<p>我们来分析下pipe的这部分代码:<br />
<span id="more-1485"></span></p>
<pre class="brush: cpp; title: ; notranslate">
//pipe.c:L349
static ssize_t
pipe_read(struct kiocb *iocb, const struct iovec *_iov,
               unsigned long nr_segs, loff_t pos)
{
...
   if (ret &gt; 0)
        file_accessed(filp);
    return ret;
}
</pre>
<p>我们可以看到在pipe读的时候要设置 file_accessed时间的,接着:</p>
<pre class="brush: cpp; title: ; notranslate">
//fs.h:L1761
extern void touch_atime(struct vfsmount *mnt, struct dentry *dentry);
static inline void file_accessed(struct file *file)
{
        if (!(file-&gt;f_flags &amp; O_NOATIME))
                touch_atime(file-&gt;f_path.mnt, file-&gt;f_path.dentry);
}
</pre>
<p>如果文件没设置 O_NOATIME就真正动手设置atime,接着:</p>
<pre class="brush: cpp; title: ; notranslate">
//inode.c:L1493
void touch_atime(struct vfsmount *mnt, struct dentry *dentry)
{
        struct inode *inode = dentry-&gt;d_inode;
        struct timespec now;

        if (inode-&gt;i_flags &amp; S_NOATIME)
                return;
        if (IS_NOATIME(inode))
                return;
        if ((inode-&gt;i_sb-&gt;s_flags &amp; MS_NODIRATIME) &amp;&amp; S_ISDIR(inode-&gt;i_mode))
                return;

        if (mnt-&gt;mnt_flags &amp; MNT_NOATIME)
                return;
        if ((mnt-&gt;mnt_flags &amp; MNT_NODIRATIME) &amp;&amp; S_ISDIR(inode-&gt;i_mode))
                return;

        now = current_fs_time(inode-&gt;i_sb);

        if (!relatime_need_update(mnt, inode, now))
                return;

        if (timespec_equal(&amp;inode-&gt;i_atime, &amp;now))
                return;

        if (mnt_want_write(mnt))
                return;

        inode-&gt;i_atime = now;
        mark_inode_dirty_sync(inode);
        mnt_drop_write(mnt);
}
</pre>
<p>我们可以看出上面的流程还是比较复杂的,开销也很大.<br />
我们来演示下:</p>
<pre class="brush: bash; title: ; notranslate">
$ cat &gt; pipe_test.c
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;assert.h&gt;
#include &lt;pthread.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;linux/unistd.h&gt;

static int fds[2];
static pthread_t rp;

static void *rp_entry(void *arg) {
  char c[1];
  while (1 == read(fds[0], c, 1)) {
    if (*c == 'Q') break;
  }
  fprintf(stderr, &quot;pipe read ok\n&quot;);
  return NULL;
}

int main(int argc, char *argv[]) {
  long i, n;
  int rc;
  if (argc &lt; 2) {
    fprintf(stderr, &quot;usage: pipe_test NNNNNN\n&quot;);
    return -1;
  }
  n = atol(argv[1]);
  pipe(fds);
  //fcntl(fds[0], F_SETFL, O_NOATIME);
  pthread_create(&amp;rp, NULL, rp_entry, NULL);
  fprintf(stderr, &quot;pipe write %ld...&quot;, n);
  for (i = 0; i &lt; n; i++) {
    write(fds[1], &quot;A&quot;, 1);
  }
  write(fds[1], &quot;Q&quot;, 1);
  fprintf(stderr, &quot;ok\n&quot;);
  pthread_join(rp, NULL);
  close(fds[0]);
  close(fds[1]);
  return 0;
}
CTRL+D
$ gcc -D_GNU_SOURCE pipe_test.c -lpthread
$ sudo opcontrol --setup --vmlinux=/usr/lib/debug/lib/modules/2.6.18-164.el5/vmlinux
$ sudo opcontrol --init &amp;&amp; sudo opcontrol --reset &amp;&amp; sudo opcontrol --start
$ ./a.out 10000000
pipe write 10000000...ok
pipe read ok
$ sudo opcontrol --shutdown
$ opreport -l|less
samples  %        app name                 symbol name
378654   92.7742  vmlinux                  .text.acpi_processor_idle
12978     3.1797  vmlinux                  current_fs_time
2530      0.6199  vmlinux                  thread_return
2345      0.5745  vmlinux                  touch_atime
2253      0.5520  vmlinux                  .text.acpi_safe_halt
1597      0.3913  vmlinux                  timespec_trunc
1368      0.3352  vmlinux                  file_update_time
1253      0.3070  vmlinux                  __mark_inode_dirty
901       0.2208  vmlinux                  pipe_writev
768       0.1882  vmlinux                  __mutex_lock_slowpath
763       0.1869  vmlinux                  try_to_wake_up
270       0.0662  vmlinux                  copy_user_generic_unrolled
254       0.0622  vmlinux                  acpi_set_register
254       0.0622  vmlinux                  system_call
233       0.0571  vmlinux                  pipe_readv
188       0.0461  vmlinux                  dnotify_parent
167       0.0409  vmlinux                  mutex_unlock
...
</pre>
<p>我们可以看到touch_atime的开销很大,远比pipe的读写大.<br />
这次把这行注释去掉: fcntl(fds[0], F_SETFL, O_NOATIME); 指示pipe在读的时候不更新atime,看下效果:</p>
<pre class="brush: bash; title: ; notranslate">
$ opreport -l|less
samples  %        app name                 symbol name
599018   95.2466  vmlinux                  .text.acpi_processor_idle
4140      0.6583  vmlinux                  .text.acpi_safe_halt
3281      0.5217  vmlinux                  thread_return
2812      0.4471  vmlinux                  current_fs_time
2615      0.4158  vmlinux                  file_update_time
1790      0.2846  vmlinux                  __mutex_lock_slowpath
1657      0.2635  vmlinux                  timespec_trunc
1341      0.2132  vmlinux                  try_to_wake_up
1281      0.2037  vmlinux                  mutex_unlock
1080      0.1717  vmlinux                  mutex_lock
1001      0.1592  vmlinux                  pipe_readv
925       0.1471  vmlinux                  pipe_writev
</pre>
<p>这下看不到touch_atime了,开销省了,对于高性能服务器是很重要的.<br />
小结: 细节很重要,记得开文件open的时候设置O_NOATIME或者用fcntl搞定它.<br />
祝玩得开心!</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1485/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Erlang版TCP服务器对抗攻击解决方案</title>
		<link>http://blog.yufeng.info/archives/1774</link>
		<comments>http://blog.yufeng.info/archives/1774#comments</comments>
		<pubDate>Thu, 03 Nov 2011 08:55:16 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[网络编程]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1774</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: Erlang版TCP服务器对抗攻击解决方案 互联网上的TCP服务器面对的环境情况比企业私有的服务器要复杂很多。常见的针对tcp服务器的攻击有以下几种： 1. 伪造协议，导致服务器crash. 比如说某条命令的字段长度，协议最大规定是1024，伪造个4096的。 2. 伪造大的报文。比如说一个包有1024M这么大。 3. 消耗服务器资源。开大量的连接， 以龟速发送报文，比如说每分钟一个字节。 4. DDOS攻击，从不同的IP发起大量的连接。 5. 攻击Erlang集群的授权体系。 6. 报文发送顺序逻辑错误，导致服务器crash. 比如说逻辑上应该先发A，再发B, 攻击者调了顺序。 7. 不停的连接断开，消耗服务器对资源的申请和释放，这个通常很耗。 等等，有很多方法。 那我们的Erlang版的TCP服务器如何应对这些情况呢？ 刚才不小心按了发布，待续。。。 Post Footer automatically generated by wp-posturl plugin for wordpress.]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1774">Erlang版TCP服务器对抗攻击解决方案</a></p>
</div>
<p>互联网上的TCP服务器面对的环境情况比企业私有的服务器要复杂很多。常见的针对tcp服务器的攻击有以下几种：<br />
1. 伪造协议，导致服务器crash. 比如说某条命令的字段长度，协议最大规定是1024，伪造个4096的。<br />
2.  伪造大的报文。比如说一个包有1024M这么大。<br />
3. 消耗服务器资源。开大量的连接， 以龟速发送报文，比如说每分钟一个字节。<br />
4.  DDOS攻击，从不同的IP发起大量的连接。<br />
5. 攻击Erlang集群的授权体系。<br />
6. 报文发送顺序逻辑错误，导致服务器crash. 比如说逻辑上应该先发A，再发B, 攻击者调了顺序。<br />
7. 不停的连接断开，消耗服务器对资源的申请和释放，这个通常很耗。<br />
等等，有很多方法。</p>
<p>那我们的Erlang版的TCP服务器如何应对这些情况呢？<br />
<span id="more-1774"></span></p>
<p>刚才不小心按了发布，待续。。。</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1774/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>简单的Map reduce用的收集函数</title>
		<link>http://blog.yufeng.info/archives/401</link>
		<comments>http://blog.yufeng.info/archives/401#comments</comments>
		<pubDate>Thu, 27 Oct 2011 08:45:23 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[map reduce]]></category>
		<category><![CDATA[upmap]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=401</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: 简单的Map reduce用的收集函数 在处理大量重复任务的时候，为了加快速度，通常会用map-reduce的方式，要是能有段代码做这个事情就好了。作者luke写了底下的代码片段，用起来感觉挺爽的，推荐给大家。原文见这里 %% http://lukego.livejournal.com/6753.html &#8211; that doesn&#8217;t care about %% the order in which results are received. upmap(F, L) -> Parent = self(), Ref = make_ref(), [receive {Ref, Result} -> Result end &#124;&#124; _ Parent ! {Ref, F(X)} end) &#124;&#124; X]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/401">简单的Map reduce用的收集函数</a></p>
</div>
<p>在处理大量重复任务的时候，为了加快速度，通常会用map-reduce的方式，要是能有段代码做这个事情就好了。作者luke写了底下的代码片段，用起来感觉挺爽的，推荐给大家。原文见<a href="http://lukego.livejournal.com/6753.html">这里</a></p>
<blockquote><p>%% http://lukego.livejournal.com/6753.html &#8211; that doesn&#8217;t care about<br />
%% the order in which results are received.<br />
upmap(F, L) -><br />
    Parent = self(),<br />
    Ref = make_ref(),<br />
    [receive {Ref, Result} -> Result end<br />
     || _ <- [spawn(fun() -> Parent ! {Ref, F(X)} end) || X <- L]].
</p></blockquote>
<p>这个函数的特点是不依赖于任务完成的顺序，结构很简单优雅。</p>
<p>祝玩得开心！</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/401/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>看multitrace代码学习如何定制自己的dbg信息</title>
		<link>http://blog.yufeng.info/archives/1647</link>
		<comments>http://blog.yufeng.info/archives/1647#comments</comments>
		<pubDate>Thu, 27 Oct 2011 08:35:14 +0000</pubDate>
		<dc:creator>Yu Feng</dc:creator>
				<category><![CDATA[Erlang探索]]></category>
		<category><![CDATA[multitrace]]></category>
		<category><![CDATA[ttb]]></category>

		<guid isPermaLink="false">http://blog.yufeng.info/?p=1647</guid>
		<description><![CDATA[原创文章，转载请注明： 转载自Erlang非业余研究 本文链接地址: 看multitrace代码学习如何定制自己的dbg信息 multitrace是ttb应用带的一个例子，给了个例子让用户来格式化和定制自己的dbg信息。 文档在这里： The module multitrace.erl which can be found in the src directory of the Observer application implements a small tool with three possible trace settings. The trace messages are written to binary files which can be formatted with the function multitrace:format/1/2. 代码太长，我就不贴了，可以点这里查看. 我演示下如何使用： 我们可以在自己的项目里面结合ttb做些调试模块，方便在出现问题的时候定位问题。 祝玩得开心！ Post Footer automatically generated by [...]]]></description>
			<content:encoded><![CDATA[<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明：</strong> 转载自<a href="http://blog.yufeng.info/">Erlang非业余研究</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.yufeng.info/archives/1647">看multitrace代码学习如何定制自己的dbg信息</a></p>
</div>
<p>multitrace是ttb应用带的一个例子，给了个例子让用户来格式化和定制自己的dbg信息。 文档在<a href="http://www.erlang.org/doc/apps/observer/ttb_ug.html#id314164">这里</a>：</p>
<blockquote><p>The module multitrace.erl which can be found in the src directory of the Observer application implements a small tool with three possible trace settings. The trace messages are written to binary files which can be formatted with the function multitrace:format/1/2.</p></blockquote>
<p>代码太长，我就不贴了，可以点<a href="https://github.com/erlang/otp/blob/master/lib/observer/src/multitrace.erl">这里</a>查看.</p>
<p>我演示下如何使用：<br />
<span id="more-1647"></span></p>
<pre class="brush: bash; title: ; notranslate">
$ pwd
/home/chuba/otp/lib/observer/src
$ erlc multitrace.erl
$ erl
Erlang R14B04 (erts-5.8.5) 1 [64-bit] [smp:16:16] [rq:16] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.8.5  (abort with ^G)
1&gt; multitrace:schedule(self()).
ok
2&gt; erlang:ports().
[#Port&lt;0.1&gt;,#Port&lt;0.49&gt;,#Port&lt;0.406&gt;,#Port&lt;0.415&gt;,
 #Port&lt;0.714&gt;,#Port&lt;0.720&gt;]
3&gt; multitrace:stop().
stopped
4&gt; multitrace:format(&quot;nonode@nohost-schedule_trace&quot;).

Tracing started on node nonode@nohost at 2011-10-27 16:29:40,984702
Flags: [{[&lt;0.47.0&gt;],[running,timestamp]}]

in:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:29:40,984453
Function : {dbg,req,1}

out:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:29:40,984528
Function : {io,wait_io_mon_reply,2}

in:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:29:40,984582
Function : {io,wait_io_mon_reply,2}

out:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:29:40,984635
Function : {io,wait_io_mon_reply,2}

in:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:29:40,984662
Function : {io,wait_io_mon_reply,2}

out:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:29:40,984720
Function : {shell,eval_loop,3}

in:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:30:32,215798
Function : {shell,eval_loop,3}

out:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:30:32,216096
Function : {code_server,call,2}

in:
Process  : &lt;0.47.0&gt;
Time     : 2011-10-27 16:30:32,216155
Function : {code_server,call,2}

Total time 'in' for process &lt;0.47.0&gt;: 484 micro seconds
ok
13&gt;
</pre>
<p>我们可以在自己的项目里面结合ttb做些调试模块，方便在出现问题的时候定位问题。</p>
<p>祝玩得开心！</p>
<div style="margin-top: 0; margin-bottom: 15px; color: #888888; font-size: 80%; font-style: italic">
<p>Post Footer automatically generated by <a href="http://easwy.com/blog/wordpress/wp-posturl/" style="color: #8888FF; text-decoration: underline;">wp-posturl plugin</a> for wordpress.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.yufeng.info/archives/1647/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

