Python, C-Python, Cython代码与GIL的交互

这篇笔记相对Python来说,有点底层,先来解释几个名词:

C-Python: 或者CPython,指C实现的Python虚拟机的基础API。最通用的Python就是是基于C实现的,它的底层API称为C-Python API,所有Python代码的最终变成这些API以及数据结构的调用,才有了Python世界的精彩;

Cython:准确说Cython是单独的一门语言,专门用来写在Python里面import用的扩展库。实际上Cython的语法基本上跟Python一致,而Cython有专门的“编译器”先将Cython代码转变成C(自动加入了一大堆的C-Python API),然后使用C编译器编译出最终的Python可调用的模块。

GIL:Global Interpreter Lock,是Python虚拟机的多线程机制的核心机制,翻译为:全局解释器锁。其实Python线程是操作系统级别的线程,在不同平台有不同的底层实现(如win下就用win32_thread, posix下就用pthread等),Python解释器为了使所有对象的操作是线程安全的,使用了一个全局锁(GIL)来同步所有的线程,所以造成“一个时刻只有一个Python线程运行”的伪线程假象。GIL是个颗粒度很大的锁,它的实现跟性能问题多年来也引起过争议,但到今天它还是经受起了考验,即使它让Python在多核平台下CPU得不到最大发挥。

GIL的作用很简单,任何一个线程除非获得锁,否则都在睡眠,而如果获得锁的线程一刻不释放锁,别的线程就永远睡眠下去。对于纯Python线程,这个问题不大,Python代码会通过解释器实时转换成微指令,而解释器给他们算着,每个线程执行了一定的指令数后就要把机会让给别的线程。这个过程中操作系统的调度作用比较微妙,不管操作系统怎么调度,即使把有锁线程挂起到后台,尝试唤醒没锁的,解释器也不给他任何执行机会,所以Python对象很安全。

所以一般来说,做纯Python的编程不需要考虑到GIL,它们是不同层面的东西,但是模块级别的C-Python、Cython等C层面的代码,跟Python虚拟机是平起平坐的,所以GIL很可能需要考虑,特别那些代码涉及IO阻塞、长时间运算、休眠等情况的时候(否则整个Python都在等这个耗时操作的返回,因为他们没获得锁,急也没办法)。

想体现这个过程,很简单,考虑下面的代码,一段纯Python和一段纯C的循环,每次print一段文字就睡眠一秒。

1
2
3
4
5
6
7
void _c_loop ( void )
{
    while(1) {
        printf("Print from C loop\n");
        sleep(1);
    }
}
1
2
3
4
def _py_loop():
    while True:
        print "Print from Python loop"
        time.sleep(1)

先不管他们是如何揉合到同一Python进程里面,两个进程分别执行了这两个函数后,他们应该以大概相互间隔着输出文字;但实际情况是,Print from Python loop这句出现了一次之后(先启动了纯Python线程,否则它连启动的机会都没),剩下的输出全都是Print from C loop,不断的输出,按Ctrl + C都没反应(因为响应信号的只有住线程,终止信号是发出了,说不定主线程也收到了,但是解释器不给机会),只好从另外的控制台kill了整个python进程。

显然问题在于,运行C那段程序的进程获取了GIL,但是这是个死循环是永远都出不去的,所以GIL永远都在这个进程手里,别的进程只能睡觉。考虑到这个C程序的情况,因为循环里面只有printf跟sleep两个操作系统的调用,完全威胁不到Python对象的安全,所以GIL完全没必要插手进来。解决办法有两种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void _c_loop_1 ( void )
{
    Py_BEGIN_ALLOW_THREADS 
    while(1) {
        printf("Print from C loop\n");
        sleep(1);
    }
    Py_END_ALLOW_THREADS
}
 
void _c_loop_2 ( void )
{
    while(1) {
        Py_BEGIN_ALLOW_THREADS
        printf("Print from C loop\n");
        sleep(1);
        Py_END_ALLOW_THREADS
    }
}

Py_BEGIN_ALLOW_THREADS、Py_END_ALLOW_THREADS是定义在Python的C-API头文件里面的宏,需要给头文件包含上#include

两个解决办法看起来差不多,效果也差不多,但意义不大一样。

_c_loop_1是在进入永远都不会停的循环之前,把GIL交出去(还有保存自己的线程状态,但现在的例子这个没有意义),然后自己继续运行,这时候_c_loop_1是跟python虚拟机是互不相关的线程了,井水不犯河水,解释器的GIL爱给谁给谁。这时候“Python只能有一个线程运行”的传说就被破除迷信了。

_c_loop_2则是每次进行printf/sleep的调用前交出GIL,之后又申请要回来,然后循环回去马上又交出去(=。=)……

这个例子来说,当然是前者有效率了(=。=反正都是无用功,何来效率),只是为了在复杂的情况(C代码会影响Python对象,调用Python的函数等)下灵活运用。Py_BEGIN_ALLOW_THREADS、Py_END_ALLOW_THREADS其实只是两句语句的宏包含,Python手册里面有详细说明,甚至直接打开Python的include就可以看到。其中BEGIN那句调用了PyEval_SaveThread(),而打开Python源码,PyEval_SaveThread函数里面毅然有PyThread_release_lock()的调用。

上面这些,在《Python源码剖析——深度探索动态语言核心技术》一书,第15章Python多线程机制 15.4.2阻塞调度一节讲解得比较详细,书里面是直接拿出time.sleep/raw_input这些Python本身的代码来举例和分析。当然咯,本文加入了很多PT自己的发散性理解。

Cython在旁边说,怎么还没到我出场。恩,马上就来。

Cython其实是很纠结的东西,因为他用的是Python一样的语法,但经常不得不时时刻刻提醒自己,有时候是在写C,有时候在写Python(=。=)。C是强类型的,Python是弱类型的,所以写Cython的时候有时候是强类型的,有时候是弱类型的,一般变量还好,你可能会想一下类型,像我刚入门,经常把漏写参数类型,不过还好Cython的编译器是有提示的,当熟悉了规则后,用Python写C啊……通过Cython,不仅可以在Python调用C,还可以在C调用Python……Python的上天入地就靠他了。

Cython代码最终都是会编译成C的,上面例子那段py_loop,改个名字叫cy_loop吧,直接放入Cython里面编译即可,他完全会变成C,然后变得像C一样霸道,执行后拿了GIL永远不还回去。

显然,只有纯Python的代码需要在执行时候经过解释器解释的,才会“自动的”交回GIL,而Cython是编译成C的代码,执行不经过解释器,如果他不交回GIL,就拿它没办法的了。

Cython是留有GIL的交互接口,在Cython手册里面加起来才半页的说明,语焉不详地介绍了Cython下跟GIL的交互方法,如果没理解上面GIL的工作方式,像我那样把这两页翻烂了都看不懂的。像刚才的例子,cy_loop可以写成这样:

1
2
3
4
5
def cy_loop():
    with nogil:
        while True:
            printf("Print from Cython loop\n")
            sleep(1)

就是用with nogil块把循环围起来,对,就加一行。如果打开Cython转换而成的C代码,发现Cython用Py_UNBLOCK_THREADS和Py_BLOCK_THREADS把这段循环括起来了,查看Python C-API,Py_UNBLOCK_THREADS跟Py_BEGIN_ALLOW_THREADS其实是一样的,只差一个花括号。不过Cython还会盯着你在nogil块里面的代码,里面不能引用Python对象,不能调用Python的函数,要画清界线!啊我只是调用个printf跟sleep,人格保证这是C库的函数!可是这样Cython还是不让过,说在nogil里面不能调用需要gil的函数,恩,printf跟sleep是通过cdef预定义的,Cython可不知道这些函数是哪里的,就默认它们需要gil了。所以要在cdef引入的时候,在后面加上nogil的声明:

1
2
3
cdef extern from *:
    unsigned int sleep(unsigned int seconds) nogil
    int printf(char *format, ...) nogil

嗯,就这样,Cython编译器终于高兴了。

Cython里面关于GIL的另外一个接口是:

1
2
cdef void callback_func(void) with gil:
    ...

Cython会生成PyGILState_Ensure()的调用,来保证这个函数在线程获得锁的时候才运行。这个情况一般是用于C的回调函数,因为回调运行是不知道什么时候的,如果这个函数里面有对Python的引用,就需要保证获得了GIL才操作。又或者你在C代码里面又生成了新的线程,而且也会引用Python的东西(纠结……)。

本文例子代码包可这里下载,或Google Code在线查看;例子里面是分别是Python、Cython、C三个循环,因为有Cython的GIL接口,C代码里面就没有使用API的宏了(注释掉)。例子里面添加了全局变量end,主线程进入pause后按Ctrl + C触发使其为真,然后三个线程会相继退出。例子在linux下编译,如在win下编译可能要修改下C代码里面的sleep,以及signal.pause()。

例子代码运行效果:

Tags: , , , , , , ,

评论 (1)

Ubuntu 下使用蓝牙/USB连接黑莓8700上网

黑莓8700支持EDGE网络,用来做GPRS Modem是相当理想的,标准的mini usb接口,蓝牙2.0,完全足够EDGE的网络速度;

本来以为用蓝牙连接会很简单,在blueman里面连接即可,可是实际上,不行:

Failed: Modem Manager did not support the connection

之前用blueman连接Nokia的机,是没问题的,看来黑莓的Modem协议有点不同……Google了一下,找到了不少BB在Linux下的资料:
How use BlackBerry device with Linux【介绍了Linux对黑莓的配置、同步、安装软件、Modem、Java开发】
how to teather blackberry and linux via bluetooth【连BB蓝牙的完整过程,试验不成功】
Berry4all 通过USB连接BB Modem的专用程序

测试Berry4all终于能够连接上BB的GPRS,观察Berry4all其实也是建立了串口通信后,使用pppd的协议来建立数据链接的,要连接cmnet,还得修改其conf下的att-chat,即其中的“拨号”协议;

blueman虽然不能建立Modem链接,但是还是能够建立起/dev/rfcomm0,即通过蓝牙的串口链接;使用蓝牙教程,基本上都是使用hcitool等工具来配置的,使用起来真的好不方便,而blueman不是刚好帮忙搞定了蓝牙的配对连接等麻烦事情么……

于是把Berry4all的conf目录的att跟att-chat文件复制到/etc/ppp/peers,参照别的教程的配置,修改att,在开始115200那行前,加入nodetach跟/dev/rfcomm0两行,最后的connect调用chat文件的路径改成connect "/usr/sbin/chat -f /etc/ppp/peers/att-chat",att-chat里面修改一行:OK 'AT+CGDCONT=1,"IP","cmnet"' ,最后运行sudo pppd call att,终于用蓝牙连上了GPRS;

其实使用ppp连接modem的方法是最传统的,也是高度可定制(如连接后配置路由表、IP、DNS),不管是用Cable、USB、蓝牙还是红外,都可以这样完成,Of Linux, GPRS Phones, Serial Cable, Irda, Bluetooth and USB文章里面列举了好多这些例子;我修改了其中的三个脚本:gprs, gprs-connect-chat, gprs-disconnect-chat替代了上面Berry4all的att脚本,因为这脚本里面的注释非常详细,以防以后需要的调整;

这是我打包了修改好后(适用于移动CMNET上网的)gprs脚本,使用blueman连接了rfcomm0后,运行sudo pppd call gprs,就能连接上GPRS,ifconfig查看出现一个ppp0,可以直接上网了;

如果不使用blueman,完全使用bluez的工具的话,可以参照Blackberry Curve 8310 as Bluetooth Modem on Ubuntu Hardy,我没测试,不过按理说是可行的。

之所以这么折腾,其实可以说blueman的一个bug吧,似乎BB的Modem没啥特别,按通用的Modem都能连接到;有时间看看blueman的代码看能不能弄好……

Tags: , , , , ,

暂无评论

ffmpeg API 笔记:使用libavcodec/libavformat/libswscale

Update 2010.1.5: 其实研究ffmpeg不用找什么教程,第一步应该是下载ffmpeg的源码包。下面提到的An FFmpeg and SDL Tutorial确实有讲解,但是教程总是跟不上代码的变化的,所以直接看可工作代码最好;ffmpeg的结构很分明,后台是几个库:libxxx,前台是三个程序ffmpeg, ffplay, ffserver,那篇教程说的就是ffplay的实现。一个播放器,其实重点不是解码,解码的东西是lib去做的,主要是做声音视频的时钟同步。ffplay的代码可以说是一个可用播放器最简单的实现了,源码里面有个output_example.c,可以说是最基本的api示范吧。ffmpeg是转换编码解码转换程序,因为涉及重新采样等等,所以代码量也不少的。


这两天"调研"了下ffmpeg的API,不得不承认被雷倒:ffmpeg又是一个很geek的项目,纯社区开发,基于逆向,功能强大,但是文档极度有限,想了解API?看源码去…… 网上关于ffmpeg API的资料,无非是ffmpeg文档里面的两个链接,Using libavformat and libavcodec by
Martin Böhme(以及其Update,介绍了新引入的API)跟An FFmpeg and SDL Tutorial
by Stephen Dranger;两个tutorial基于ffmpeg 0.4.8,现在ffmpeg发布的版本是0.5.0,好像数值相差不大,不过0.4.8是5年前的了(相比之下wine用了15年版本号才到达1.0,有过之余无不及),两个教程里面的代码在0.5.0下一编译,哇,一堆错误,可不仅有些api函数变了,有些结构成员压根就没了,头文件的位置更是不一样(各个库分家了)……所以我调试了好几个小时,终于把例子的代码弄好(其实Martin Böhme那篇有一段09年加入的更新说明,链接了有相关的解决办法,我一开始没注意,几个小时自己解决,不过也有收获)。

最后我调试好的代码流程:打开一个视频文件,抓取前5帧保存为文件;
基于Stephen Dranger的Tutorial1源码在此:GoogleCode

av_register_all();//初始化ffmpeg库,如果系统里面的ffmpeg没配置好这里会出错
av_open_input_file();
av_find_stream_info();//查找文件的流信息
dump_format();//dump只是个调试函数,输出文件的音、视频流的基本信息了,帧率、分辨率、音频采样等等
for(...);//遍历文件的各个流,找到第一个视频流,并记录该流的编码信息
sws_getContext();//根据编码信息设置渲染格式
avcodec_find_decoder();//在库里面查找支持该格式的解码器
avcodec_open();//打开解码器
pFrame=avcodec_alloc_frame();//分配一个帧指针,指向解码后的原始帧
pFrameRGB=avcodec_alloc_frame();//分配一个帧指针,指向存放转换成RGB后的帧
avpicture_fill(pFrameRGB);//给pFrameRGB帧加上分配的内存;
while true{
     av_read_frame();//读取一个帧(到最后帧则break)
     avcodec_decode_video();//解码该帧
     sws_scale();//把该帧转换(渲染)成RGB
     SaveFrame();//对前5帧保存成ppm图形文件(这个是自定义函数,非API)
     av_free_packet();//释放本次读取的帧内存
}
avcodec_close();
av_close_input_file();

用到的API就这么多,当然实际代码稍复杂一点;ppm图像是类似BMP的非压缩格式,SaveFrame就是相当于把pFrameRGB的内存拷贝进文件,写文件并不复杂;

调试过程的问题,首先是头文件,ffmpeg 0.5.0的API已经拆分成好几个独立的库,用pacman -Ql ffmpeg看了下文件分布,在include下好几个目录都是它的,看名字可以大概猜出他们的功能:

libavcodec:CODEC其实是Coder/Decoder的缩写,也就是编码解码器;
libavdevice:对输出输入设备的支持;
libavformat:对音频视频格式的解析
libavutil:集项工具;
libpostproc:后期效果处理;
libswscale:视频场景比例缩放、色彩映射转换;

修改好头文件包含,终于少了些not declared错误;

Martin Böhme那篇教程的代码是使用g++编译的,虽然代码是C风格;在我修改了头文件以及一些错误之后,居然链接出错,av_register_all什么的函数统统undefined reference,想到ffmpeg是纯C实现,以及以前用g++编译GTK出现回呼函数找不到的经历,相信又是g++的function mangling搞的,Google了一下,解决方法是把几个头文件包含在exterc "C"里面

代码里面的错误还涉及一些结构成员的变化,比如

pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_VIDEO

就有个类型错误,因为0.5.0里面的codec已经是指针,而不是结构了,要把.换成->,而相应地获得解码器指针,不再需要&:

pCodecCtx=&pFormatCtx->streams[videoStream]->codec;

原教程提到一个视频码率的rate的Hack:

    // Hack to correct wrong frame rates that seem to be generated by some codecs<br />    if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)<br />        pCodecCtx->frame_rate_base=1000;

好吧现在frame_rate跟frame_rate_base压根就没了,去掉算了;

最麻烦的变化还是原代码里面的img_convert,就是解码出一个帧的数据后,需要转换成RGB格式才能写入文件,然而这个函数在0.5.0里面彻底没了。尝试把img_convert完全注释掉,把原始帧img_convert写入文件,还算可喜的是能够看到图形,只是被分成三个画面的通道图形罢了;

Google了一番,还是回到 Stephen Dranger的An FFmpeg and SDL Tutorial第八节,介绍了swscale的接口,虽然里面的例子是转换成SDL所用的YUV,而不是RGB;注意到其使用的参数PIX_FMT_YUV420P,跟img_convert所用的PIX_FMT_RGB24有相同前序,就试试照样花虎了;
给sws_getContext传入源格式的H/W,格式,输出格式的H/W,PIX_FMT_RGB24格式,其中有个参数flags的解释是 specify which algorithm and options to use for rescaling,是选择在缩放过程中是使用线性还是双立方等算法(参数文档没说,要找看源码去),这里照抄了例子里面的SWS_BICUBIC。获得这个SwsContext,类似python的re.compile,再用这个转换器去转换每一个帧,所以后面每次解码了帧后,调用sws_scale,跟原来的img_convert倒是挺像;

也就是说,以后如果需要对视频进行4:3跟16:9的转换,就是在sws_getContext的参数里面做设置了;

最后整个程序正常,会把视频的前5帧抓成图像了,只是会有个小警告:

[swscaler @ 0x1d8f670]No accelerated colorspace conversion found.

估计是转换成RGB,swscale里面没有特别优化的算法?

目前发现程序运行的时候CPU占用很高,是因为程序还没有控制帧速的时钟,只会用尽CPU的性能不断的读取解码;

PT修改过可编译通过(ffmpeg 5.0/gcc 4.4.2/ubuntu 9.10)的前三个Tutorial代码可在Google Code查看下载。(编译方法请看各个文件头部的注释说明)

Tags: , , , ,

暂无评论

Google Code项目Wiki页在SVN仓库被复位后无法修改的Bug

Google Code的项目控制因为其简洁深受很多开源作者的喜欢,PT好几个项目都是存放在Google Code上,不过之前遇到了一个些少恼人的Bug:

当建立了Wiki页,因为某种原因重置了SVN仓库,那原来的Wiki页将无法修改、无法删除(返回500 Server Error)。

重置SVN一般属于特殊情况,比如要把另外一个服务器的SVN仓库搬到另外一个SVN,而只保留后来的版本数据,才需要重置SVN仓库。我当时的情况是,不小心把包含自己帐号密码的测试脚本提交了上去SVN;版本管理的特点是版本修改是无法被抹除的,情急之下只好放弃版本痕迹,备份了文件,请求了SVN重置;之后就发现原先的Wiki页无法修改了。

一番查找后,发现Google Code自身的Support里面早有人发现了这个问题,可以看到,这个Bug第一次在2007年11月就有人提出了,一直到现在都陆续有人确认Bug依然存在……所以当时我就没管那个有问题的Ghost Page了,任其放着;两年都没有修复的bug看了没什么好指望的了;

不过最近这个问题有人提出了个颇为纠结的解决办法:创建wiki/the_page文件(跟之前的wikipage同名),然后不断修改递交svn,当svn的revision版本号高于重置之前wiki page最后修改的版本号,就可以修改或者删除这个Ghost Page了……

想到我那个项目现在的版本号早就超过了之前重置SVN仓库时候的版本号,直接跑去wikipage点Delete this page,哈,直接搞定了!

从这个Bug可以透视到Google Code的Wiki页构建机制:Wiki页跟SVN仓库内相应文件是紧密互动的,在Web对Wiki页的修改就相对于直接提交了相应SVN内的文件,当直接修改SVN内的Wiki文件时候,Web上的Page自动根据SVN刷新WEB页的缓冲,并记录文件的修改版本号;而重置了SVN仓库将导致SVN的版本号从0重新开始,这样Wiki页的缓冲模块没法找到相应的版本号对应的wiki文件进行操作,所以报告500错误;只能当版本号重新达到原先的最后修改时,Wiki Web才能正常的和SVN交互。

另外有趣的是,Google的程序员似乎对修复这个Bug没什么兴趣;可能容易引起新的bug吧,呵呵。

Tags: , , , , ,

暂无评论

锐捷/联想/神州数码 802.1x客户端支持MacOS、BSD

Insion同学之前发来一段可以在MacOS/BSD编译运行获取网卡MAC地址的代码,于是整理了一下,让几个802.1x Client都能支持MacOS/BSD了。

下载了个FreeBSD 7.2在vbox虚拟机里面装了下,发现FreeBSD比Linux好像原始多了[呃,我说安装程序],分区的时候他不叫Create Partition,叫Create Slice,我对着界面看了半天才猜到,囧;退出那里写着:Q = Finish,Quit就Quit嘛,什么Finish,纠结……但还好,其他的配置跟Linux还是很相像的,调试了一下就有了全可编译的代码了。

代码部分修改倒没多少,就添加了一个专门给BSD系系统获取MAC的函数,再用maroc判断一下,以及几个头文件,就完事了,有点麻烦的是makefile,发现freebsd默认那个make好像是很古老很古老的版本,我程序里面那个Makefile是用了vim里面c-support插件里面带的Makefile模板,有点复杂,但是freebsd居然不支持!所以整理了个简单的版本,专门给MacOS/BSD编译,也方便别人的修改;另外一个原因是,程序里面转换服务器消息时候用了iconv库,linux里面iconv是系统内嵌库来的,用不着链接的时候给出参数,但MacOS/BSD偏偏就要-liconv……

需要编译MacOS/BSD版本的同学,可以check出项目里面trunk的代码,运行make -f Makefile.bsd来编译。Insion同学已经编译成功,而且在他的主页上有二进制版下载了。

相对的说,可能在MacOS里面编译是最麻烦的,我大概说说流程(实际上我没试过,我可没Mac机器[T.T])

1.安装gcc,参考这里http://connect.apple.com/的Dev Tools里面下载Xcode Tools,安装。
2.编译安装libpcap,从http://www.tcpdump.org/release/libpcap-1.0.0.tar.gz下载源码,tar xvfz libpcap-1.0.0.tar.gz解压,进去该目录,./configure、make、make install安装完成;
3.编译802.1x客户端,从所用项目内签出源码,在目录内make,没出什么差错的话,已经完成了。然后按Readme.txt的方法安装运行,即可!

项目主页

锐捷:http://code.google.com/p/zruijie4gzhu/
联想:http://code.google.com/p/zlevoclient/
神州数码:http://code.google.com/p/zdcclient/

Tags: , , , , , ,

评论 (17)

油猴脚本Twitter Picture Preview支持中文推特圈、官方Twitter

油猴脚本:Twitter Picture Preview

Feature: Adding preview thumbs right after a short url to an popular image-service site in twitter.
功能:给twitter内包含指向图片服务的网址加上该图片的缩略图。

客户端支持/Client Supports:
twitter.com / itweet.net / twitese.appspot.com

图片服务支持/Image-Service Supports:
twitpic / flic.kr / moby.to / yfrog / ts1.in / hellotxt.com / twitxr.com / twitgoo.com

维护了一下之前写的油猴脚本:Twitpic in itweet,从另外一个相同功能的脚本TwitterPhotoShow的代码,通过监控items容器的DOM的事件DOMNodeInserted、DOMSubtreeModified来相应内容的变化,这样就可以把原来的定时器什么一大堆的代码去掉了;

同时利用了一下javascript的对象特性,原先的代码可能看起来还跟C类似的,重写一下语法看起来好像差很远了……改写的好处是添加新的图片服务处理就容易多了。比如一个处理器是这么写的:

1
2
3
4
5
6
7
img_processor['ts1in'] = {
    reg: /ts1\.in\/(\d+)/,
    func:function (url_key, url_elem) {
        var src = "http://ts1.in/thumb/" + url_key[1];
        append_image (src, url_elem);
    }
};

所以新版里面添加了一大堆从TwitterPhotoShow里面抄来,我都没见过的图片服务;虽然TwitterPhotoShow没有支持最近在推上很流行的flickr跟moby.to,可能因为api相对麻烦一点吧!

Tags: ,

暂无评论

Acer Aspire 4736ZG 本本上安家Linux的小问题

早天在新蛋上入手了Acer Aspire 4736ZG本本一台,T4200、1G、NV105M、250G,带蓝牙摄像头等,3600,还送了一台水星无线路由。低端本本为了降低成本,很多都是不装Windows的,这款也是,本来以为原装系统都是Linux,应该对Linux兼容不错的啦,那天到提货点验本本时候,运行一看,傻了,那个什么Linpus,2.6.15的内核,没带X,lshw没有,lspci没有,hwinfo更没有,总之就没什么东西好看的,除了黑漆漆的画面告诉我屏幕没坏点,就匆匆打上包回来了。

第一件事就是用Arch 09.08的Live CD启动重新分区,顺便装好core,但是启动后又傻了,连不上有线网卡……这款机器的网卡是Atheros AR8132的千兆卡,不知道是太新还是太罕见。几经折腾后发现规律:完全关机重开后,Arch就能认到网卡,ipconfig -a能看到eth0,但是ifconfig eth0 up了之后,是这个样子的:

eth0      Link encap:Ethernet  HWaddr 00:26:18:80:C5:AB
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4294967293 errors:4294967278 dropped:4294967290 overruns:4294967293 frame:4294967281
          TX packets:4294967293 errors:4294967284 dropped:0 overruns:4294967293 carrier:4294967291
          collisions:4294967281 txqueuelen:1000
          RX bytes:4294967293 (4095.9 Mb)  TX bytes:4294967293 (4095.9 Mb)
          Interrupt:28

大堆奇怪的数字,这时候不管dhcp还是手动指定IP,都没法正常工作的,如果这时重启,好,完全认不了网卡了,又要完全关机再开一次。

这个问题在Arch论坛上面也有人碰到,他的是华硕eeePC 1005HA,也是上个月的帖子。[详细情况帖子里面比较完备,但是,未解决]

于是我就转战Ubuntu。Ubuntu 9.04的Live CD版本也是没能驱动好这块网卡的,但是这里找到解决方法,总之就是到Atheros官网下载网卡的驱动,编译,挂载驱动模块,幸好Ubuntu的Live CD跟初始系统都带了GCC和Make utils,编译安装过程倒没什么波折,很快就折腾好连上网了。呃,怎么这么简单?于是试试在台式机的Arch上面编译了这个atl1c.ko,哇,一开始就一屏幕的错误……后来发现这个是这个驱动跟内核兼容问题,Ubuntu好像专门有补丁搞定的,Ubuntu 9.04把内核升级到2.6.28.15-generic网卡就完全正常了,也顺便下载了9.10的Live CD,直接iso启动,发现虽然是31内核,但是网卡工作也正常……好吧……看来要么等kernel彻底搞定这块网卡的驱动,不然只能自己找Ubuntu的patch来编译才能跑Arch了……

Ubuntu很是省心,挂上受限驱动什么3D特效全都出来了,跑了下glxgears,大概2600fps,比台式机的集显好一点吧……7025只有1300左右的fps;更新了一下系统,用Ubuntu Tweak添加了一堆三方源后拉好一大堆软件,基本可用了,另外的问题是,声卡的耳机输出没声音!

本本的耳机问题好像从来都是老大难的,因为音频接口的硬件随意性很大,Linux驱动的很难做到通用;本本里面的是ALC888 HD声卡,装了最新的alsa-driver 1.0.21,情况依旧,喇叭有声音,但是耳机就是没反应,也没那个选项有效;后来查看了一些帖子才知道,虽然是同一款驱动,但是有不同的挂载模式的参数,通常就是用来指定这类变化很大的接口等,有些帖子给出一大堆参数列表,说,在/etc/modprbe.d/alsa-base.conf里面更换不同的参数,然后重启测试……天啊,起码上百条的参数,要重启到哪个年月阿……稍微看了下,所谓的挂载参数就是传给modprobe的参数嘛,比如modprobe snd_hda_intel model=ooxx,只需要用不同的参数重新挂载snd_hda_intel,完全没必要重启阿……在Linlap找到一个AudioTester脚本,专门用来测试不同的模块挂载参数的,但是在Ubuntu下用也挺麻烦,正常模式下不让删除snd_hda_intel模块,只好启动到recover mode,手动挂载后运行这个脚本;终于测试到"lenovo-sky"这个参数能让耳机出声,证明还不是残废……但是进入alsamixer里面看,使用这个参数之后,只有寥寥几个选项,连录音都不见了……而且耳机跟喇叭是同时发声的,不能关闭喇叭……

后来想到,这个参数列表应该不是固定的,新的alsa里面应该会有新的参数,不知道有没有专用Acer的参数呢?因为看到原来的列表里面有acer跟acer-aspire,虽然两个没合适这款机器;grep了一下alsa-driver的源代码目录,发现sound/pci/hda/patch_realtek.c里面有一大堆acer xxxx,试了acer-aspire-4730g跟acer-aspire-4730g,设置成4ch/6ch模式后,终于能通过设置Surround让耳机出声,通过Front调整外放喇叭的声音!

Tags: , , , , , , , , ,

评论 (1)

什么是pythonic?

昨天的技术沙龙上,清风大妈Zoom.Quiet给大家提了个“很基础很基本”的问题,什么是pythonic?

自己没特地做过功课,但pythonic这个词不陌生,应该见过几次,但是要说具体是什么,确实很空白。说起python,我首先想起来的是其很让人愉悦的编码体验,一些很常用的封装让人感觉很“惊艳”,比如说,for line in open('file'):ooxxooxx,替代了C++、Java等里面的readline,更不会让人产生在C里面打开文件时候那种恐惧感;还有一些很贴心的细节就是,高度对象化,比如说gtk的TreeViewModel,可以直接送给SQL cursor的execute,直接把用户界面的数据写入到数据库里面去……因为都是list。

像这样的“愉悦感”,就是我理解的pythonic。昨天会场上有人的答案是:“简洁,优雅,高效”,获得比较多人的认可(得到了奖品带大妈签名的《可爱的Python》一本)。

回来后看了看华蟒用户组的首页,呃,很明显写着嘛:申明 pythonic == "大道至简",也八九不离十了。

这两天也是这个邮件组里面有人讨论实现小时候铅笔盒上的九九乘法表,最获得大家认可的答案是:

print "".join([('%s*%s=%s%s' % (y,x,x*y,'\n' if x==y else '\t')) for x in range(1,10) for y in range(1,10) if x >= y])

看了犯晕,这很明显跟python给人“惊艳感”的做法背道而驰的,但怎么会这么多人认可呢……同时想起咋们群主“逆”说一直没搞懂的一句算素数的python:

print reduce(lambda l,y:not 0 in map(lambda x:y % x, l) and l+[y] or l,xrange(2,1000), [] )

这叫做pythonic?既不简洁,又不优雅,更不高效,充其量作为一个脑力游戏,还不如玩玩数独!

玩小聪明一直都是天朝人的传统,人家孔乙己还会“茴”字的N个写法呢,但这类小聪明,着实应该远离python。

Tags: , ,

评论 (2)

Page 1 of 1612345»...Last »