博客

  • Re [CPyUG] Coroutine协程和闭包本质上有什么区别么

    协程与闭包的关系,就好比是鸡蛋和灌饼,你说它俩本质上有什么区别?

    我建议楼主如果对概念感兴趣,可以参考wiki百科关于 coroutine和闭包的条目
    http://en.wikipedia.org/wiki/Closure_%28computer_programming%29
    http://en.wikipedia.org/wiki/Coroutine
    而不是在这里瞎猜瞎联想。

    至于Python与Lua的协程,可以参考我翻译的这篇

    【译文】比较Lua协程与Python生成器


    要注意的是Python本身没有协程概念(第三方实现那就不算本身),只有Generator。

    就我个人浅见,闭包概念比较容易理解使用,而协程是一个动态交互的动作,涉及到主程序和协程子程序之间的交换,理解起来要麻烦多多。
    跟闭包相关联的概念不是协程,而是first class function。
    而与协程相关的,多是“并发”这个概念。

    关于协程,有一篇论文非常值得参考
    www.inf.puc-rio.br/~roberto/docs/MCC15-04.pdf
    是Lua作者写的,建议感兴趣的朋友看看。
    至于闭包,看完维基百科条目,基本就应该了解的差不多了

  • 递归实验-C语言递归调用的极限

    C语言递归调用不是无限的,当递归到一定时候,会出现stack over flow的问题。http://en.wikipedia.org/wiki/Stack_buffer_overflow

    但是,这个度是多少呢?

    我构造了一个极为简单的C语言递归程序,大家可以参考这里https://gist.github.com/749543

    #include "stdafx.h"
          int count = 0;
    void f()
    {
      int a = 1; int b = 1; int c = 1; int d = 1;
      a = b + 3;
      c = count + 4;
      d = count + 5*c;
      count++;
      printf("count: %d\n", count);
      f();
    }

    int _tmain(int argc, _TCHAR* argv[])
         {
            f(); return 0;
          }

    我使用的是VC2010SP1的C语言模式编译,console程序,无优化。

    运行结果如下:

    默认情况下,count最后结果为42860,然后程序就结束了。通过查询MSDN可以知道

    http://msdn.microsoft.com/en-us/library/8cxs58a6%28v=vs.80%29.aspx

    我们可以通过修改project的link选项中的stack commit size和stack reserve size来改变程序的stack大小。

    如果这两个size都设置为1、64、640、2048这几个值,结果都是10092.

    如果size设置为1024000,那么结果是42860,与默认情况相同。可以看出这个size是以BYTE为单位。

    如果size设置为1048000,结果为43372.

    size设置为1072000以及2048000,结果都是86551.

    size设置为2110000以及3072000,结果都是130242.

    一般来说,我们基本上不需要考虑修改这个大小,因为很少会有将近四万的递归层次,默认值已经足够用了。但是如果万一不够用或者有这种递归需求,那么就需要修改这两个值,另外当我们新建一个线程的时候,也可以编程修改线程stack的大小。

    http://cs.nyu.edu/exact/core/doc/stackOverflow.txt 这里解释了一下不同平台上stackoverflow的问题,有一些数据可以参考。

    http://stackoverflow.com/questions/53827/checking-available-stack-size-in-c 在这里有人提供了一个有趣的递归C代码片段来判断栈大小。

    在这里多废话几句,关于Lua的proper tail call http://www.lua.org/pil/6.3.html 。当我们使用Lua for windows构造一个简单的递归程序运行,依然是会得到stack over flow的结果。但是当我们使用proper tail call这种方式调用,(由于抛弃了前面程序的栈)调用可以无限循环下去,所以Lua这个特性常常用来构造state machine。关于使用proper tail call来实现斐波拉契,可以参考这里http://lua-users.org/wiki/ProperTailRecursion

  • C语言中本地变量local variable的作用域与生存期

    《狂人C》中191页提到“每次运行到auto变量j所在block,会为j寻找存储空间,离开j所在代码模块,j的内存被释放掉。

    这是不正确的。

    结论应该是:对于C语言而言,本地变量会在栈开始处申请,栈销毁时结束生命。但是本地变量的作用域与所在block相关。之所以编译不通过,是因为这种block之外访问block之内变量的语法是错误的,离开本地变量所在block{},它的作用域无效,但不是说销毁了。

    作用域错误是语法层面的。而生存期(存在销毁)与程序栈相关,是运行时的概念,它们是两码事。而且,有没有auto这个keyword修饰,结果都一样,所以auto用不用没有任何意义。

    关于C语言程序栈以及调用规范,可以参考我前面翻译的文章:http://sunxiunan.com/?p=1229 也可以查找wiki关于calling conversion方面的资料。

    下面我们通过这段代码说明一下:

    https://gist.github.com/748095

    int main (int argc, char *argv[])

    {

     int a = 99;

     int b = a;

     int* pLocala = NULL;

     int* pLocalb = NULL;

     int d = 1;

    
    

     printf("hello world \n");

     do{

     int a = 3;

     int b = 23;

     pLocala = &a;

     pLocalb = &b;

     b = a + 5;

     printf("%d %p\n", b, &a);

     }while(0);

    
    

     int e = 1;

    
    

     b = a;

     printf("%d %p\n", b, &a);

     printf("%p %p %p %p %p %p %p %p\n", &e, pLocalb, pLocala, &d, &pLocalb, &pLocala, &b, &a);

     return(0);

    }

    如果你运行这段代码(无论是VC++还是GCC),最后printf出来的结果应该是连续的,而&pLocala和&pLocalb其实就是一种while循环中定义的变量a和b所在地址,可以看出来它们夹在变量d与e中间。

    我们可以用汇编代码看的更清楚一些,在VC2010生成的汇编代码(C源代码与前面略有不同),完整输出参考 https://gist.github.com/748116

    ————————————————

    ;    COMDAT _wmain

    _TEXT    SEGMENT

    _b$4410 = -32                        ; size = 4

    _a$4409 = -20                        ; size = 4

    _a$ = -8                        ; size = 4

    _argc$ = 8                        ; size = 4

    _argv$ = 12                        ; size = 4

    ; 7    : {

    ……

    ; 8    :     int a = 0;

    mov    DWORD PTR _a$[ebp], 0

    $LN3@wmain:

    ; 9    :

    ; 10   :     do

    ; 11   :     {

    ; 12   :         auto int a = 3;

    mov    DWORD PTR _a$4409[ebp], 3

    ; 13   :         auto int b = 0;

    mov    DWORD PTR _b$4410[ebp], 0

    ……

    ——————————————

    可以看到中间;12 : auto int a = 3;使用的是这样的汇编代码

    mov    DWORD PTR _a$4409[ebp], 3

    很显然_a$4409是在程序栈开始位置定义,而不是书中提到的”每次进入block时定义”。

    此文发在CU后得到不少有价值的评论

    http://bbs.chinaunix.net/thread-1834663-2-1.html

    果然如我所料,如果没有O1这个编译选项,VC++2010是不会压缩空间的。
    如果加了O1这个”mini space”优化选项,出来的结果也不是OwnWaterloo提到的,临时变量都没有了,优化得很彻底。
    我用的是VC2010,过早的优化果然罪恶!(开个玩笑)

    无论如何,还是要谢谢OwnWaterloo的说法:勿以汇编释语言。
    其实我也同意这个看法,所以尽量以K&R来作为解读文本。
    但是对于一些实现相关的内容,如果还有通过自身的解释或者对标准的注解来做说明,那是非常费劲的。
    《狂人C》中对++前后缀方式的说明就是如此,尽管键盘农夫试图把++解释的更为通俗易懂深入浅出,但是我看了还是一头雾水。
    而当我看过++代码生成的“无优化VC++2010特别为了注释”版本以后,就恍然大悟,源码之下别无秘密。
    而且,如果没有我用汇编展示的这个例子,OwnWaterloo大侠也不会出手解释,从这点来讲,也是抛砖引玉。

    当然也可以说我这是一种误读,太细化,系统及平台依赖太强,
    但是我感觉用汇编来解读部分C语言实现相关的细节,在现阶段对我而言是有帮助的,那就可以了。

    int _tmain(int argc, _TCHAR* argv[])
    {
    int a = 0;
    do
    {
    char buf[1024];
    buf[0] = 1;
    }while (0);

    a = 3;

    do
    {
    char buf[1024];
    buf[0] = 93;
    }while(0);

    return 0;
    }

    ASM输出结果为:

    _TEXT        SEGMENT
    _buf$4413 = -2076                                        ; size = 1024
    _buf$4409 = -1044                                        ; size = 1024
    _a$ = -12                                                ; size = 4
    __$ArrayPad$ = -4                                        ; size = 4
    _argc$ = 8                                                ; size = 4
    _argv$ = 12                                                ; size = 4
    _wmain        PROC                                                ; COMDAT

    ; 7    : {

    push        ebp
    mov        ebp, esp
    sub        esp, 2272                                ; 000008e0H
    push        ebx
    push        esi
    push        edi
    lea        edi, DWORD PTR [ebp-2272]
    mov        ecx, 568                                ; 00000238H
    mov        eax, -858993460                                ; ccccccccH
    rep stosd
    mov        eax, DWORD PTR ___security_cookie
    xor        eax, ebp
    mov        DWORD PTR __$ArrayPad$[ebp], eax

    ; 8    :         int a = 0;

    mov        DWORD PTR _a$[ebp], 0
    $LN6@wmain:

    ; 9    :
    ; 10   :         do
    ; 11   :         {
    ; 12   :                 char buf[1024];
    ; 13   :                 buf[0] = 1;

    mov        BYTE PTR _buf$4409[ebp], 1

    ; 14   :         }while (0);

    xor        eax, eax
    jne        SHORT $LN6@wmain

    ; 15   :
    ; 16   :         a = 3;

    mov        DWORD PTR _a$[ebp], 3
    $LN3@wmain:

    ; 17   :
    ; 18   :         do
    ; 19   :         {
    ; 20   :                 char buf[1024];
    ; 21   :                 buf[0] = 93;

    mov        BYTE PTR _buf$4413[ebp], 93                ; 0000005dH

    ; 22   :         }while(0);

  • 关于Python Web开发我的感悟-兼答wsgi fastcgi uwsgi

    有位朋友最近在python.cn接连提问关于python web开发的问题,
    当然由于他问题的标题是Python http server,我也很难确定我下面的说法可以回答他。

    如果单论http server,通俗点讲,其实就是一个可以相应客户端针对80端口的http请求的一个网络程序。
    apache也好,nginx也罢莫不如此。python写一个http server也很简单,只要绑定80端口,解析请求,返回一些格式化好的响应数据就行了。

    但是根据列举的这些关键字,其实这位朋友要问的应该是python web开发。
    从最简单开始讲,python支持cgi编程,也有标准的cgi模块。
    什么是CGI,它不是Python自己独有的东西,而是一个通用的web标准,可以粗陋的认为CGI就是一种用任意语言(那时候都是指perl)写Web程序的标准。最简单的就是用C写一个hello world的程序,只要做一些简单的apache配置,你就可以在网上运行这个程序。

    cgi有缺点,就是你自己玩玩还好,想想一个py程序如何运行的?它需要运行py解释器,然后载入这段py脚本,
    运行,然后py解释器退出。也就是说每当一个用户访问你的py程序都要经历这个py解释器运行、载入、退出的过程。

    设计很不优化。

    很显然,这时候需要重用,缓存,对不对?

    我印象中fastcgi机制(这是一种机制,早期nginx都是用lighttpd的spawn-fcgi)就是做这样的工作,它会起几个python进程,不退出一直运行着,当你有请求,某一个空闲的python进程就负责载入你的脚本,然后跑,然后继续空闲。

    看出区别了吧,python不需要反复启动、退出,这是极为耗费时间的,所以节省了这个过程,就省去了很多时间。
    实际上这个机制被普遍运用着,比如office预启动或者很多程序都在系统启动时加载。

    当然fastcgi还有其它的机制保证它的运行,我了解不多,就不乱说了。

    那么wsgi是什么?
    跟wsgi一样概念的有Ruby语言的Rack,可以认为wsgi是一种通用的接口标准或者接口协议,实现了python web程序与服务器之间交互的通用性。就好比JCP一样。有了这个东西,web.py或者bottle或者django等等的python web开发框架,就可以轻松地部署在不同的web server上了,不需要做任何特殊配置(也需要一些小小的配置调整)
    http://wsgi.org/wsgi/Frameworks 这里可以看到几乎所有流行的web框架都支持wsgi协议。

    uwsgi又是什么?
    就我看到的一些东西,uwsgi类似tornadoweb或者flup,也是一种python web server,负责响应python 的web请求,因为apache也好,nginx也罢,它们自己都没有解析动态语言如php的功能,而是分派给其他模块来做,比如apache就可以说内置了php模块,支持的非常爽,让人感觉好像apache就支持php一样。

    我说的很简单,其实最重要的是这一句。学Pythonweb开发,应该从动手做开始,而且应该从appengine开始,先省略python部署问题,着重于用python开发web程序,真正跑起来用起来。至于后续的一些概念,应该多学多看,其实我现在对很多python基础也是了解的极为粗浅,但是喜欢没事就查查资料,所以能对这些词语忽悠一些出来。