• 公布 mryufeng 同学的 Erlang 秘笈
    时间:2008-10-22   作者:佚名   出处:互联网

    上次说请 mryufeng 同学多写写研究心得,还推说不大爱写长东西。隔不了几天,跑到他的 blog 上一看,乖乖龙的东!整了一大堆秘笈,原来是要藏私!说不得,对于这样同志,只能揪出来示众了。注意,以下内容全部是“海贼版”,未经作者明确同意,请慎入!

    1, erl CTRL+C 的未公开功能

    在erl shell下按下CTRL+C的时候,显示:

    1. BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
    2.          (v)ersion (k)ill (D)b-tables (d)istribution

    注:*nix 下的 erl 才有,windows 下的 erl 按 ctrl+C 就直接退出了。

    但是实际上可以有更多功能,看代码:

    1. while (1) {
    2.     if ((i = sys_get_key(0)) <= 0)
    3.         erl_exit(0, "");
    4.     switch (i) {
    5.     case 'q':
    6.     case 'a':
    7.     case '*': /*
    8.            * The asterisk is an read error on windows,
    9.            * where sys_get_key isn't that great in console mode.
    10.            * The usual reason for a read error is Ctrl-C. Treat this as
    11.            * 'a' to avoid infinite loop.
    12.            */
    13.         erl_exit(0, "");
    14.     case 'A':        /* Halt generating crash dump */
    15.         erl_exit(1, "Crash dump requested by user");
    16.     case 'c':
    17.         return;
    18.     case 'p':
    19.         process_info(ERTS_PRINT_STDOUT, NULL);
    20.         return;
    21.     case 'm':
    22.         return;
    23.     case 'o':
    24.         port_info(ERTS_PRINT_STDOUT, NULL);
    25.         return;
    26.     case 'i':
    27.         info(ERTS_PRINT_STDOUT, NULL);
    28.         return;
    29.     case 'l':
    30.         loaded(ERTS_PRINT_STDOUT, NULL);
    31.         return;
    32.     case 'v':
    33.         erts_printf("Erlang (%s) emulator version "
    34.                ERLANG_VERSION "\n",
    35.                EMULATOR);
    36.         erts_printf("Compiled on " ERLANG_COMPILE_DATE "\n");
    37.         return;
    38.     case 'd':
    39.         distribution_info(ERTS_PRINT_STDOUT, NULL);
    40.         return;
    41.     case 'D':
    42.         db_info(ERTS_PRINT_STDOUT, NULL, 1);
    43.         return;
    44.     case 'k':
    45.         process_killer();
    46.         return;
    47. #ifdef OPPROF
    48.     case 'X':
    49.         dump_frequencies();
    50.         return;
    51.     case 'x':
    52.         {
    53.         int i;
    54.         for (i = 0; i <= HIGHEST_OP; i++) {
    55.             if (opc[i].name != NULL) {
    56.             erts_printf("%-16s %8d\n", opc[i].name, opc[i].count);
    57.             }
    58.         }
    59.         }
    60.         return;
    61.     case 'z':
    62.         {
    63.         int i;
    64.         for (i = 0; i <= HIGHEST_OP; i++)
    65.             opc[i].count = 0;
    66.         }
    67.         return;
    68. #endif
    69. #ifdef DEBUG
    70.     case 't':
    71.         p_slpq();
    72.         return;
    73.     case 'b':
    74.         bin_check();
    75.         return;
    76.     case 'C':
    77.         abort();
    78. #endif
    79.     case '\n':
    80.         continue;
    81.     default:
    82.         erts_printf("Eh?\n\n");
    83.     }
    84.     }

    注:就是说,除了 a c p i l v k D d 之外,还有 A m o X x z ,如果在 DEBUG 下编译的,还有 t b C 可以用。

    好多调试用的功能 希望对大家有用。

    2,如何看erts内部的状态

    经常在性能优化的时候 要看下erts内部的允许状态 erlang有未公开的函数

    erts_debug:get_internal_state(XX)

    XX为atom有以下几个
    DECL_AM(node_and_dist_references);
    DECL_AM(DbTable_words);
    DECL_AM(next_pid);
    DECL_AM(next_port);
    DECL_AM(check_io_debug);
    DECL_AM(available_internal_state);
    DECL_AM(monitoring_nodes);

    XX为list有以下几个
    DECL_AM(link_list);
    DECL_AM(monitor_list);
    DECL_AM(channel_number);
    DECL_AM(have_pending_exit);

    可以看的很细节的运行期数据,前提是先用 erts_debug:set_internal_state(available_internal_state, true). 否者调用 get_internal_state 会提示失败.

    3,erlang inet:setopts 未公开选项

    inet:setopt有packet设置选项:

    1. {packet, PacketType} (TCP/IP sockets)
    2.     Defines the type of packets to use for a socket. The following values are valid:
    3.  
    4.     raw | 0
    5.         No packaging is done.
    6.     1 | 2 | 4
    7.         Packets consist of a header specifying the number of bytes in the packet, followed by that number of bytes. The length of header can be one, two, or four bytes; the order of the bytes is big-endian. Each send operation will generate the header, and the header will be stripped off on each receive operation.
    8.     asn1 | cdr | sunrm | fcgi | tpkt | line
    9.         These packet types only have effect on receiving. When sending a packet, it is the responsibility of the application to supply a correct header. On receiving, however, there will be one message sent to the controlling process for each complete packet received, and, similarly, each call to gen_tcp:recv/2,3 returns one complete packet. The header is not stripped off.
    10.         The meanings of the packet types are as follows:
    11.         asn1 - ASN.1 BER,
    12.         sunrm - Sun's RPC encoding,
    13.         cdr - CORBA (GIOP 1.1),
    14.         fcgi - Fast CGI,
    15.         tpkt - TPKT format [RFC1006],
    16.         line - Line mode, a packet is a line terminated with newline, lines longer than the receive buffer are truncated.

    文档中写的就这么多了 其实还有2个选项:http, httph 用于解释http的请求,实现在 otp_src_R11B-5\erts\emulator\drivers\common\inet_drv.c 里:

    1. #ifdef USE_HTTP
    2. ...
    3.     #endif
    4.  
    5. /*
    6. ** load http message:
    7. **  {http_eoh, S}                          - end of headers
    8. **  {http_header,   S, Key, Value}         - Key = atom() | string()
    9. **  {http_request,  S, Method,Url,Version}
    10. **  {http_response, S, Version, Status, Message}
    11. **  {http_error,    S, Error-Line}
    12. */

    消息以上面的方式发给process,用于 gen_tcp 由于在driver层面实现的,所以效率就很高,对于编写http隧道之类的程序 很有帮助哦。

    注:得到一个提示,如果要在 erlang 中以极高的效率来实现某个东西,可以在 driver 层面入手。

    4,erl_call erlang cnode 功能强大

    otp_src_R11B-5\lib\erl_interface\src\prog\erl_call.c 是个不错的工具, 就是ei的前端能够通过cnode给erlang的后端发各种请求。

    具体的见主题: 如何把erlang应用在项目中?

    1. where: -a  apply(Mod,Fun,Args) (e.g -a 'erlang length [[a,b,c]]'
    2.          -c  cookie string; by default read from ~/.erlang.cookie
    3.          -d  direct Erlang output to ~/.erl_call.out.
    4.          -e  evaluate contents of standard input (e.g echo "X=1,Y=2,{X,Y}."|erl_call -e ...)
    5.          -h  specify a name for the erl_call client node
    6.          -m  read and compile Erlang module from stdin
    7.          -n  name of Erlang node, same as -name
    8.          -name  name of Erlang node, expanded to a fully qualified
    9.          -sname name of Erlang node, short form will be used
    10.          -q  halt the Erlang node (overrides the -s switch)
    11.          -r  use a random name for the erl_call client node
    12.          -s  start a new Erlang node if necessary
    13.          -v  verbose mode, i.e print some information on stderr
    14.          -x  use specified erl start script, default is erl

    使用方法 :
    1. erl -name xx@192.168.0.98 启动后端
    2. export EI_TRACELEVEL=6
    i. erl_call -v -d -n xx@erl98.3322.org -m 模块方式 ( -v -d 调试和verbose模式)
    如: -module(a).
    CTRL+D
    ii. erl_call -v -d -n xx@erl98.3322.org -e 表达式方式
    如: 1+2.
    CTRL+D
    iii. erl_call -v -d -n xx@erl98.3322.org -a ‘erlang length [[a,b,c]]’

    这两种都是从stdin读 直到你按下CTRL+D, 然后你就可以看到结果。

    另外,这只程序有内存泄漏

    1. *    Note: We don't free any memory at all since we only
    2. *    live for a short while.

    而且 erl_call.c有bug,第812行

    1. free(mbuf);            /* Allocated in read_stdin() */

    注释掉这行就可以了

    这个程序默认是不安装带标准发布目录去的。

    从这个程序 我们可以知道cnode 能作的事情受限于你的想像力。

    注:
    ping erl98.3322.org
    PING erl98.3322.org (192.168.0.98) 56(84) bytes of data.

    由于erl_call程序有点小问题 -n xx@erl98.3322.org 最好用域名 否者erl_call就抓狂了。

    copy & paste 后感:“非业余研究”就是不一样啊,硕果累累的。mryufeng 同学,好样的!

    网友留言/评论

    我要留言/评论