麦田守望者's profile异想空间PhotosBlogLists Tools Help

Blog


    11/17/2009

    被我遗忘了很久的一本好书

    有一本被我遗忘了很久的书,它叫《GNU/Linux编程指南》。已经忘记是什么时候买的了。翻看了一下目录。这本书非常系统地讲解了,在Linux环境中编程所需要了解的方方面面的内容。其内容涉及编程环境、系统编程、网络接口、GUI编程、驱动程序开发等。每个部分都提供了详尽的代码示例。由于有些年头了,书中介绍的某些技术细节可能稍显过时。但是,这并不妨碍通过它完整地了解Linux环境的应用程序开发。

    回想当初买这本书的时候,对什么是GNU、什么是UNIX和Linux、什么是自由软件和开源软件,没有一点概念。然而,运气相当好的是,自己曾经在无意间买了一本非常值得珍藏的好书。


    11/9/2009

    总结的任务之一是寻找模式

    有了模式,我们能够以最有效的方法解决某一类问题。找到模式,需要量的积累,以及不断的思考和总结。

    10/26/2009

    与rc有关的几个重要文件

    OpenBSD uses an rc(8) style startup. This uses a few key files for startup.

    • /etc/rc - 主脚本。不应该编辑它。
    • /etc/rc.conf - /etc/rc使用的配置文件,用于为系统设置启动参数。
    • /etc/rc.conf.local - 配置文件。此文件中的设置会覆盖掉/etc/rc.conf中的相同的设置值,因此不该直接编辑它。当升级你的系统的时候,这很重要。
    • /etc/netstart - 用于初始化网络的脚本。不应该编辑它。
    • /etc/rc.local - 用于本地管理的脚本。特定于守护进程或者主机的信息放在这个文件中。
    • /etc/rc.securelevel - 脚本文件,它执行那些必须在安全级别变动之前执行的命令。参看 init(8)
    • /etc/rc.shutdown - 在关机的时候执行的脚本文件。关机前你想要做的任何事情都可以放在这个文件里。参看 rc.shutdown(8)

    以上摘自OpenBSD FAQ。在理解了它们各自的用途之后,需要注意使用它们的注意事项。

    10/22/2009

    在命令行中调节音量

    在命令行调节音量使用mixerctl,方法如下:

    $ mixerctl outputs.master=200,200

    其中,两个数值200,分别代表的是左右声道的音量,中间用一个逗号分隔。其取值范围是0到255。不加任何参数执行mixerctl,能够看到系统当前的音频配置参数。

    把系统时间调整为本地时间

    By default, OpenBSD assumes your hardware clock set to UTC (Universal Coordinated Time) rather than local time, assumed by some other operating systems, which can cause problems when multi-booting.

    Many other operating systems, can be configured to do the same, which avoids this problem altogether.

    If having the hardware clock set to UTC is a problem, you can change the default behavior of OpenBSD using config(8). For example, to configure OpenBSD to use a hardware clock set to US/Eastern (5 hours behind UTC, so 300 minutes):

    # config -ef /bsd
    OpenBSD 4.6 (GENERIC.MP) #89: Thu Jul 9 21:32:39 MDT 2009
    deraadt@i386.openbsd.org:/usr/src/sys/arch/i386/compile/GENERIC.MP
    Enter 'help' for information
    ukc> timezone 300
    timezone = 300, dst = 0
    ukc> quit
    Saving modified kernel.

    See options(4) and search for option "TIMEZONE=value" for more information.

    以上是从OpenBSD FAQ中摘录的。config命令用于更改时区。对于北京时间,使用如下命令行修改时区:

    ukc> timezone -480

    timezone的参数是以分钟为单位。因此,八小时为480分钟。quit之后,重启心爱的OpenBSD,date一下。看,时间变过来了。


    10/21/2009

    OpenBSD的rc脚本是如何工作的(摘录)

    The main files a system administrator should concentrate on are /etc/rc.conf (for guidance), /etc/rc.conf.local (for changes), /etc/rc.local and /etc/rc.shutdown. To get a look of how the rc(8) procedure works, here is the flow:

    After the kernel is booted, /etc/rc is started:

    • Filesystems are checked.
    • Default configuration variables are read in from /etc/rc.conf, then local changes to those variables are read from /etc/rc.conf.local. Settings in rc.conf.local will override those in rc.conf.
    • Filesystems are mounted
    • Clears out /tmp and preserves any editor files
    • Configures the network via /etc/netstart
      • Configures your interfaces up.
      • Sets your hostname, domainname, etc.
    • Starts system daemons
    • Performs various other checks (quotas, savecore, etc)
    • Local daemons are run, via /etc/rc.local
    9/10/2009

    对getcwd函数的一例应用的不解

    OpenBSD源代码树的pwd.c中,有这样一个代码片段:

    int main(int argc, char *argv[])
    {
    /* 省略其他代码 */

    char *p;

    if ((p = getcwd(NULL, (size_t)0)) == NULL)
    err(1, "getcwd");
    (void)printf("%s\n", p);
    exit(0);
    }

    该段代码的目的是获得当前工作目录,并打印出来。请注意getcwd的调用方式。根据OpenBSD Maunalgetcwd函数的描述:如果给第一个参数传递NULL的话,getcwd会在某个地方分配内存空间,存放当前目录的字符串,并返回指向这个字符串的指针。调用方应该在稍后调用free,释放字符串占用的内存空间。然而,在上述从OpenBSD源代码中摘抄的代码片段,并没有调用free。
    不清楚代码的作者为何这样写。疏忽遗漏了,还是getcwd函数的行为有其特殊性?如果看到这的读者你了解内幕,请赐教,不胜感激!

    8/29/2009

    听歌也很简单

    听MP3你用什么软件?我用VLC。不过,我还用更高效、易用的mpg321。mpg321是一个命令行程序,没有任何用户界面。这就使得mpg321非常的高效,占用非常之少的系统资源。安装mpg321同样很容易。如果使用Debian或者Ubuntu,那么sudo apt-get install mpg321;如果使用BSD Unix,那么可以使用他们各自的软件包管理系统安装;或者干脆从源代码编译。看你的喜好了。

    播放MP3:

    mpg321 file1.mp3

    播放多个MP3:

    mpg321 file1.mp3 file2.mp3 singer/famous-song.mp3

    打乱顺序播放:

    mpg321 -z file1.mp3 file2.mp3 singer/famous-song.mp3

    man mpg321会显示完整的使用参考。

    mpg321可以被用在shell脚本中。发挥一下想象力,是否能够把它用到什么特别有意思的地方呢。

    BTW:mpg321不是Windows环境中的产物。想在Windows下使用的话,你可以搜一搜试试看。

    8/27/2009

    IATTopic.queryCatalog

    IATTopic接口声明了queryCatalog方法:

    def queryCatalog(REQUEST=None, **kw):
    """Invoke the catalog using our criteria to augment any passed
    in query before calling the catalog.
    """

    利用这个方法,我们能够以编程的方式控制提供了IATTopic接口的对象的搜索行为。最典型的是Plone的标准内容类型Collection,在更早期的Plone版本中其称作Smart Folder。Collection这个名字可以看作是面向用户的名字,非常清晰直观的反映了它的功能。在Plone内部,Collection类型实际上是一个ATTopic类,其portal_type是Topic,archetype_name是Collection。言归正传,看一看能用这个接口方法做什么。

    在ATTopic类实现中,丰富了queryCatalog方法的参数:

    def queryCatalog(self, REQUEST=None, batch=False, b_size=None,
    full_objects=False, **kw):
    pass

    利用这些参数,能完成如下任务:

    1、在已经设置了搜索条件的基础上,附加其它额外的条件。将任何额外的搜索条件,按照ZCatalog的规范组合成一个字典对象,传递给kw参数。关于ZCatalog的参考,《The Zope2 Book》的Searching and Catagorizing Content章节有详细的介绍。

    2、按批次返回特定数量的对象集合,即分页。batch参数指明是否按批次返回搜索结果,b_size说明返回的结果集的大小。除了这两个参数,还需要在context.REQUEST对象中设定b_start。b_start指明,在完整的搜索结果结合中,从第几个对象开始返回b_size数量的对象集合。和数组的下标一样,b_start的值从0计起。

    在这种情况下,queryCatalog方法返回的是一个 ZTUtils.Batch.Batch对象。Batch对象实现了__getitem__和__len__方法。同时,还提供了previous和 next属性,用于前后双向跌代下一个Batch对象。如果到头了,则previous和next会相应的被置为None。

    3、返回的对象集合中,包含每一个完整的对象。full_objects参数指明,调用每一个Brain对象的getObject方法,从ZODB中获取完整的对象。此种做法可能会影响Plone的运行性能。

    举个例子:

    在当前上下文中存在一个Collection对象,其ID为search。想要搜索EffectiveDate在2009年1月1日以来的Page对象,并且按照EffectiveDate排序,显示头10个对象。

    from DateTime import DateTime
    qc = {}
    qc['portal_type'] = 'Document'
    qc['effective'] = { 'query': DateTime('2009/1/1'), 'range': 'min' }
    qc['sort_on'] = 'effective'
    qc['sort_limit'] = 10
    qc['sort_order'] = 'descending'
    context.search.queryCatalog(**qc)[0:10]

    如果想要分页显示所有Page对象,每一页显示20个,显示第2页:

    qc = {}
    qc['portal_type'] = 'Document'
    qc['sort_on'] = 'effective'
    qc['sort_order'] = 'descending'
    # 这一行很重要。如果缺少这一行,则会始终从搜索到的第一个对象开始返回结果
    context.REQUEST.set('b_start', (2 - 1) * 20)
    context.search.queryCatalog(batch=True, b_size=20, **qc)
    7/31/2009

    解决psycopg2导入错误

    psycopg2版本:2.0.11

    Zope版本:2.10.7-final

    在上述环境中创建一个ZPsycopgDA数据连接对象的时候,会出现ImportError异常,提示psycopg version mismatch (imported 2.0.11 (dt dec ext pq3))。

    解决办法:在ZPsycopgDA目录中,修改DA.py文件的119行:

    - if psycopg2.__version__[:5] not in ALLOWED_PSYCOPG_VERSIONS:
    + if psycopg2.__version__.split(' ')[0] not in ALLOWED_PSYCOPG_VERSIONS:

    导致这个错误的原因是,2.0.11版本的psycopg2的__version__属性会返回“2.0.11 (dt dec ext pq3)”,只有按照空格打断字符串并取得第一元素,才能正确获得版本号。


    7/30/2009

    解锁Plone对象

    如果某个对象被上了锁,导致没法对其进行操作,可以用wl_clearLocks方法为其解锁:

    obj = getattr(context, 'obj_id')
    obj.wl_clearLocks()

    至于对象为何以及在什么条件下会被上锁,我尚不清楚。如果您清楚,感谢指点一二。

    7/29/2009

    content_status_modify方法在哪里定义的?

    寻寻觅觅,在源码中始终不见content_status_modify方法的踪迹。你知道吗?请告诉我。谢谢!

    BaseFolder对象的manage_addFolder方法

    BaseFolder位于Products.Archetypes.BaseFolder.py

    在Python脚本对象中,可以使用manage_addFolder方法创建一个Folder对象。从ATFolder继承下来的容器对象,都包含了这个方法。当在自定义的容器对象上调用manage_addFolder方法的时候,要明确地给type_name参数传递一个字符串值Folder。例如:

    context.manage_addFolder(id = 'objid', title = 'Object's title', type_name = 'Folder')

    然而,不要被这个方法的字面意思所蒙蔽。只要给type_name参数传递了正确的portal_type值,还可以用它创建其他类型的内容对象。例如:

    context.manage_addFolder(id = 'objid', title = 'title', type_name = 'Document')
    context.manage_addFolder(id = 'objid', title = 'title', type_name = 'Image')

    这里“正确的”含义有二。一是,type_name的值必须是已经注册的对象的portal_type值;二是,指定的portal_type必须是当前容器被允许容纳的对象类型中的一种。

    实际上,manage_addFolder在做了一系列准备工作后调用invokeFactory方法,而invokeFactory最终调用portal_types对象的constructContent方法创建对象。如果很清楚自己要做些什么,也可以直接调用invokeFactory和constructContent方法。

    7/22/2009

    IObjectManager接口中的几个有用的方法

    IObjectManager位于OFS.interfaces.py文件中。IObjectManager接口定义了一个容器类需要实现的方法。其中有几个方法很有用。

    def hasObject(id):
        """ Indicate whether the folder has an item by ID. """

    def manage_delObjects(ids=[], REQUEST=None):
        """Delete a subordinate object

           The objects specified in 'ids' get deleted.
        """

    def __getitem__(key):
        """
        """

    • hasObject方法接受一个参数,该参数表示某个对象的id,hasObject方法会判断该对象是否存在于容器中。
    • manage_delObjects方法用于在容器中删除一组对象。删除的依据是ids参数,这是一个列表对象,其中包括将要被删除的对象的id。
    • __getitem__方法使得提供了IObjectManager接口的对象可以像字典对象那样获得容器中的某个对象。

    IObjectManager接口还提供了诸如superValues、objectItems、objectValues等其他方法用于访问容器中的对象。

    Plone的ATFolder对象通过继承和实现IATFolder接口,也提供了IObjectManager接口的功能。因此,在Plone中,以编程的方式对一个ATFolder对象施加更多的控制就变得容易许多。通过对CMFCore和CMFPlone的探索得知,还有许多其他更高层的接口可以利用。

    7/12/2009

    在portal_catalog中按照分类搜索使用Subject关键字

    当以编程方式通过portal_catalog工具搜索具有特定分类属性的内容对象的时候,使用Subject关键字。请注意首字母大写。在ZMI中,portal_catalog的Indexes页中存在一个类型为KeywordIndex,名称为Subject的索引。这就是我们所需要的。

    7/11/2009

    (dynamic view)和(selected layout)

    Plone提供了一种机制,允许开发者给实现了IATFolder接口的内容类型提供不同的显示视图(Views),而用户则可以通过Display菜单设置内容类型的动态视图。作为内容类型的开发者,特别需要留意“(dynamic view)”和“(selected layout)”的使用。即在某个内容类型的GenericSetup配置文件中,必须确保下面的两行正确出现:

    <alias from="(Default)" to="(dynamic view)" />
    <alias from="view" to="(selected layout)" />

    如果,选择Display菜单中的视图没有任何作用的话,那么可以看看这两个配置值是否正确出现。


    5/9/2009

    在基于Debian的Linux中安装OpenOffice.org 3.1.0

    我在自己的Debian 5.0 (lenny)中成功安装了OpenOffice.org 3.1.0,步骤简单,在这里与大家分享。

    1. 下载OpenOffice.org 3.1.0。下载地址:http://download.openoffice.org/other.html#en-US
    2. 删除系统中已经存在的OpenOffice。使用命令:sudo apt-get remove openoffice*。留意:不要落下openoffice后面的星号。
    3. 解压缩下载后的文件OOo_3.1.0_LinuxIntel_install_en-US_deb.tar.gz,并进入解压后的目录OOO310_m11_native_packed-4_en-US.9399/DEBS
    4. 安装目录中的所有deb包,使用命令:sudo dpkg -i *.deb
    5. 进入OOO310_m11_native_packed-4_en-US.9399/DEBS/desktop-integration,同样使用dpkg命令安装该目录中唯一的deb包。使用命令:sudo dpkg -i openoffice.org3.1-debian-menus_3.1-9393_all.deb

    如果一切顺利,到这已经可以使用新版本的OpenOffice了。

    在安装新版本OpenOffice之前,我一直使用2.4.2版本,并且Java环境运行良好。由于每台计算机几乎不可能有完全一致的系统环境,因此我不确保上述步骤在您的系统中100%运行无误。仅供参考。

    另外,可以参考一篇图文并茂地讲解OpenOffice的文章(英文)。

    5/5/2009

    理解Browser View之多适配器

    视图组件是命名的多适配器。其作用于的对象是HTTP Request对象和提供某个特定接口的对象实例。在视图组件的configure.zcml文件中声明page的时候,page元素就描述了这一概念。例如:

    <browser:page
      for="..interface.IReport"
      class="report_view.py"
      name="view"
      template="report_view.pt"
      permission="zope2.View"
    />

    属性name指出,我们声明了一个名字叫做view的视图。for指出只有当请求一个提供了IReport接口的对象的时候,才会调用这个视图。当然,发出请求的用户必须具有permission属性指定的权限才行。

    用编程方式获得这个视图对象,能够更直观地体会到多适配器的概念:

    from zope.component import getMultiAdapter
    from zope.publisher.browser import TestRequest
    # 假设该Report类实现了IReport接口
    from myproject import Report

    report = Report()
    request = TestRequest()
    view = getMultiAdapter((report, request), name=u"view")
    view()



    cctv.com与IE结下因缘

    如果想在cctv.com上看直播,那么必须安装Windows和IE。cctv.com剥夺了所有不用IE和Windows的人看视频的权力。