Archive for the ‘Programming’ Category
因为1个异常导入190个包
今天reddit programming上的一个帖子,提到了ubuntu上python-apport包的一个bug。
重现这个bug很简单:
Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> len(sys.modules) 35 >>> foo Traceback (most recent call last): File "", line 1, in NameError: name 'foo' is not defined >>> len(sys.modules) 225 >>>
在执行foo之前,当前导入的包只有35个,执行之后却有了225个。问题出在Ubuntu的Apport系统,它为Python添加了一个异常处理方法,这里面的bug导致了这些多余的包被导入。
这个bug已经被人提交并被确认,目前暂时还没有补丁,最简单的避免方法就是把python-apport包卸载
原文地址: http://rhodesmill.org/brandon/2010/ubuntu-exception-190-modules/
SICP里提到的画图语言
SICP第二章里提到了一种用来画图的Lisp方言,用来演示数据抽象和闭包的表达能力(见http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-15.html#%_sec_2.2.4)。
最近尝试了下,发现soegaard同学已经在PLT Scheme中实现了一个类似的库,可以很方便的在DrScheme上使用。
sicp.plt包使用很简单,在Language->Choose Language中选择Module,然后在需要用到这个包的时候用
(require (planet soegaard/sicp:2:1/sicp))
声明即可。第一次运行时DrScheme会自动下载这个包并安装,如果网络有限制可以先从http://planet.plt-scheme.org/display.ss?package=sicp.plt&owner=soegaard下载然后在DrScheme中选择本地包安装。
另外SICP上使用的两个painter(wave和rogers)没有在这个包里提供,取而代之是diagonal-shading和einstein。
下面这个程序显示了一个简单的分形图像:
#lang scheme
(require (planet "sicp.ss" ("soegaard" "sicp.plt" 2 1)))
(define (right-split painter n)
(if (= n 0)
painter
(let ((smaller (right-split painter (- n 1))))
(beside painter (below smaller smaller)))))
(define (up-split painter n)
(if (= n 0)
painter
(let ((smaller (up-split painter (- n 1))))
(below painter (beside smaller smaller)))))
(define (corner-split painter n)
(if (= n 0)
painter
(let ((up (up-split painter (- n 1)))
(right (right-split painter (- n 1))))
(let ((top-left (beside up up))
(bottom-right (below right right))
(corner (corner-split painter (- n 1))))
(beside (below painter top-left)
(below bottom-right corner))))))
(paint (corner-split diagonal-shading 4))
程序输出:
gcc中设置特定代码块的优化级别
今天碰到一个gcc优化相关的问题,为了让一个页变成脏页(页表中dirty位被置上),需要执行下面这段代码:
uint32_t *page; // ... page[0] = page[0];
最后一行代码很有可能被gcc优化掉,因为这段代码看起来没有任何实际的作用。那么如何防止gcc对这段代码做优化呢?
设置gcc编译时优化级别为-O0肯定是不合适的,这样对程序性能影响会比较大。stackoverflow上的Dietrich Epp给出了一个强制类型转换的方案:
((unsigned char volatile *)page)[0] = page[0];
通过volatile关键字禁止gcc的优化,和我之前采用的方法类似。
Plow同学给出了另一个利用gcc 4.4特性的方法:
#pragma GCC push_options
#pragma GCC optimize ("O0")
your code
#pragma GCC pop_options
这里用到了gcc 4.4的特性Function Specific Option Pragmas,在特定代码前保存当前的编译选项,然后对特定的代码使用O0优化级别,最后再恢复之前保存的编译选项。
俺觉得这个特性有些场合下挺好用的,在这里分享下,虽然因为编译器版本问题最后我还是用了前面一种方法。
这样也能算圆周率
reddit programming版面最近的热帖,下面这个程序输出的结果是一个近似的圆周率(3.156)。
#define _ F-->00 || F-OO--;
long F=00,OO=00;
main(){F_OO();printf("%1.3f\n", 4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_
}
乍看下这个程序有点莫名其妙,分析一下宏后就知道它的方法了。两个全局变量F和OO分别记录 圆的面积和直径 的相反数,根据4*面积/直径/直径就能得到近似的圆周率了。
至于面积和直径的计算,F在会在每一个_展开的地方减一,这样就得到了圆的面积。直径的计算要展开几行代码才能看得更清楚:
F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--; -F-->00 || F-OO--;
这是用cpp展开圆形前两行代码的结果,因为或运算的特殊性,F- OO- -只会在每一段的第一行执行,所以OO- -执行的次数就等于圆的直径了。
强制程序使用int 0×80做系统调用
因为大多数情况下程序都是通过libc间接地发出系统调用的,所以只要编译一个只使用int 0×80的glibc库,然后在执行程序的时候用LD_LIBRARY_PATH或其他方法指定使用新编译的glibc库即可。
以glibc-2.9, Linux i386为例,在sysdeps/unix/sysv/linux/i386/syscall.S中可以看到
ENTRY (syscall)
PUSHARGS_6 /* Save register contents. */
_DOARGS_6(44) /* Load arguments. */
movl 20(%esp), %eax /* Load syscall number into %eax. */
ENTER_KERNEL /* Do the system call. */
POPARGS_6 /* Restore register contents. */
cmpl $-4095, %eax /* Check %eax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
这里使用了ENTER_KERNEL这个宏做系统调用,接下来在sysdeps/unix/sysv/linux/i386/sysdep.h里可以找到这个宏的定义
/* The original calling convention for system calls on Linux/i386 is to use int $0x80. */ #ifdef I386_USE_SYSENTER # ifdef SHARED # define ENTER_KERNEL call *%gs:SYSINFO_OFFSET # else # define ENTER_KERNEL call *_dl_sysinfo # endif #else # define ENTER_KERNEL int $0x80 #endif
而I386_USE_SYSENTER这个宏也是在同一个头文件中定义的
#if defined USE_DL_SYSINFO \
&& (!defined NOT_IN_libc || defined IS_IN_libpthread)
# define I386_USE_SYSENTER 1
#else
# undef I386_USE_SYSENTER
#endif
把这个条件宏改成
#undef I386_USE_SYSENTER
就能强制glibc使用int 0×80了。
这个方法只能过滤通过glibc做的系统调用。对于程序里写死使用sysenter的情况就要使用反汇编或者其他手段了。
SVN更新短信通知脚本
主要功能:开发人员执行svn commit后自动将开发人id、修订版本号和日志内容通过短信的方式通知所有人。
首先修改svn服务器对应项目目录hook/post-commit文件
#!/bin/bash
export LANG=en_US.utf8
REPOS="$1"
REV="$2"
cd /home/svn/repositories/sebank/hooks
./sms.py commit $REPOS $REV
注意别忘了这里的export LANG,我一开始测试的时候发现中文一直有乱码,后来才意识到shell的环境变量里缺这个。
hook/sms.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, urllib, os
from subprocess import *
user = 'your mobile number'
pword = 'fetion password'
phone = [
'13764444444',
'13813333333',
]
repo = sys.argv[2]
rev = sys.argv[3]
cmdlog = 'svnlook log -r %s %s'%(rev, repo)
cmdauthor = 'svnlook author -r %s %s'%(rev, repo)
log = Popen(cmdlog, stdout=PIPE, shell=True).stdout.read().strip()
author = Popen(cmdauthor, stdout=PIPE, shell=True).stdout.read().strip()
msg = 'sebank #%s %s: %s' % (rev, author, log)
for number in phone:
url = 'http://sms.api.bz/fetion.php?username=%s&password=%s&sendto=%s&message=%s' % (user, pword, number, urllib.quote_plus(msg))
cmd = 'curl "%s"' % url
send = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
send.wait()
这里user是发送者的手机号,且该用户的飞信好友需包含其他用户,pword为飞信密码,phone为用户手机号码列表。
最后设置下这两个文件的权限,保证http/svn用户能执行即可。另外由于sms.py中飞信密码是明文保存的,注意控制它的读权限。
关于tuple的读音
水木上有个帖子讨论tuple这个词该怎么读,于是有人翻出来这个三年前的新闻组邮件
http://coding.derkeiler.com/Archive/Python/comp.lang.python/2006-02/msg01915.html
I used to pronounce it toople. But the people that taught me Python
found it both comical and confusing. At first they thought I meant a 2
element tuple. So they wondered if a 3 element tuple was a threeple,
etc. After much harrassing, I changed my wayward ways and pronounced
it tuhple to fit in with the cool Python guys.![]()
Then we went to hear Guido speak about Python 2.2 at a ZPUG meeting in
Washington, DC. When he said toople I almost fell out of my chair
laughing, particularly because the people who taught me to say it the
“right” way were with me. When I looked over, they just hung their
head in shame.I work with Guido now and I’m conflicted. I’m still conditioned to say
tuhple. Whenever he says toople, I just get a smile on my face. I
think most of the PythonLabs guys pronounce it toople.
有意思,嘿嘿
我一直习惯把这个词读成/tʌpəl/,估计是受tunnel这个词的影响,然后查了下http://en.wiktionary.org/wiki/tuple#Pronunciation_2 发现/tuːpəl/和/tʌpəl/都可以
CGO 09 一篇关于DCL检测的论文
Double-Checked Lock是一个常见的由于程序员把内存模型默认为sequential momery consistency导致的问题,具体见我去年写的一篇博文http://techblog.iamzellux.com/2008/07/singleton-pattern-and-double-checked-lock/
虽然Java 5解决了这个问题,但是C++等语言中这个问题依然存在,依然有很多因为程序员假设sequential consistency而编译器做了错误的指令调度后导致的bug,见http://www.newsmth.net/bbscon.php?bid=335&id=250203
CGO 09的这篇paper Detecting and Eliminating Potential Violations of Sequential Consistency for Concurrent C/C++ Programs针对这个问题进行了深入研究,通过加fence指令的方法解决了因编译器的指令调度造成的违背程序员原义的问题,可以在http://ppi.fudan.edu.cn/yuelu_duan下载到。没有认真读过,俺在这里就不误人子弟了 O.O
到底啥才是Type Safety呢
水木的FuncProgram上最近为这个问题引发了一场大水(其实也不大,在那个版上算比较大了 XD),里面有一个很不错的引用,现摘录如下: Read the rest of this entry »
Daily Notes on Python[11.17-11.23]
模块动态加载机制
1. Advanced Python(某一期Google TechTalks的话题)上提到import指令本质是个语法糖,import sys等价于sys = __import__(“sys”)。解析import sys的bytecode可以看到四个指令(参数略):
LOAD_CONST
LOAD_CONST
IMPORT_NAME
STORE_NAME
IMPORT_NAME把sys模块导入并保存到栈上,STORE_NAME把这个指针当作普通对象保存在sys这个变量中。
2. IMPORT_NAME指令行为分析
将参数打包并用PyEval_CallObject()这个统一调用接口运行__import__方法,bltinmodule.c中的builtin__import__函数包装了这个功能。help(__import__)显示的__import__方法的参数列表
__import__(…)
__import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module
对应于builtin__import__中调用的另一层函数封装
PyObject *
PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
这个函数调用了真正干活的函数,import.c中的
static PyObject *
import_module_level(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
3. import_module_level函数
首先调用get_parent()得到import发生时的package对象,接下来多次调用load_next依次得到各级的包名,跟踪了下import xml.dom.minidom的行为,发现name的值依次是”dom.minidom”, “minidom”。最后根据import的形式是from … import …还是import …返回不同的处理结果。
4. import_submodule函数
load_next函数中调用这个函数进行模块的查找和载入。主要分三步:find_module, load_module, add_submodule。
5. from … import …
ensure_fromlist处理了这个情况。另外对于from … import *的情况,会从module文件中读入一个__all__对象,从中知道module公开的符号信息。
几个以后可能用到的函数:
[import.c]
/* Parse a source file and return the corresponding code object */
static PyCodeObject * parse_source_module(const char *pathname, FILE *fp);
/* Execute a code object in a module and return the module object
* WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is
* removed from sys.modules, to avoid leaving damaged module objects
* in sys.modules. The caller may wish to restore the original
* module object (if any) in this case; PyImport_ReloadModule is an
* example.
*/
PyObject * PyImport_ExecCodeModule(char *name, PyObject *co);
