存档

作者存档

搭建私有的 python 包发布中心 pypi

2010年12月12日 3 条评论

项目组现在使用Python越来越多,大部分老逻辑都已经迁移到了Python上,相当数量的新逻辑都是Python写的。经过之前的一段时间的分享,团队已经开始使用 virtualenvsetuptools 来进行Python代码的开发和打包和发布了。

但是现在的问题是,随着项目规模的变大,以及几个子项目的启动,代码复用开始成为一个问题。有很多代码在多个库当中被使用,应该被抽取出来成为单独的模块。但是这些模块和模块的依赖关系会比较复杂,比如一个应用可能需要依赖pypi上开源的库,同时需要依赖一个内部的库,而内部的库又依赖开源的库。如何解决这种情况下的依赖管理和自动包管理呢?

实际上setuptools 和 easy_install 已经提供了完善的依赖管理,在setup.py当中写上所有依赖的模块名已经在团队内形成了共识,那么内部模块是否可以用同样的方式进行管理呢?

答案是肯定的。easy_install 本质是 pypi 服务的客户端,它依赖的web服务称为 pypi,或者叫Cheese Shop。是 http://pypi.python.org/simple/ 。这个页面下面就是一系列的 index.html 文件,指向各个版本的包文件。这个index.html本身没有严格的格式规定,只是其中应该包含<a>标签,指向每个版本。easy_install 负责抽取出这些标签,形成一个文件列表,比较版本,下载指定版本或者最近版本,并安装到系统上。

而且,easy_install支持一个命令行参数 –index-url,或者短参数 -i ,可以指定兼容于 pypi 的 pypi 索引。这个索引只要满足pypi的规定就是可以的,自然,这个是可以自己搭建的。

但是easy_install有一个限制,就是只能指定一个index URL。对于多个index的问题,PEP381明确说了,这是客户端的问题。easy_install选择不解决这个问题,也是一种解决方案吧……

但是easy_install不解决,我们就要想办法自己解决。去除这个限制有两个方法,一个是让自己的私有pypi在发现私有包里面没有匹配的包名字的时候重定向到pypi.python.org/simple,另一种方法就是使用 easy_install 的替代品 pip。在翻看了众多的部署脚本之后,我们决定,还是使用前面一个策略。

除了常用的easy_install来安装包,pypi还需要一个功能就是支持 distutils 兼容的协议上传包到服务器。对于使用disutils或者setuptools建立的setup.py文件,开发者可以使用 python setup.py register 将项目名注册到 pypi index,也可以通过 python setup.py bdist_egg upload 上传打包好的文件。这个协议很简单,很容易即可实现,只是其中需要的用户管理方面,稍微复杂和体力活一些。

也正是因为简单,搭建私有的pypi服务器的开源程序有很多,PEP 381当中有两个推荐,分别是PloneSoftwareCenter 和 EggBasket。PloneSoftwareCenter是一个恐龙级别的东西,它是一个完整的CMS,pypi只是其中一个小小的功能。为了这样一个简单的功能需要安装一大堆Plone的东西,实在是难以接受,而且它的文档简直是个杯具……,唉。

EggBasket稍好,但是也要安装一堆东西,包括一只小恐龙TurboGears。所幸TurboGears只是一只小恐龙,而且EggBasket本身的文档比较清楚,一步步照着做即可。由于一些安全方面的限制,EggBasket单独的服务器端口在我们的服务器上是不能访问到的,因此我们用apache的mod_proxy做了一个反向代理。

很快,基于EggBasket和apache mod_proxy反向代理的私有pypi就搭建起来,问题随即而来:EggBasket不支持我们上面要求的自动重定向。所幸源代码也不多,简单修改了一下之后,做了一个patch。需要的可以下载下来自己apply。我已经联系了EggBasket的作者,希望能够将这个patch合并进官方代码,但是作者表示,他现在正在休假。【update @2010-12-12 自从这个patch发送过去已经接近半年了,还没有响应,好吧,我放弃了】

经过这些patch,我们的pypi服务器就成功搭建起来了。项目组使用它的方式是:

开发机:
(dev) zhangc@dev-01:pypismpl$ python setup.py register -r http://pypi-server/pypi
We need to know who you are, so please choose either:
1. use your existing login,
2. register as a new user,
3. have the server generate a new password for you (and email it to you), or
4. quit
Your selection [default 1]: 1
Username: zhangc
Password: ********
Server response (200): OK
I can store your PyPI login so future submissions will be faster.
(the login will be stored in /home/zhangc/.pypirc)
Save your login (y/N)? y

这步操作执行一次即可,如果最后一步选择了save login,则后面不再需要每次都register。

在程序新版本稳定了之后,执行:
(dev) zhangc@dev-01:pypismpl$ python setup.py bdist_egg upload -r http://pypi-server/pypi/upload

即可把新版本的程序打包成egg并且上传到服务器。

这时候如果通过浏览器访问pypi服务器,会发现新版本的pypismpl程序已经在页面上列出了。

然后在生产机上:
(dev) zhangc@production-01:~$ sudo -u appuser -E /usr/app/env/bin/python -i http://pypi-server/pypi -U pypismpl

新版本的程序就会自动部署了。

标签:

谈一个Kernel32当中的ANSI到Unicode转换的问题

2010年6月17日 3 条评论

【这篇文章就是我在twitter上说过的那篇文章,早已写好,但由于一些原因压到现在才发表,深表歉意】

众所周知,Windows的几乎所有带有字符串参数的API都是有W和A两个版本,分别对应于Unicode和ANSI版本。同时,Windows内部是使用Unicode的,因此所有的A版本的API函数都是实际上调用了一次ANSI到Unicode的字符集转换之后,再调用Unicode版本的函数。

你真的清楚这中间的这一步转换吗?普通的工程师确实不需要了解太深刻,但是我们是做安全软件的,安全软件的一个基本实现机制就是Hook,就是在原本的API调用路径上面拐一个弯,改变原本的API执行流程,以便在其中插入安全检查等逻辑。

这当中,如果没有对于所要Hook的函数的深刻理解,也许会有很多看似灵异的事件出现。在我们的代码当中,我们就使用了Hook的机制,然后很幸运的(很不幸?),遇到了这样一个灵异问题:

在测试当中,发现某个软件一旦被我们Hook,就总是崩溃。必现的崩溃总是很容易解决的定位的,很快,我们发现崩溃来源于下面一个调用序列:

HMODULE hMod = LoadLibraryA("Kernel32");
PROC pfxIsWow64Process = GetProcAddress(hMod, "IsWow64Process");

对LoadLibrary的调用返回没有检查返回值,进而继续调用GetProcAddress。那么,当hMod = NULL的时候,GetProcAddress并不检查hMod是否是0,而直接试图去寻找其中的导出表,于是导致了非法的内存访问。

但是,这里的代码当中,调用的是LoadLibraryA(“kernel32″),kernel32.dll作为系统的核心链接库,通常情况下几乎必然是已经加载到了进程空间当中,即使没有,加载此DLL也不应该失败。

但是调试的结果显示,这个返回值的确是NULL,看起来相当灵异。但是我们相信,计算机没有灵异事件。为了排错,我们祭出WinDbg和IDAPro,从汇编层面一步步跟踪LoadLibraryA函数,看看中间究竟发生了什么。经过漫长的错误定位工作,在此略过不表(关于整个排查过程可以写一篇专门的文章),我们终于发现了问题的所在。

没错,加载了Hook就会造成崩溃,因此崩溃的原因在于Hook。

我们使用Inline Hook技术修改了LoadLibrary函数的执行流程,使得在进行真正的DLL加载之前进行某些校验和检查的工作。LoadLibrary函数其实是一个函数族,包括LoadLibraryA,LoadLibraryW,LoadLibraryExA,和LoadLibraryExW。它们之间的调用关系如下图:

我们选择了在整个调用依赖树的最底端的LoadLibraryExW的入口点作为Hook点,当一个函数调用LoadLibraryExW的时候,其实是调用到我们的Hook函数,Hook函数再调用真正的LoadLibraryExW,经过Hook,一个典型的LoadLibraryA的调用流程如下图:

问题就出在了这个CheckLibrary函数里面。在这个函数里面,我们调用了一个公共模块当中的日志函数,这个日志函数的逻辑是,以追加模式打开一个日志文件,写入然后关闭。且不论这个日志函数这样做的合理性,先来看看这段代码:

void Log(const char* msg) {
FILE* pf = fopen(_g_log_file, "a");
fprintf(pf, "%s\n", msg);
fclose(pf);
}

非常直观的程序,怎么会带来问题呢?我们来继续分析fopen函数。fopen函数是c库提供的文件IO函数,最终调用到Windows API CreateFileA,然后CreateFileA会调用到CreateFileW。把这个调用关系加上,调用序列如下图所示:

在这个函数调用序列当中,存在两次ANSI 到 Unicode 的转换,即图中标记有五角星的两个地方。那么我们来看看两个转换的代码是什么样子的:

LoadLibraryExA:

CreateFileA

注意到其中高亮的部分了么?两个函数在进入之后,都调用了一个公共的函数 _Base8bitStringToStaticUnicodeString,从名字就可以看出来,这段代码负责将输入的ANSI字符串转换为Unicode。那么这个函数做了什么呢?跟进去看看(这个使用了Hex-Rays的反编译功能,看得更清楚一点):

再仔细看给RtlAntiStringToUnicodeString这个函数传入的参数地址pUnicodeString,来自*MK_FP(__FS__, 24) + 3064,这是Hex-Rays的表示法,其实对应下面一串汇编序列(由于编译器的优化导致的乱序,重点看红线标出来的指令):

large fs:18h 这句魔法一样的指令,其实是 TEB 的地址。这个地址对于同一个线程来说是一样的,因此,如果在一个线程当中调用这个函数,总是会引用到同一个地址,这个地址就是当前线程的TEB当中的一个静态缓冲区(StaticUnicodeString & StaticUnicodeBuffer)。

很明显,这个静态缓冲区的作用是性能优化,对于ANSI到Unicode的转换,复用一个thread-specific的缓冲区来接受目标Unicode字符串。对于不太长的函数参数字符串,这个优化可以避免一次堆分配的过程,对性能的提升是很明显的。正常情况下这个优化是合理而且有效的。但是在存在Hook的情况下,事情就不是这么简单了。考虑一下上面那张图当中的LoadLibrary的调用序列:

解释一下:

  1. 在第1次ANSI到Unicode转换的时候, LoadLibraryA将目标Unicode字符串 L”kernel32″ 存入 StaticUnicodeBuffer,然后将这个字符串的首地址传递给LoadLibraryW
  2. LoadLibraryW在调用到LoadLibraryExW之前,先调用到了Hook函数
  3. Hook函数调用到了CreateFileA,传入日志文件名(ANSI)作为函数参数
  4. CreateFileA进行第二次ANSI到Unicode转换,将目标Unicode字符串(日志文件名)存入了StaticUnicodeBuffer,然后将这个字符串首地址传给CreateFileW
  5. CreateFileW返回,
  6. CreateFileA返回,
  7. Hook函数返回,
  8. 调用到实际的LoadLibraryExW。此时LoadLibraryExW从参数指针当中取出字符串试图进行加载动态库的操作,但是,它取出的是被在第4步当中覆盖掉的字符串,也就是Unicode版本的日志文件名!试图加载日志文件,自然会收到LoadLibrary失败的返回。‘
  9. LoadLibraryExW返回NULL,LoadLibraryW返回NULL,LoadLibraryA返回NULL。

然后,没有检查LoadLibraryA返回值的程序,在调用GetProcAddress的时候,biu~ 掉了。

好了,知道了为什么,怎么解决也就很容易了,在CheckLibrary当中,先备份传入的参数的内容,然后继续走流程,等到需要调用原本函数的时候,将备份的参数传给原本函数。

这就是Hook爱好者们的义务:当你试图改变某个执行流程的时候,请务必清楚地了解关于这个流程的一切。

当心一种利用手机和银行转账汇款的火车票诈骗

2010年4月27日 6 条评论

今天同事去买车票的时候遇到的。虽然最终我们并没有上当,但是还是把整个流程在这里写出来,以便更多的人识别骗局。

首先把这个骗局场景给大家讲述一下,碰到类似的情况,请对号入座,看看自己是不是受害人或者受害人的朋友。为了讲述方便,我们把这个事情当中的角色用三个名字来说明:买票人、黄牛、以及受害人的朋友(简称朋友,是付款人)。其中黄牛就是骗子。在我碰到的这个案例当中,同事的角色是买票人,我的角色是付款人。

这个骗局当中,买票人在黄牛的指引下,引导买票人通过手机和一个朋友联系,并精心设计流程得手。这个过程如下:

  1. 黄牛事先知道某车次的票被售罄,然后以此车次的票为诱饵在网上发帖转让;或者笼统的说自己可以搞到票,等待受害者上钩。
  2. 买票人需要去购买火车票,但是很不幸,他要买的的火车票没有了;
  3. 买票人通过某这方式找到黄牛,并且联系上了他;
  4. 黄牛告诉买票人,他可以搞到票,但是由于某种原因(原因五花八门,但通常黄牛声称是内部人士,需要规避风险),他不能进行现金交易,而需要通过下面的步骤来进行:
    1. 买票人需要用自己的手机联系一位朋友,并且这样告诉他的朋友:“我在买票,有人能搞到票,但是需要你帮忙把钱汇到某个账户,然后他会给我票。”,并且把下面的步骤向朋友解释;
    2. 黄牛用自己的手机联系买票人的朋友,重复说明该流程,并提供一个银行帐户给买票人的朋友;
    3. 黄牛将票给买票人,买票人将自己的手机给黄牛;
    4. 黄牛使用买票人的手机联系买票人的朋友,这说明第3步已经完成,买票人已经拿到了票,而自己正拿着买票人的手机,朋友需要转账给提供的账户来让买票者赎回手机(在给买票者及其朋友讲述这个流程的时候,骗子强调,由于票已经在给了买票人,只有当他拿到了票款,才能把手机还回去,防止买票人拿回手机之后不付钱);
    5. 朋友将钱汇到黄牛提供的帐号;
    6. 黄牛确认钱已经收到之后,将手机还给买票人;
    7. 买票人带走车票,回去之后将票款还给朋友。
  5. 买票人按要求执行 4.1 当中的操作,给一个朋友打电话并解释;
  6. 黄牛执行 4.2 当中的操作,给该朋友打电话并解释,同时提供帐号;
  7. 黄牛和买票人执行 4.3 当中的操作,用手机交换票;
  8. 黄牛用买票人的手机打电话给他的朋友,要求汇款;
  9. 朋友汇款;
  10. 黄牛确认款项收到之后,将手机还给买票人;
  11. 交易完成

这个过程有什么问题么?看起来无懈可击,通过使用手机和票的抵押,交易双方的利益都得到了保证。而依靠手机号码的识别性,朋友也能够验证买票人的身份。

但是整个流程当中仍然有几个可疑的地方,就是上面的这些红色标记的地方。

首先,买票人和黄牛一定是面对面的,因为两人可以互相交换手机和票,因此,没有理由两人不可以交换人民币。而且,火车票之类的东西并非非常高价,通常数百元,现金并不多。当然,在我同事遇到的这个例子当中,黄牛宣称他和我的同事之间隔着一块玻璃墙,因为他提供的是“内部票”,但是这个借口仍然经不起推敲。隔着一个玻璃墙递送手机,比递送人民币更加引人注目。而且,根据我对火车站流程的了解,他们不存在所谓的“内部票”一说。

第二,如果当我接到电话,是黄牛在说话,但是是我同事的手机号的时候,能够证明黄牛正在使用我同事的手机吗?很多人也许认为是可以的,但是当你知道这个世界上存在一种叫做手机改号软件提供商)的时候,你就能够知道其实这个什么都不能证明。改号软件利用的是电信系统来电显示的漏洞,通过非法的电话网关接入运营商的网络,并且伪造拨打方的号码。通过此类软件,骗子实际上可以假冒任何人的身份进行通信。作为可能的受害者的朋友,应对此类系统的策略其实很简单,一种方式是要求和朋友讲话(这也是很多警匪片里面侦探确认人质仍然活着的方式);另一种方式是在收到朋友的手机号打来的电话请求时,不是接听而是回拨。第一种方式在我遇到的这个例子当中已经被骗子废掉了,因为骗子强调,为了保护他的权益,他不能把手机还给买票人,而正如他之前所说,他和买票人之间隔着一堵 玻璃墙,因为他拿“内部票”是需要到售票窗里面去的,因此我也不可能听见同事的声音。但是第二种方式仍然是可用的,因为手机改号软件本身的缺陷,回拨的请求会真的发送到同事的手机上,而不是骗子伪造的通话端。因此,通过接到电话回拨的方式,一定程度上可以完成一次相对可靠的认证,从而避免上当受骗。

你知道了改号软件,那么就很容易想到,在上面的步骤当中,在第四步完成之后,买票人和黄牛分别为买票人的朋友解释了流程之后,很可能的过程是:

  1. 骗子借口离开买票人(这个很可能是通过说“我去拿票”);
  2. 骗子使用号码伪造软件,伪造买票人的手机号码向买票人的朋友通话,要求转账;
  3. 朋友转账;
  4. 骗子消失,朋友和买票人受骗。

随便Google了一下,这种诈骗手段还真是比较新的,大部分都是最近的帖子:
案例1
案例2案例3案例4

我和同事很幸运,没有成为那个上当的一对通信者。回顾整个过程,讲讲我的判断过程,也希望能够给其他人防范未知诈骗提供一些帮助。

在同事给我打电话讲述流程的时候,我问了他两个问题:1. 你是否看到这个人了?2. 你是否看到票了。同事对第一个回答是是,第二个却是否。这个是第一个引起我怀疑的地方,甚至,我重新检查了来电号码,仔细辨认了同事的声音,以便确定电话的确是他打来的,同时仔细听了背景声音,确定他的确在火车站。但是我还是建议他先看到票,然后再说交易。无论什么交易,先看到货,再谈交易是基本原则,除非有一个足够强势的第三方担保,例如淘宝。

在同事挂掉电话之后,我正在琢磨整个流程的时候,我接到了骗子的电话。同事之前完全没有告诉我骗子会给我打电话。而骗子在电话接通之后就非常流利的开始讲解整个流程,坦率地讲,这个骗子是一个优秀的社会工程学专家,在整个过程当中他语速一致,条例清楚,伪装也很巧妙,尤其是通过“内部人士”的角色,成功规避了“要求听到同事的声音”这种请求。但是我还是从对话当中发现了他的一些疑点:

  1. 此人能够给我打电话,讲述这样一个复杂的流程,却不能接受现金交易,而且借口是为了逃避监管。而实际上,为了逃避监管,现金交易是首选,转帐反而会留下证据(大家不知道发现金的那些单位么?),这是疑点一。
  2. 第二,骗子一直在强调,他和同事是“隔着玻璃”的,但是同事说,他们在一起。
  3. 骗子坚持要求我使用ATM转帐而不是专业版网银,而且只透漏帐号,拒绝透漏帐户名(ATM转帐不需要户名,户名是在输入帐号之后显示的,但是专业版转帐是需要先提供户名,由系统进行匹配的),目的也许是为了让我能够远离互联网,我于是忽悠他说我已经在ATM机旁边了,对方明显松了一口气,然后转而告诉我,别着急,等他电话。

这些疑点足够我产生怀疑了。接下来就是搜索。使用“火车票”,“转帐”,“手机”等关键字组合搜索之后,很快就发现了大量的此类案例,然后立即拨同事的电话,告知他一切。而这个时候,同事正在和他一起走向某个地方的路上。同事听明白我讲的话之后,挂掉电话。

不知道他和骗子说了什么,骗子马上打来了一个电话,是他的号码,我还没有接,他就挂掉了,也许是做贼心虚了。

公开骗子的电话和帐号:手机 18773861321,帐号 6225 8878 3931 1236。大家可以搜索一下,这个号码在很多网站都留了联系方式。

PS: 推荐《欺骗的艺术》(又译《入侵的艺术》)一书,我所学习的识别骗子的技巧,相当一部分来自此书。【下载

标签: ,

CALL指令有多少种写法

2010年4月4日 6 条评论

最近有一个需求,给你个地址,看看这个地址前面是不是一个CALL指令(请同学们自行联想该需求的来源)。作为团队的救火队员+炮灰,这个简单的事情自然落在了我的头上。

这个事情很简单,作为一个善于站在别人肩膀上的程序员我们可以考虑使用 libdisasm;如果要考虑x64,就试试udis86;如果需要用Python,就有Python包装好的 pydasm。不过这两个400KB+的库,显然不值得为了一个CALL指令导入到编译出来大小仅仅100K不到的项目代码里面。

那么就自己抽一个CALL指令解码逻辑出来好了。这个逻辑的复杂性在于,你无法知道前面一个CALL指令有多长。因此,首先需要枚举出所有的CALL指令格式。

Intel有公开的指令集格式文档,你需要的是第二卷的上半部分,指令集从A到M。这篇文档的难度超出一般人想象,里面有众多晦涩的标识、与硬件紧密相关的介绍,拿到这后,即使直接翻到目录的CALL 指令一节,也不见得能够弄清楚。不相信?我们就翻到那里看看:

CALL指令格式一览表

CALL指令格式一览表

虽然很明确的列出,第一列是指令的二进制形式,第二列是指令的汇编形式,但是面对着 E8 cw, FF/2这样的标识,一样不知道究竟对应的二进制格式是什么样的。

那好,我们就从理解这些标识开始。文档向前翻,有一个专门的节(3.1.1 Instruction Format)讲述这些标识的含义。这里抽出其中两个用得着的翻译一下:

表格中的“Opcode”列列出了所有的所有可能的指令对应的二进制格式。有可能的话,指令代码使用十六进制显示它们在内存当中的字节。除了这些16进制代码之外的部分使用下面的标记:

cb, cw, cd, cp, co, ct — opcode后面跟着的一个1字节(cb),2字节(cw),4字节(cd),6字节 (cp),8字节(co) 或者 10字节(ct) 的值。这个值用来表示代码偏移地址,有可能的话还包括代码段寄存器的值。

/digit — digit为0到7之间的数字,表示指令的 ModR/M byte 只使用 r/m字段作为操作数,而其reg字段作为opcode的一部分,使用digit指定的数字。

红字部分不知道什么含义?没关系,我们先不看它。对于cb/cw之类的,基本上能够简单看明白其中的一些指令含义了:

E8 cw 的含义是:字节 0xE8 后面跟着一个2字节操作数表示要跳转到的地址与当前地址的偏移量。
E8 cd 的含义是:字节 0xE8 后面跟着一个4字节的操作数表示要跳转的地址与当前地址的偏移量。
9A cd 的含义是:字节 0x9A 后面跟着一个6字节的操作数表示要跳转的地址和代码段寄存器的值。

那么,同样的0xE8开头的指令,CPU如何区分后面的操作数是2字节还是4字节?答案是和CPU的模式有关,在实模式下,0xE8接受2字节操作数,而32位保护模式下接受4个字节,64位保护模式下同样接受4字节,同时需要对该操作数进行带符号扩展。

因此,CALL指令的前两种格式是:E8 xx xx xx xx,和 9A xx xx xx xx xx xx。一个是5字节长,一个是7字节长。其实E8 那种,就是我们在汇编指令里面写 CALL lable之后产生的,最常见的CALL指令。

然后是下面的FF /2。这个是0xFF字节后面跟上一个blablabla的东西。这个blablabla的东西是什么呢?要解释这个,首先需要知道红字标出来的部分,即ModR/M是什么东西。

这个要先回到最基本的一个问题:IA32的指令格式。

IA32,64指令格式

IA-32,Intel 64指令格式

其中每个部分是什么含义呢?

首先是指令前缀。有印象的应该记得当年学习微机原理的时候提到过得循环前缀 repnz/repne,这个前缀就是被编码在指令的前面部分的。每个前缀最多一个字节,一条指令最多4个前缀。

然后是指令代码(opcode),这部分标识了指令是什么。这个是指令当中唯一必需的部分。前面例子当中的 0xE8,0xFF都是opcode。

再后面就是我们要重点关心的 ModR/M字段了,还有和它密切相关的SIB字节。手册2.1.3当中有对于它们的详细描述。

许多指令需要引用到一个在内存当中的值作为操作数,这种指令需要一个称为寻址模式标识字节(addressing-form specifier byte),或者叫做ModR/M字节紧跟在主opcode后面。ModR/M字节包含下面三个部分的信息:

  • mod(模式)域,连同r/m(寄存器/内存)域共同构成了32个可能的值:8个寄存器和24个寻址模式。
  • reg/opcode(寄存器/操作数)域指定了8个寄存器或者额外的3个字节的opcode。究竟这三个字节用来做什么由主opcode指定。
  • r/m(寄存器/内存)域可以指定一个寄存器作为操作数,或者可以和mod域联合用来指定寻址模式。有时候,它和mod域一起用来为某些指令指定额外的信息。

这一段有些晦涩。其意思解释一下是这样的:一个指令往往需要引用一个在内存当中的值,典型的就是如mov:

MOV eax, dword ptr [123456]
MOV eax, dword ptr [esi]

这其中的 123456 或者 esi 就是 MOV 指令引用的内存地址,而MOV关心的是这个地址当中的内容。这个时候,需要某种方式来为指令指定这个操作数的类型:是一个立即数表示的地址,还是一个存放在寄存器当中的地址,或者,就是寄存器本身。

这个用来区分操作数类型的指令字节就是 ModR/M,确切的说是其中的5个位,即mod和r/m域。剩下的三个位,可能用来做额外的指令字节。因为,IA32的指令个数已经远超过一个字节所能表示的256个了。因此,有的指令就要复用第一个字节,然后依据ModR/M当中的reg/opcode域进行区分。

现在回头看前面的红字标识的部分,能不能理解 /digit 这种表示法了?

对于SIB的介绍,我们先忽略,看看对于CALL指令的枚举我们已经能做什么了。

CALL指令的表示法:FF /2,是 0xFF 后面跟着一个 /digit 表示的东西。就是说,0xFF后面需要跟一个 ModR/M 字节,ModR/M字节使用 reg/opcode 域 = 2 。那么,reg/opcode = 2 的字节有32个,正如ModR/M的解释,这32个值代表了32种不同的寻址方式。是哪32种呢?手册上面有张表:

32字节寻址模式下的ModR/M字节

32字节寻址模式下的ModR/M字节

非常复杂的一张表。现在就看看这张表怎么读。

首先是列的定义。由于 reg/opcode 域可以用来表示opcode,也可以用来表示reg,因此同一个值在不同的指令当中可能代表不同的含义。在表当中,就表现为每一列的表头都有很多个不同的表示。我们需要关心的就是 opcode 这一个。注意看我用红圈圈出来的部分,这一列就是 opcode=2 的一列。而我们需要的 CALL 指令,也就是在这一列当中,0xFF后面需要跟着的内容。

行的定义就是不同的寻址模式。正如手册所说,mod + R/M域,共5个字节,定义了32种寻址模式。0×10 – 0×17 对应于寄存器寻址。例如指令 CALL dword ptr [eax] :[eax]寻址对应的是 0×10,因此,该指令对应的二进制就是 FF 10。同理, CALL dword ptr [ebx] 是 FF 13,CALL dword ptr [esi] 是 FF 16,这些指令都是2个字节。有人也许问 CALL word ptr [eax] 是什么?抱歉,这不是一个合法的32位指令。

0×50-0×57部分需要带一个 disp8,即 8bit 立即数,也就是一个字节。这个是基地址+8位偏移量的寻址模式。例如 CALL dword ptr [eax+10] 就是 FF 50 10 。注意虽然表当中写的是 [eax] + disp8 这种形式,但是并不表示是取得 eax 指向的地址当中的值再加上 disp8,而是在eax上加上disp8再进行寻址。因此写成 [eax+disp8] 更不容易引起误解。后面的disp32也是一样的。这个类型指令是3个字节。

0×90 – 0×97部分需要带 disp32,即4字节立即数。这个是基地址+32位偏移量。例如 CALL dword ptr [eax+12345] 就是 FF 90 00 01 23 45。有趣的是, CALL dword ptr [eax+10] 也可以写成 FF 90 00 00 00 10。至于汇编成哪个二进制形式,这是汇编器的选择。这个类型的指令是6个字节。

0xD0 – 0xD7部分则直接是寄存器。这边引用的寄存器的类型有很多,但是在CALL指令当中只能引用通用寄存器,因此 CALL eax 就是 FF D0,臭名昭著的 CALL esp 就是 FF D4。注意 CALL eax 和 CALL [eax] 是不一样的。这些指令也是2个字节。

仔细的人也许主要到了,在表当中,0×14, 0×15, 0×54和0×94是不一样的。0×15比较简单,这个要求 ModR/M后面跟上一个32位立即数作为地址。即常见的 CALL dword ptr [004F778e] 这种格式的,直接跳转到一个固定内存地址处存放的值,常见于调用Windows的导出表。对应的二进制是 FF 15 00 4F 77 8E ,有6个字节。

0×14,0×54,0×94部分是最复杂的,因为这个时候,ModR/M不足以指定寻址方式,而是需要一个额外的字节,这个字节就是指令当中的第4个字节,SIB。同样在手册的2.1.3,紧跟着ModR/M的定义:

某些特定的ModR/M字节需要一个后续字节,称为SIB字节。32位指令的基地址+偏移量,以及 比例*偏移量 的形式的寻址方式需要SIB字节。 SIB字节包括下列信息:

  • scale(比例)域指定了放大的比例。
  • index(偏移)域指定了用来存放偏移量 的寄存器。
  • base (基地址)域用来标识存放基地址的寄存器。

0×14, 0×54, 0×94就是这里所说的“特定的ModR/M字节。这个字节后面跟着的SIB表示了一个复杂的寻址方式,典型的见于虚函数调用:

CALL dword ptr [ecx+4*eax]

就是调用ecx指向的虚表当中的第eax个虚函数。这个指令当中,因为没有立即数,因此FF后面的字节就是0×14,而 [ecx+4*eax] 就需要用SIB字节来表示。在这个指令当中,ecx就是 Base,4是Scale,eax是Index。

那么,Base, Scale和Index是如何确定的呢?手册上同样有一张表(又是巨大的表):

32位寻址模式当中的SIB字节

32位寻址模式当中的SIB字节

列是Base,行是Index*Scale,例如[ecx+4*eax] 就是0×81。

根据这张表,CALL dword ptr [ecx+4*eax] 就是 FF 14 81 。由此可见,对于 0×14系列的来说,CALL指令就是 3个字节。
而 0×54 带 8bit 立即数,就是对应于 CALL指令:CALL dword ptr [ecx+4*eax+xx],这个指令就是 FF 54 81 xx,是4个字节。
同理,0×94带32位立即数,对应于CALL指令:CALL dword ptr [ecx+4*eax+xxxxxxxx],这个指令就是 FF 94 81 xx xx xx xx,是7个字节。

OK,截止到目前,我们基本上能够列出常见的CALL指令的格式了:

指令 二进制形式
CALL rel32 E8 xx xx xx xx
CALL dword ptr [EAX] FF 10
CALL dword ptr [ECX] FF 11
CALL dword ptr [EDX] FF 12
CALL dword ptr [EBX] FF 13
CALL dword ptr [REG*SCALE+BASE] FF 14 xx
CALL dword ptr [abs32] FF 15 xx xx xx xx
CALL dword ptr [ESI] FF 16
CALL dword ptr [EDI] FF 17
CALL dword ptr [EAX+xx] FF 50 xx
CALL dword ptr [ECX+xx] FF 51 xx
CALL dword ptr [EDX+xx] FF 52 xx
CALL dword ptr [EBX+xx] FF 53 xx
CALL dword ptr [REG*SCALE+BASE+off8] FF 54 xx xx
CALL dword ptr [EBP+xx] FF 55 xx
CALL dword ptr [ESI+xx] FF 56 xx
CALL dword ptr [EDI+xx] FF 57 xx
CALL dword ptr [EAX+xxxxxxxx] FF 90 xx xx xx xx
CALL dword ptr [ECX+xxxxxxxx] FF 91 xx xx xx xx
CALL dword ptr [EDX+xxxxxxxx] FF 92 xx xx xx xx
CALL dword ptr [EBX+xxxxxxxx] FF 93 xx xx xx xx
CALL dword ptr [REG*SCALE+BASE+off32] FF 94 xx xx xx xx xx
CALL dword ptr [EBP+xxxxxxxx] FF 95 xx xx xx xx
CALL dword ptr [ESI+xxxxxxxx] FF 96 xx xx xx xx
CALL dword ptr [EDI+xxxxxxxx] FF 97 xx xx xx xx
CALL EAX FF D0
CALL ECX FF D1
CALL EDX FF D2
CALL EBX FF D3
CALL ESP FF D4
CALL EBP FF D5
CALL ESI FF D6
CALL EDI FF D7
CALL FAR seg16:abs32 9A xx xx xx xx xx xx

有了这个列表,写一段代码来完成最初我们的需求也就不难了。

标签: ,

Sergey Brin 的童年

2010年3月14日 1 条评论

最近因为Google的退出事件,越来越多的新闻开始对这个行为进行深挖,越来越多的消息开始指向Google的创始人之一的Sergey Brin。Sergey Brin被称为Google的良心,而Google的“不作恶”当中的“作恶”的定义也就是“Brin说是恶的,那就是恶的”。这个激起了我了解Sergey Brin的经历的好奇心。正好wikipedia上面记录了Sergey Brin的童年介绍,看后很受触动。

Sergey Brin在6岁以前在苏联度过,他的父亲在他6岁那年决定离开苏联移居美国。我可以理解他的父亲对于苏联和苏联式的社会主义的失望,也许还有恐惧,否则他也不会做出这样的决定。1979年,正是那一个社会主义实体第60年的时候,正是一个一直怀揣梦想的人第一次接触到西方社会之后的一年之后,一切都和现在的我如此相似。如果我做了和米歇尔一样的事情的话,将来,我的孩子,会对着我的眼睛说出和Sergey一样的话么。

– 翻译开始 –

1979年, 当布林6岁的时候,他的家庭感到不得不移居美国。在和《The Google Story》作者Mark Malseed的一次采访当中,谢尔盖的父亲解释他如何“在进入大学之前就被扼杀了他成为天文学家的梦想。根据官方说法,反犹太主义在苏联是不存在的,但事实上,共产党禁止犹太人进入大学任教,尤其被禁止进入物理系……”。米歇尔.布林(谢尔盖的父亲)因此不得不将其专业换成了数学,在那里他几乎门门得到A。但是,“几乎没有人认为我可以进入研究生院,因为我是犹太人”。布林全家住在莫斯科中心的一个30平方米的3室的公寓里,而且和他的祖母同住。谢尔盖告诉Malseed:“我很久之前就知道父亲不能去追逐他的职业梦想”,但是谢尔盖只记得到达美国之后的事情的详细情况了。他记起,1977年,当他父亲从波兰华沙的一次数学会议上回来的时候,他宣布是时候考虑全家移民了。“我们不能再在这里待下去了”,他告诉妻子和母亲。在那次会议上,他能够“自由地和来自美国、法国、英国和德国的同行们交流,发现他西方世界当中的同类们并‘不是怪物’”,他还说,“我是家里唯一感到离开是如此重要的事情的人”。

谢尔盖的母亲不想离开他们在莫斯科的家,她在哪里度过了一生。Malseed写道:“对于伊吉尼亚来说,决定权完全落在了谢尔盖的头上。而她的丈夫承认他考虑他自己的未来的同时也在考虑谢尔盖的未来,而且,80%都是在考虑谢尔盖”。他们1978年9月申请了离境签证,随后他的父亲被“立即辞退”,因为相关的原因,他们的母亲也被迫离开工作岗位。在剩下的8个月里,没有固定收入,他们不得不在等待的时间里去做一些临时工作,而且不知道他们的申请是否能够获得批准。在这段时间里,谢尔盖的父母轮流照顾他,他的父亲学会了计算机编程。1979年5月,他们拿到了离境签证,被允许离开这个国家。

在这个2000年10月的采访当中,谢尔盖说到:“我知道我的父母那段时间艰难,我感谢他们把我带到了美国”。10年前,1990年的夏天,谢尔盖17岁生日前的几周,他的父亲带着一群有数学天赋的高中生,包括谢尔盖,到苏联参加一个为期两周的交换学生项目。 根据谢尔盖的回忆,这次旅行“唤醒了他童年对于威权的恐惧”,他还记得,他第一次对被苏维埃压迫感到冲动,并且向警车扔石头。Malseed补充道:活动的第二天,当他们游览到莫斯科郊外的一个疗养院的时候,谢尔盖走到他父亲的旁边,望着他的眼睛说:“谢谢你把我们都带出了苏联”。

[翻译] [RabbitMQ+Python入门经典] 兔子和兔子窝

2010年3月14日 14 条评论

RabbitMQ作为一个工业级的消息队列服务器,在其客户端手册列表的Python段当中推荐了一篇blog,作为RabbitMQ+Python的入门手册再合适不过了。不过,正如其标题Rabbit and Warrens(兔子和养兔场)一样,这篇英文写的相当俏皮,以至于对于我等非英文读者来说不像一般的技术文档那么好懂,所以,翻译一下吧。翻译过了,希望其他人可以少用一些时间。翻译水平有限,不可能像原文一样俏皮,部分地方可能就意译了,希望以容易懂为准。想看看老外的幽默的,推荐去看原文,其实,也不是那么难理解……

原文:http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/

兔子和兔子窝

当时我们的动机很简单:从生产环境的电子邮件处理流程当中分支出一个特定的离线分析流程。我们开始用的MySQL,将要处理的东西放在表里面,另一个程序从中取。不过很快,这种设计的丑陋之处就显现出来了…… 你想要多个程序从一个队列当中取数据来处理?没问题,我们硬编码程序的个数好了……什么?还要能够允许程序动态地增加和减少的时候动态进行压力分配?

是的,当年我们想的简单的东西(做一个分支处理)逐渐变成了一个棘手的问题。以前拿着锤子(MySQL)看所有东西都是钉子(表)的年代是多么美好……

在搜索了一下之后,我们走进了消息队列(message queue)的大门。不不,我们当然知道消息队列是什么,我们可是以做电子邮件程序谋生的。我们实现过各种各样的专业的,高速的内存队列用来做电子邮件处理。我们不知道的是那一大类现成的、通用的消息队列(MQ)服务器——无论是用什么语言写出的,不需要复杂的装配的,可以自然的在网络上的应用程序之间传送数据的一类程序。不用我们自己写?看看再说。

让大家看看你们的Queue吧……

过去的4年里,人们写了有好多好多的开源的MQ服务器啊。其中大多数都是某公司例如LiveJournal写出来用来解决特定问题的。它们的确不关心上面跑的是什么类型的消息,不过他们的设计思想通常是和创建者息息相关的(消息的持久化,崩溃恢复等通常不在他们考虑范围内)。不过,有三个专门设计用来做及其灵活的消息队列的程序值得关注:

Apache ActiveMQ 曝光率最高,不过看起来它有些问题,可能会造成丢消息。不可接受,下一个。

ZeroMQ 和 RabbitMQ 都支持一个开源的消息协议,成为AMQP。AMQP的一个优点是它是一个灵活和开放的协议,以便和另外两个商业化的Message Queue (IBM和Tibco)竞争,很好。不过ZeroMQ不支持消息持久化和崩溃恢复,不太好。剩下的只有RabbitMQ了。如果你不在意消息持久化和崩溃恢复,试试ZeroMQ吧,延迟很低,而且支持灵活的拓扑。

剩下的只有这个吃胡萝卜的家伙了……

当我读到它是用Erlang写的时候,RabbitMQ震了我一下。Erlang 是爱立信开发的高度并行的语言,用来跑在电话交换机上。是的,那些要求6个9的在线时间的东西。在Erlang当中,充斥着大量轻量进程,它们之间用消息传递来通信。听起来思路和我们用消息队列的思路是一样的,不是么?

而且,RabbitMQ支持持久化。是的,如果RabbitMQ死掉了,消息并不会丢失,当队列重启,一切都会回来。而且,正如在DigiTar(注:原文作者的公司)做事情期望的那样,它可以和Python无缝结合。除此之外,RabbitMQ的文档相当的……恐怖。如果你懂AMQP,这些文档还好,但是有多少人懂AMQP?这些文档就像MySQL的文档假设你已经懂了SQL一样……不过没关系啦。

好了,废话少说。这里是花了一周时间阅读关于AMQP和关于它如何在RabbitMQ上工作的文档之后的一个总结,还有,怎么在Python当中使用。

开始吧

AMQP当中有四个概念非常重要:虚拟主机(virtual host),交换机(exchange),队列(queue)和绑定(binding)。一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单,RabbitMQ当中,用户只能在虚拟主机的粒度进行权限控制。因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。如果这就够了,那现在就可以开始了。

交换机,队列,还有绑定……天哪!

刚开始我思维的列车就是在这里脱轨的…… 这些鬼东西怎么结合起来的?

队列(Queues)是你的消息(messages)的终点,可以理解成装消息的容器。消息就一直在里面,直到有客户端(也就是消费者,Consumer)连接到这个队列并且将其取走为止。不过。你可以将一个队列配置成这样的:一旦消息进入这个队列,biu~,它就烟消云散了。这个有点跑题了……

需要记住的是,队列是由消费者(Consumer)通过程序建立的,不是通过配置文件或者命令行工具。这没什么问题,如果一个消费者试图创建一个已经存在的队列,RabbitMQ就会起来拍拍他的脑袋,笑一笑,然后忽略这个请求。因此你可以将消息队列的配置写在应用程序的代码里面。这个概念不错。

OK,你已经创建并且连接到了你的队列,你的消费者程序正在百无聊赖的敲着手指等待消息的到来,敲啊,敲啊…… 没有消息。发生了什么?你当然需要先把一个消息放进队列才行。不过要做这个,你需要一个交换机(Exchange)……

交换机可以理解成具有路由表的路由程序,仅此而已。每个消息都有一个称为路由键(routing key)的属性,就是一个简单的字符串。交换机当中有一系列的绑定(binding),即路由规则(routes),例如,指明具有路由键 “X” 的消息要到名为timbuku的队列当中去。先不讨论这个,我们有点超前了。

你的消费者程序要负责创建你的交换机(复数)。啥?你是说你可以有多个交换机?是的,这个可以有,不过为啥?很简单,每个交换机在自己独立的进程当中执行,因此增加多个交换机就是增加多个进程,可以充分利用服务器上的CPU核以便达到更高的效率。例如,在一个8核的服务器上,可以创建5个交换机来用5个核,另外3个核留下来做消息处理。类似的,在RabbitMQ的集群当中,你可以用类似的思路来扩展交换机一边获取更高的吞吐量。

OK,你已经创建了一个交换机。但是他并不知道要把消息送到哪个队列。你需要路由规则,即绑定(binding)。一个绑定就是一个类似这样的规则:将交换机“desert(沙漠)”当中具有路由键“阿里巴巴”的消息送到队列“hideout(山洞)”里面去。换句话说,一个绑定就是一个基于路由键将交换机和队列连接起来的路由规则。例如,具有路由键“audit”的消息需要被送到两个队列,“log-forever”和“alert-the-big-dude”。要做到这个,就需要创建两个绑定,每个都连接一个交换机和一个队列,两者都是由“audit”路由键触发。在这种情况下,交换机会复制一份消息并且把它们分别发送到两个队列当中。交换机不过就是一个由绑定构成的路由表。

现在复杂的东西来了:交换机有多种类型。他们都是做路由的,不过接受不同类型的绑定。为什么不创建一种交换机来处理所有类型的路由规则呢?因为每种规则用来做匹配分子的CPU开销是不同的。例如,一个“topic”类型的交换机试图将消息的路由键与类似“dogs.*”的模式进行匹配。匹配这种末端的通配符比直接将路由键与“dogs”比较(“direct”类型的交换机)要消耗更多的CPU。如果你不需要“topic”类型的交换机带来的灵活性,你可以通过使用“direct”类型的交换机获取更高的处理效率。那么有哪些类型,他们又是怎么处理的呢?

Fanout Exchange – 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。

Direct Exchange – 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog

Topic Exchange – 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。我在RedHat的朋友做了一张不错的图,来表明topic交换机是如何工作的:

Source: Red Hat Messaging Tutorial: 1.3 Topic Exchange

持久化这些小东西们

你花了大量的时间来创建队列、交换机和绑定,然后,砰~服务器程序挂了。你的队列、交换机和绑定怎么样了?还有,放在队列里面但是尚未处理的消息们呢?

放松~如果你是用默认参数构造的这一切的话,那么,他们,都,biu~,灰飞烟灭了。是的,RabbitMQ重启之后会干净的像个新生儿。你必须重做所有的一切,亡羊补牢,如何避免将来再度发生此类杯具?

队列和交换机有一个创建时候指定的标志durable,直译叫做坚固的。durable的唯一含义就是具有这个标志的队列和交换机会在重启之后重新建立,它不表示说在队列当中的消息会在重启后恢复。那么如何才能做到不只是队列和交换机,还有消息都是持久的呢?

但是首先一个问题是,你真的需要消息是持久的吗?对于一个需要在重启之后回复的消息来说,它需要被写入到磁盘上,而即使是最简单的磁盘操作也是要消耗时间的。如果和消息的内容相比,你更看重的是消息处理的速度,那么不要使用持久化的消息。不过对于我们@DigiTar来说,持久化很重要。

当你将消息发布到交换机的时候,可以指定一个标志“Delivery Mode”(投递模式)。根据你使用的AMQP的库不同,指定这个标志的方法可能不太一样(我们后面会讨论如何用Python搞定)。简单的说,就是将Delivery Mode设置成2,也就是持久的(persistent)即可。一般的AMQP库都是将Delivery Mode设置成1,也就是非持久的。所以要持久化消息的步骤如下:

  1. 将交换机设成 durable。
  2. 将队列设成 durable。
  3. 将消息的 Delivery Mode 设置成2 。

就这样,不是很复杂,起码没有造火箭复杂,不过也有可能犯点小错误。

下面还要罗嗦一个东西……绑定(Bindings)怎么办?我们无法在创建绑定的时候设置成durable。没问题,如果你绑定了一个durable的队列和一个durable的交换机,RabbitMQ会自动保留这个绑定。类似的,如果删除了某个队列或交换机(无论是不是durable),依赖它的绑定都会自动删除。

注意两点:

  • RabbitMQ 不允许你绑定一个非坚固(non-durable)的交换机和一个durable的队列。反之亦然。要想成功必须队列和交换机都是durable的。
  • 一旦创建了队列和交换机,就不能修改其标志了。例如,如果创建了一个non-durable的队列,然后想把它改变成durable的,唯一的办法就是删除这个队列然后重现创建。因此,最好仔细检查创建的标志。

开始喂蛇了~

【译注】说喂蛇是因为Python的图标是条蛇。

AMQP的一个空白地带是如何在Python当中使用。对于其他语言有一大坨材料。

但是对Python老兄来说,你需要花点时间来挖掘一下。所以我写了这个,这样别的家伙们就不需要经历我这种抓狂的过程了。

首先,我们需要一个Python的AMQP库。有两个可选:

根据你的需求,py-amqplib或者txAMQP都是可以的。因为是基于Twisted的,txAMQP可以保证用异步IO构建超高性能的AMQP程序。但是Twisted编程本身就是一个很大的主题……因此清晰起见,我们打算用 py-amqplib。更新:请参见Esteve Fernandez关于txAMQP的使用和代码样例的回复

AMQP支持在一个TCP连接上启用多个MQ通信channel,每个channel都可以被应用作为通信流。每个AMQP程序至少要有一个连接和一个channel。

from amqplib import client_0_8 as amqp
conn = amqp.Connection(host="localhost:5672 ", userid="guest",
password="guest", virtual_host="/", insist=False)
chan = conn.channel()

每个channel都被分配了一个整数标识,自动由Connection()类的.channel()方法维护。或者,你可以使用.channel(x)来指定channel标识,其中x是你想要使用的channel标识。通常情况下,推荐使用.channel()方法来自动分配channel标识,以便防止冲突。

现在我们已经有了一个可以用的连接和channel。现在,我们的代码将分成两个应用,生产者(producer)和消费者(consumer)。我们先创建一个消费者程序,他会创建一个叫做“po_box”的队列和一个叫“sorting_room”的交换机:

chan.queue_declare(queue="po_box", durable=True,
exclusive=False, auto_delete=False)
chan.exchange_declare(exchange="sorting_room", type="direct", durable=True,
auto_delete=False,)

这段代码干了啥?首先,它创建了一个名叫“po_box”的队列,它是durable的(重启之后会重新建立),并且最后一个消费者断开的时候不会自动删除(auto_delete=False)。在创建durable的队列(或者交换机)的时候,将auto_delete设置成false是很重要的,否则队列将会在最后一个消费者断开的时候消失,与durable与否无关。如果将durable和auto_delete都设置成True,只有尚有消费者活动的队列可以在RabbitMQ意外崩溃的时候自动恢复。

(你可以注意到了另一个标志,称为“exclusive”。如果设置成True,只有创建这个队列的消费者程序才允许连接到该队列。这种队列对于这个消费者程序是私有的)。

还有另一个交换机声明,创建了一个名字叫“sorting_room”的交换机。auto_delete和durable的含义和队列是一样的。但是,.excange_declare() 还有另外一个参数叫做type,用来指定要创建的交换机的类型(如前面列出的): fanout, directtopic.

到此为止,你已经有了一个可以接收消息的队列和一个可以发送消息的交换机。不过我们需要创建一个绑定,把它们连接起来。

chan.queue_bind(queue=”po_box”, exchange=”sorting_room”,
routing_key=”jason”)

这个绑定的过程非常直接。任何送到交换机“sorting_room”的具有路由键“jason” 的消息都被路由到名为“po_box” 的队列。

现在,你有两种方法从队列当中取出消息。第一个是调用chan.basic_get(),主动从队列当中拉出下一个消息(如果队列当中没有消息,chan.basic_get()会返回None, 因此下面代码当中print msg.body 会在没有消息的时候崩掉):

msg = chan.basic_get("po_box")
print msg.body
chan.basic_ack(msg.delivery_tag)

但是如果你想要应用程序在消息到达的时候立即得到通知怎么办?这种情况下不能使用chan.basic_get(),你需要用chan.basic_consume()注册一个新消息到达的回调。

def recv_callback(msg):
    print 'Received: ' + msg.body
chan.basic_consume(queue='po_box', no_ack=True,
callback=recv_callback, consumer_tag="testtag")
while True:
    chan.wait()
chan.basic_cancel("testtag")

chan.wait() 放在一个无限循环里面,这个函数会等待在队列上,直到下一个消息到达队列。chan.basic_cancel() 用来注销该回调函数。参数consumer_tag 当中指定的字符串和chan.basic_consume() 注册的一直。在这个例子当中chan.basic_cancel() 不会被调用到,因为上面是个无限循环…… 不过你需要知道这个调用,所以我把它放在了代码里。

需要注意的另一个东西是no_ack参数。这个参数可以传给chan.basic_get()chan.basic_consume(),默认是false。当从队列当中取出一个消息的时候,RabbitMQ需要应用显式地回馈说已经获取到了该消息。如果一段时间内不回馈,RabbitMQ会将该消息重新分配给另外一个绑定在该队列上的消费者。另一种情况是消费者断开连接,但是获取到的消息没有回馈,则RabbitMQ同样重新分配。如果将no_ack 参数设置为true,则py-amqplib会为下一个AMQP请求添加一个no_ack属性,告诉AMQP服务器不需要等待回馈。但是,大多数时候,你也许想要自己手工发送回馈,例如,需要在回馈之前将消息存入数据库。回馈通常是通过调用chan.basic_ack()方法,使用消息的delivery_tag属性作为参数。参见chan.basic_get() 的实例代码。

好了,这就是消费者的全部代码。(下载:amqp_consumer.py

不过没有人发送消息的话,要消费者何用?所以需要一个生产者。下面的代码示例表明如何将一个简单消息发送到交换区“sorting_room”,并且标记为路由键“jason” :

msg = amqp.Message("Test message!")
msg.properties["delivery_mode"] = 2
chan.basic_publish(msg,exchange="sorting_room",routing_key="jason")

你也许注意到我们设置消息的delivery_mode属性为2,因为队列和交换机都设置为durable的,这个设置将保证消息能够持久化,也就是说,当它还没有送达消费者之前如果RabbitMQ重启则它能够被恢复。

剩下的最后一件事情(生产者和消费者都需要调用的)是关闭channel和连接:

chan.close()
conn.close()

很简单吧。(下载:amqp_publisher.py

来真实地跑一下吧……

现在我们已经写好了生产者和消费者,让他们跑起来吧。假设你的RabbitMQ在localhost上安装并且运行。

打开一个终端,执行python ./amqp_consumer.py让消费者运行,并且创建队列、交换机和绑定。

然后在另一个终端运行python ./amqp_publisher.py “AMQP rocks.” 。如果一切良好,你应该能够在第一个终端看到输出的消息。

付诸使用吧

我知道这个教程是非常粗浅的关于AMQP/RabbitMQ和如何使用Python访问的教程。希望这个可以说明所有的概念如何在Python当中被组合起来。如果你发现任何错误,请联系原作者(williamsjj@digitar.com) 【译注:如果是翻译问题请联系译者】。同时,我很高兴回答我知道的问题。【译注:译者也是一样的】。接下来是,集群化(clustering)!不过我需要先把它弄懂再说。

注:关于RabbitMQ的知识我主要来自这些来源,推荐阅读:

–完–

西厢计划

2010年3月10日 没有评论

今天在twitter上看到的@gfwrev成果,向伟大的翻墙先行者致敬。

作为个搞技术的人,我们要干点疯狂的事。如果我们不动手,我们就要被比我们差的远的坏技术人员欺负。这太丢人了。眼前就是,GFW这个东西,之前是我们不抱团,让它猖狂了。现在咱们得凑一起,想出来一个办法让它郁闷一下,不能老被欺负吧。要不,等到未来,后代会嘲笑我们这些没用的家伙,就象我们说别人“你怎么不反抗?”

这就是2008年7月tek4小组建立时候的宣言。

今天,西厢计划开始了。干的漂亮。

标签:

断网记

2010年2月24日 3 条评论

过年回家,一直没有网络,只能依靠手机,还是2G网络,连EDGE都没有,憋得严重,等到假期结束,回到北京的窝,感叹:终于要回到我的Always Connected的生活当中了。啊,大中华局域网,我回来了。

到家打开电闸,开机,上网,上不了?去路由器看看,拨不通?郁闷了,难道这几天有人动过我的路由器?然后开始查日志,报告无法通过身份认证,密码不对?被人盗号了?这种时候最好的办法就是打客服电话,往往就是他们动动手指就可以搞定的。可是死活想不起联通的客服电话是多少,肯定不是10000,这是电信的,我在南方的时候没少和这个号码较劲。那就是10001?拨了一下不是,是电信的充值服务。Google一下吧,然后想到没上网。无奈之下,手机上网,搜索,得知,10010。

然后拨10010,客服连续问了我几个“你确定密码是对的”之后,很不情愿地说帮我查一查,要我报固定电话号码。我当时装宽带就没打算用固定电话,家里连个电话机都没有,怎么知道电话号码是多少。我问宽带帐号行不行,说不行;问身份证号行不行,说也不行,必须是固定电话号码。然后说你稍等,就开始翻箱倒柜地找当时办业务时候的单子,还好当时留下来了,大体的地方也知道,10分钟之内找到了。找到单子,报上号码,然后客服告诉我,这台电话被欠费撤机了。

欠费?还撤机?怎么可能,我可是一次缴清了一年的宽带使用费的,这才半年不到,怎么会欠费了。客服估计听到这种抱怨多了去了,不紧不慢地说,你交了也没用,这个是因为电话欠费停掉的,因为你装了宽带就必须先装一个固定电话,这个固定电话有每月24.5的月租费。我说我当时办的是ADSL啊,为什么给我装了固定电话,这属于强制消费;而且,我办网络业务的时候,怎么没人跟我说过还有这笔钱,也没人跟我说过如果不交会撤机。客服好像有点烦了,说都是这样的,都要装固定电话,装了不交费就要撤机,就差说你不知道你怪谁了。鬼啊,我用电信的服务这么久,网络就是网络,从来没有过要强制安装一个固定电话业务的,这年头,想要提高自己的固定电话使用率也不用这样捆绑销售吧。也许电信也是要有一个电话号码的,不过人家把这个电话号码绑定在宽带里面了。

不过我还是忍住没发火,问她说那你告诉我现在我要上网怎么办。客服说只能重新开一个网络服务。重新开什么意思呢?就是重新交初装费238,重新一次缴清一年的宽带使用费。说白了就是想多弄一笔初装费吗?那好,怎么办理呢?需要本人带上身份证到联通营业厅办理。那我上面一笔钱怎么办呢?客服说,到时候会退给你的。到时候是啥时候?不知道,会退给你的。明天可以吗?不行,要等公司通知。乖乖,真的是钱在谁手里谁是大爷啊。那怎么退呢?需要带上最初的发票和本人身份证到营业厅办理。要是我人不在呢,能找人代领吗?对不起,必须本人到营业厅办理。

第二天中午请了个假,到联通营业厅。幸好这老大的营业厅中午是营业的,要是中午不营业,像那个自来水公司一样,我上班了他们还没上班,我下班了他们已经下班了,我午休他们也午休,估计这网络我还办不了了。进门有人问我办什么业务,我说我投诉。一听到这个马上旁边有人把我拉到了一边,又是端茶又是倒水。我看到他胸牌上写着XX经理,估计是想息事宁人。我偏不,要了一张投诉单,把情况写了一遍,提出两点不满:

1. 没有任何人告知我宽带业务绑定了固定电话业务,且电话需要单独缴费,且五个月不缴费会撤机;
2. 在停机和撤机之前,我没有得到任何的警告。

因此我对被撤机不服,要求如下:

1. 恢复原来的包年业务,或者新装一个包年业务,但是免初装费。
2. 免除之前所谓的欠费。
3. 将恢复或新装的网络绑定的固定电话业务取消,或其他不需要缴纳使用费的业务。
4. 改进内部流程,改进通知用户的方式,在无法通过固定电话通知的情况下,使用备用联系方式通知。

然后他开始解释,第一是合同上有写。但是合同上没写多久不缴费会撤机,且没有其他地方约定,也没有人告知。第二是他们打了固定电话,但是我没接到。对此我质问,运营商的目的是通知用户还是通知电话。况且,在安装电话的时候,我被要求留下一个联系方式,这个联系方式并非该固定电话,但是并没有联系。对此经理的解释是,他们流程如此,负责撤机的部门无法拿到该联系方式。

有这句话就行了,这就说明他们承认这个是运营商的责任了。其实我这个投诉单写之前,我仔细研究了合同上面的约定和电信管理条例,发现合同上面并未告知欠费多久会撤机;另外就是电信管理条例规定运营商不得以任何方式限定电信用户使用其指定的业务,因此绑定固定电话业务是违规的。这两条不满他们没什么话说。

另外就是提出的要求也是有些讲究的。通常投诉会到单独的客服部门,这个部门的层次不太清楚,但是基层的客服经理是具有免除某些费用的权利的。因此,投诉最好以免除费用为主要诉求,比较容易达到。一个新业务的初装费问题通常不大,而欠费则通常比较麻烦,需要碰运气。要求改进流程这回事,肯定不是基层的客服经理可以解决的,因此他肯定搞不定,但是四条要求如果只满足一个免初装费,正常很难平衡,所以这个纯粹是为了增加讨价还价的资本的。

因为知道这个投诉拿下的问题不大,所以我就先办了一个新的ADSL包半年,到了明年8月到期,我也正好房子到期。反正投诉结果出来可以讨价还价,把免初装费改成退初装费。毕竟着急用网络,老婆工作还要用。也许是投诉的作用,当天晚上就有人联系我,第二天就把新的ADSL装上了。

另外说一个在联通营业厅的见闻。一个老爷子,家里的电话处于保号停机状态,现在他需要在上面开通一个ADSL业务,但是电话仍然保号停机,你认为应该怎么办?正常的思路,也是电信的思路就是加一个ADSL业务。

但是联通的做法是:今天我们帮您启用了电话,即把电话从保号停机状态变成启用状态。这个在24小时内生效。您明天再拿着您的身份证来一趟营业厅,申请加装ADSL,这个会在7个工作日内生效。装好了之后,您需要带着本人身份证来营业厅将电话设置成停机保号,同时缴纳这7天的电话租用费。而且,没得商量,都必须本人带身份证到营业厅。听到这个,那老爷子和我一起被雷到了,老爷子估计有70岁了诶。

一个运营商的业务设计,怎么可以烂到这种地步而还能存活,这就是垄断。因为北京的小区线路是垄断的,大部分小区的线路都是网通的,现在是联通的,而且,更换为电信线路的流程异常复杂。

这个事情发生在两天前,今天早上,有一位客服经理联系到了我,先表示歉意,然后说了他们的处理结果,几番讨价还价之后,最终结果如下:第一是新装一个ADSL,免掉初装费;第二就是下个月10号之后我随时可以到营业厅办理退款;第三是欠费还是要交,这个免不了;第四是保号停机还是要我自己去营业厅办,但是保号停机之前的月租费不用交;第五就是流程问题反映给上面了。

基本上达到了目的。

这个事件给我的启示是:

1. 不满就一定要投诉,越是大公司越要投诉。这是最快捷的解决问题的途径。
2. 提要求要头脑清楚,尽量提靠谱的要求,再提几个大的不着边的要求,通常会成功率比较高。
3. 记着最后的杀手锏,可以给工信部投诉。比如这个处理结果如果我不满意,我完全可以拿固定电话业务绑定开通这回事到工信部投诉。对于这个,运营商也怕。

和体制内的人打交道,包括政府和垄断国企,就事论事,不卑不亢,给足对方面子,通常办事也不是很难。

第一章 总 则

第一条 为了规范电信市场秩序,维护电信用户和电信业务经营者的合法权益,保障电信网络和信 息的安全,促进电信业的健康发展,制定本条例。

第二条 在中华人民共和国境内从事电信活动或者与电信有关的活动,必须遵守本条例。

本条例所称电信,是指利用有线、无线的电磁系统或者光电系统,传送、发射或者接 收语音、文字、数据、图像以及其他任何形式信息的活动。

第三条 国务院信息产业主管部门依照本条例的规定对全国电信业实施监督管理。

省、自治区、直辖市电信管理机构在国务院信息产业主管部门的领导下,依照本条例 的规定对本行政区域内的电信业实施监督管理。

第四条 电信监督管理遵循政企分开、破除垄断、鼓励竞争、促进发展和公开、公平、公正的原则。

电信业务经营者应当依法经营,遵守商业道德,接受依法实施的监督检查。

第五条 电信业务经营者应当为电信用户提供迅速、准确、安全、方便和价格合理的电信服务。

第六条 电信网络和信息的安全受法律保护。任何组织或者个人不得利用电信网络从事危害国家安全、社会公共利益或者他人合法权益的活动。

第二章 电信市场

第一节 电信业务许可

第七条 国家对电信业务经营按照电信业务分类,实行许可制度。

经营电信业务,必须依照本条例的规定取得国务院信息产业主管部门或者省、自治 区、直辖市电信管理机构颁发的电信业务经营许可证。

未取得电信业务经营许可证,任何组织或者个人不得从事电信业务经营活动。

第八条 电信业务分为基础电信业务和增值电信业务。

基础电信业务,是指提供公共网络基础设施、公共数据传送和基本话音通信服务的业 务。增值电信业务,是指利用公共网络基础设施提供的电信与信息服务的业务。

电信业务分类的具体划分在本条例所附的《电信业务分类目录》中列出。国务院信息 产业主管部门根据实际情况,可以对目录所列电信业务分类项目作局部调整,重新公布。

第九条 经营基础电信业务,须经国务院信息产业主管部门审查批准,取得《基础电信业务经营许可证》。

经营增值电信业务,业务覆盖范围在两个以上省、自治区、直辖市的,须经国务院信 息产业主管部门审查批准,取得《跨地区增值电信业务经营许可证》;业务覆盖范围在一个省、自治区、直辖市行政区域内的,须经省、自治区、直辖市电信管理机 构审查批准,取得《增值电信业务经营许可证》。

运用新技术试办《电信业务分类目录》未列出的新型电信业务的,应当向省、自治 区、直辖市电信管理机构备案。

第十条 经营基础电信业务,应当具备下列条件:

(一)经营者为依法设立的专门从事基础电信业务的公司,且公司中国有股权或者股 份不少于51%;

(二)有可行性研究报告和组网技术方案;

(三)有与从事经营活动相适应的资金和专业人员;

(四)有从事经营活动的场地及相应的资源;

(五)有为用户提供长期服务的信誉或者能力;

(六)国家规定的其他条件。

第十一条 申请经营基础电信业务,应当向国务院信息产业主管部门提出申请,并提交本条例。

第十条规定的相关文件。国务院信息产业主管部门应当自受理申请之日起180日内 审查完毕,作出批准或者不予批准的决定。予以批准的,颁发《基础电信业务经营许可证》;不予批准的,应当书面通知申请人并说明理由。

第十二条 国务院信息产业主管部门审查经营基础电信业务的申请时,应当考虑国家安全、电信网络安全、电信资源可持续利用、环境保护和电信市场的竞争状况等因素。

颁发《基础电信业务经营许可证》,应当按照国家有关规定采用招标方式。

第十三条 经营增值电信业务,应当具备下列条件:

(一)经营者为依法设立的公司;

(二)有与开展经营活动相适应的资金和专业人员;

(三)有为用户提供长期服务的信誉或者能力;

(四)国家规定的其他条件。

第十四条 申请经营增值电信业务,应当根据本条例

第九条  第二款的规定,向国务院信息产业主管部门或者省、自治区、直辖市电信 管理机构提出申请,并提交本条例  第十三条规定的相关文件。申请经营的增值电信业务,按照国家有关规定须经有关主管部门审批的,还应当提交有关主管部门 审核同意的文件。国务院信息产业主管部门或者省、自治区、直辖市电信管理机构应当自收到申请之日起60日内审查完毕,作出批准或者不予批准的决定。予以批 准的,颁发《跨地区增值电信业务经营许可证》或者《增值电信业务经营许可证》;不予批准的,应当书面通知申请人并说明理由。

第十五条 电信业务经营者在经营过程中,变更经营主体、业务范围或者停止经营的,应当提前90日向原颁发许可证的机关提出申请,并办理相应手续;停止经营的,还应当 按照国家有关规定做好善后工作。

第十六条 经批准经营电信业务的,应当持依法取得的电信业务经营许可证,向企业登记机关办理登记手续。

专用电信网运营单位在所在地区经营电信业务的,应当依照本条例规定的条件和程序 提出申请,经批准,取得电信业务经营许可证,并依照前款规定办理登记手续。

第二节 电信网间互联

第十七条 电信网之间应当按照技术可行、经济合理、公平公正、相互配合的原则,实现互联互通。

主导的电信业务经营者不得拒绝其他电信业务经营者和专用网运营单位提出的互联互 通要求。

前款所称主导的电信业务经营者,是指控制必要的基础电信设施并且在电信业务市场 中占有较大份额,能够对其他电信业务经营者进入电信业务市场构成实质性影响的经营者。

主导的电信业务经营者由国务院信息产业主管部门确定。

第十八条 主导的电信业务经营者应当按照非歧视和透明化的原则,制定包括网间互联的程序、时限、非捆绑网络元素目录等内容的互联规程。互联规程应当报国务院信息产业 主管部门审查同意。该互联规程对主导的电信业务经营者的互联互通活动具有约束力。

第十九条 公用电信网之间、公用电信网与专用电信网之间的网间互联,由网间互联双方按照国务院信息产业主管部门的网间互联管理规定进行互联协商,并订立网间互联协 议。

网间互联协议应当向国务院信息产业主管部门备案。

第二十条 网间互联双方经协商未能达成网间互联协议的,自一方提出互联要求之日起60日内,任何一方均可以按照网间互联覆盖范围向国务院信息产业主管部门或者省、自 治区、直辖市电信管理机构申请协调;收到申请的机关应当依照本条例  第十七条  第一款规定的原则进行协调,促使网间互联双方达成协议;自网间互联

一方或者双方申请协调之日起45日内经协调仍不能达成协议的,由协调机关随机邀 请电信技术专家和其他有关方面专家进行公开论证并提出网间互联方案。协调机关应当根据专家论证结论和提出的网间互联方案作出决定,强制实现互联互通。

第二十一条 网间互联双方必须在协议约定或者决定规定的时限内实现互联互通。未经国务院信息产业主管部门批准,任何一方不得擅自中断互联互通。网间互联遇有通信技术障 碍的,双方应当立即采取有效措施予以消除。网间互联双方在互联互通中发生争议的,依照本条例  第二十条规定的程序和办法处理。

网间互联的通信质量应当符合国家有关标准。主导的电信业务经营者向其他电信业务 经营者提供网间互联,服务质量不得低于本网内的同类业务及向其子公司或者分支机构提供的同类业务质量。

第二十二条 网间互联的费用结算与分摊应当执行国家有关规定,不得在规定标准之外加收费用。

网间互联的技术标准、费用结算办法和具体管理规定,由国务院信息产业主管部门制 定。

第三节 电信资费

第二十三条 电信资费标准实行以成本为基础的定价原则,同时考虑国民经济与社会发展要求、电信业的发展和电信用户的承受能力等因素。

第二十四条 电信资费分为市场调节价、政府指导价和政府定价。

基础电信业务资费实行政府定价、政府指导价或者市场调节价;增值电信业务资费实 行市场调节价或者政府指导价。

市场竞争充分的电信业务,电信资费实行市场调节价。

实行政府定价、政府指导价和市场调节价的电信资费分类管理目录,由国务院信息产 业主管部门经征求国务院价格主管部门意见制定并公布施行。

第二十五条 政府定价的重要的电信业务资费标准,由国务院信息产业主管部门提出方案,经征求国务院价格主管部门意见,报国务院批准后公布施行。

政府指导价的电信业务资费标准幅度,由国务院信息产业主管部门经征求国务院价格 主管部门意见,制定并公布施行。电信业务经营者在标准幅度内,自主确定资费标准,报省、自治区、直辖市电信管理机构备案。

第二十六条 制定政府定价和政府指导价的电信业务资费标准,应当采取举行听证会等形式,听取电信业务经营者、电信用户和其他有关方面的意见。

电信业务经营者应当根据国务院信息产业主管部门和省、自治区、直辖市电信管理机 构的要求,提供准确、完备的业务成本数据及其他有关资料。

第四节 电信资源

第二十七条 国家对电信资源统一规划、集中管理、合理分配,实行有偿使用制度。

前款所称电信资源,是指无线电频率、卫星轨道位置、电信网码号等用于实现电信功 能且有限的资源。

第二十八条 电信业务经营者占有、使用电信资源,应当缴纳电信资源费。具体收费办法由国务院信息产业主管部门会同国务院财政部门、价格主管部门制定,报国务院批准后公 布施行。

第二十九条 电信资源的分配,应当考虑电信资源规划、用途和预期服务能力。

分配电信资源,可以采取指配的方式,也可以采用拍卖的方式。

取得电信资源使用权的,应当在规定的时限内启用所分配的资源,并达到规定的最低 使用规模。未经国务院信息产业主管部门或者省、自治区、直辖市电信管理机构批准,不得擅自使用、转让、出租电信资源或者改变电信资源的用途。

第三十条 电信资源使用者依法取得电信网码号资源后,主导的电信业务经营者和其他有关单位有义务采取必要的技术措施,配合电信资源使用者实现其电信网码号资源的功 能。

法律、行政法规对电信资源管理另有特别规定的,从其规定。

第三章 电信服务

第三十一条 电信业务经营者应当按照国家规定的电信服务标准向电信用户提供服务。电信业务经营者提供服务的种类、范围、资费标准和时限,应当向社会公布,并报省、自治 区、直辖市电信管理机构备案。

电信用户有权自主选择使用依法开办的各类电信业务。

第三十二条 电信用户申请安装、移装电信终端设备的,电信业务经营者应当在其公布的时限内保证装机开通;由于电信业务经营者的原因逾期未能装机开通的,应当每日按照收 取的安装费、移装费或者其他费用数额百分之一的比例,向电信用户支付违约金。

第三十三条 电信用户申告电信服务障碍的,电信业务经营者应当自接到申告之日起,城镇48小时、农村72小时内修复或者调通;不能按期修复或者调通的,应当及时通知电 信用户,并免收障碍期间的月租费用。但是,属于电信终端设备的原因造成电信服务障碍的除外。

第三十四条 电信业务经营者应当为电信用户交费和查询提供方便。电信用户要求提供国内长途通信、国际通信、移动通信和信息服务等收费清单的,电信业务经营者应当免费提 供。

电信用户出现异常的巨额电信费用时,电信业务经营者一经发现,应当尽可能迅速告 知电信用户,并采取相应的措施。

前款所称巨额电信费用,是指突然出现超过电信用户此前三个月平均电信费用5倍以 上的费用。

第三十五条 电信用户应当按照约定的时间和方式及时、足额地向电信业务经营者交纳电信费用;电信用户逾期不交纳电信费用的,电信业务经营者有权要求补交电信费用,并可 以按照所欠费用每日加收3‰的违约金。

对超过收费约定期限30日仍不交纳电信费用的电信用户,电信业务经营者可以暂停 向其提供电信服务。电信用户在电信业务经营者暂停服务60日内仍未补交电信费用和违约金的,电信业务经营者可以终止提供服务,并可以依法追缴欠费和违约 金。

经营移动电信业务的经营者可以与电信用户约定交纳电信费用的期限、方式,不受前 款规定期限的限制。

电信业务经营者应当在迟延交纳电信费用的电信用户补足电信费用、违约金后的48 小时内,恢复暂停的电信服务。

第三十六条 电信业务经营者因工程施工、网络建设等原因,影响或者可能影响正常电信服务的,必须按照规定的时限及时告知用户,并向省、自治区、直辖市电信管理机构报 告。

因前款原因中断电信服务的,电信业务经营者应当相应减免用户在电信服务中断期间 的相关费用。

出现本条  第一款规定的情形,电信业务经营者未及时告知用户的,应当赔偿由此 给用户造成的损失。

第三十七条 经营本地电话业务和移动电话业务的电信业务经营者,应当免费向用户提供火警、匪警、医疗急救、交通事故报警等公益性电信服务并保障通信线路畅通。

第三十八条 电信业务经营者应当及时为需要通过中继线接入其电信网的集团用户,提供平等、合理的接入服务。

未经批准,电信业务经营者不得擅自中断接入服务。

第三十九条 电信业务经营者应当建立健全内部服务质量管理制度,并可以制定并公布施行高于国家规定的电信服务标准的企业标准。

电信业务经营者应当采取各种形式广泛听取电信用户意见,接受社会监督,不断提高 电信服务质量。

第四十条 电信业务经营者提供的电信服务达不到国家规定的电信服务标准或者其公布的企业标准的,或者电信用户对交纳电信费用持有异议的,电信用户有权要求电信业务经 营者予以解决;电信业务经营者拒不解决或者电信用户对解决结果不满意的,电信用户有权向国务院信息产业主管部门或者省、自治区、直辖市电信管理机构或者其 他有关部门申诉。收到申诉的机关必须对申诉及时处理,并自收到申诉之日起30日内向申诉者作出答复。

电信用户对交纳本地电话费用有异议的,电信业务经营者还应当应电信用户的要求免 费提供本地电话收费依据,并有义务采取必要措施协助电信用户查找原因。

第四十一条 电信业务经营者在电信服务中,不得有下列行为:

(一)以任何方式限定电信用户使用其指定的业务;

(二)限定电信用户购买其指定的电信终端设备或者拒绝电信用户使用自备的已经取 得入网许可的电信终端设备;

(三)违反国家规定,擅自改变或者变相改变资费标准,擅自增加或者变相增加收费 项目;

(四)无正当理由拒绝、拖延或者中止对电信用户的电信服务;

(五)对电信用户不履行公开作出的承诺或者作容易引起误解的虚假宣传;

(六)以不正当手段刁难电信用户或者对投诉的电信用户打击报复。

第四十二条 电信业务经营者在电信业务经营活动中,不得有下列行为:

(一)以任何方式限制电信用户选择其他电信业务经营者依法开办的电信服务;

(二)对其经营的不同业务进行不合理的交叉补贴;

(三)以排挤竞争对手为目的,低于成本提供电信业务或者服务,进行不正当竞争。

第四十三条 国务院信息产业主管部门或者省、自治区、直辖市电信管理机构应当依据职权对电信业务经营者的电信服务质量和经营活动进行监督检查,并向社会公布监督抽查结 果。

第四十四条 电信业务经营者必须按照国家有关规定履行相应的电信普遍服务义务。

国务院信息产业主管部门可以采取指定的或者招标的方式确定电信业务经营者具体承 担电信普遍服务的义务。

电信普遍服务成本补偿管理办法,由国务院信息产业主管部门会同国务院财政部门、 价格主管部门制定,报国务院批准后公布施行。

第四章 电信建设

第一节 电信设施建设

第四十五条 公用电信网、专用电信网、广播电视传输网的建设应当接受国务院信息产业主管部门的统筹规划和行业管理。

属于全国性信息网络工程或者国家规定限额以上建设项目的公用电信网、专用电信 网、广播电视传输网建设,在按照国家基本建设项目审批程序报批前,应当征得国务院信息产业主管部门同意。

基础电信建设项目应当纳入地方各级人民政府城市建设总体规划和村镇、集镇建设总 体规划。

第四十六条 城市建设和村镇、集镇建设应当配套设置电信设施。建筑物内的电信管线和配线设施以及建设项目用地范围内的电信管道,应当纳入建设项目的设计文件,并随建设 项目同时施工与验收。所需经费应当纳入建设项目概算。

有关单位或者部门规划、建设道路、桥梁、隧道或者地下铁道等,应当事先通知省、 自治区、直辖市电信管理机构和电信业务经营者,协商预留电信管线等事宜。

第四十七条 基础电信业务经营者可以在民用建筑物上附挂电信线路或者设置小型天线、移动通信基站等公用电信设施,但是应当事先通知建筑物产权人或者使用人,并按照省、 自治区、直辖市人民政府规定的标准向该建筑物的产权人或者其他权利人支付使用费。

第四十八条 建设地下、水底等隐蔽电信设施和高空电信设施,应当按照国家有关规定设置标志。

基础电信业务经营者建设海底电信缆线,应当征得国务院信息产业主管部门同意,并 征求有关部门意见后,依法办理有关手续。海底电信缆线由国务院有关部门在海图上标出。

第四十九条 任何单位或者个人不得擅自改动或者迁移他人的电信线路及其他电信设施;遇有特殊情况必须改动或者迁移的,应当征得该电信设施产权人同意,由提出改动或者迁 移要求的单位或者个人承担改动或者迁移所需费用,并赔偿由此造成的经济损失。

第五十条 从事施工、生产、种植树木等活动,不得危及电信线路或者其他电信设施的安全或者妨碍线路畅通;可能危及电信安全时,应当事先通知有关电信业务经营者,并由 从事该活动的单位或者个人负责采取必要的安全防护措施。

违反前款规定,损害电信线路或者其他电信设施或者妨碍线路畅通的,应当恢复原状 或者予以修复,并赔偿由此造成的经济损失。

第五十一条 从事电信线路建设,应当与已建的电信线路保持必要的安全距离;难以避开或者必须穿越,或者需要使用已建电信管道的,应当与已建电信线路的产权人协商,并签 订协议;经协商不能达成协议的,根据不同情况,由国务院信息产业主管部门或者省、自治区、直辖市电信管理机构协调解决。

第五十二条 任何组织或者个人不得阻止或者妨碍基础电信业务经营者依法从事电信设施建设和向电信用户提供公共电信服务;但是,国家规定禁止或者限制进入的区域除外。

第五十三条 执行特殊通信、应急通信和抢修、抢险任务的电信车辆,经公安交通管理机关批准,在保障交通安全畅通的前提下可以不受各种禁止机动车通行标志的限制。

第二节 电信设备进网

第五十四条 国家对电信终端设备、无线电通信设备和涉及网间互联的设备实行进网许可制度。

接入公用电信网的电信终端设备、无线电通信设备和涉及网间互联的设备,必须符合 国家规定的标准并取得进网许可证。

实行进网许可制度的电信设备目录,由国务院信息产业主管部门会同国务院产品质量 监督部门制定并公布施行。

第五十五条 办理电信设备进网许可证的,应当向国务院信息产业主管部门提出申请,并附送经国务院产品质量监督部门认可的电信设备检测机构出具的检测报告或者认证机构出 具的产品质量认证证书。

国务院信息产业主管部门应当自收到电信设备进网许可申请之日起60日内,对申请 及电信设备检测报告或者产品质量认证证书审查完毕。经审查合格的,颁发进网许可证;经审查不合格的,应当书面答复并说明理由。

第五十六条 电信设备生产企业必须保证获得进网许可的电信设备的质量稳定、可靠,不得降低产品质量和性能。

电信设备生产企业应当在其生产的获得进网许可的电信设备上粘贴进网许可标志。

国务院产品质量监督部门应当会同国务院信息产业主管部门对获得进网许可证的电信 设备进行质量跟踪和监督抽查,公布抽查结果。

第五章 电信安全

第五十七条 任何组织或者个人不得利用电信网络制作、复制、发布、传播含有下列内容的信息:

(一)反对宪法所确定的基本原则的;

(二)危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;

(三)损害国家荣誉和利益的;

(四)煽动民族仇恨、民族歧视,破坏民族团结的;

(五)破坏国家宗教政策,宣扬邪教和封建迷信的;

(六)散布谣言,扰乱社会秩序,破坏社会稳定的;

(七)散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;

(八)侮辱或者诽谤他人,侵害他人合法权益的;

(九)含有法律、行政法规禁止的其他内容的。

第五十八条 任何组织或者个人不得有下列危害电信网络安全和信息安全的行为:

(一)对电信网的功能或者存储、处理、传输的数据和应用程序进行删除或者修改;

(二)利用电信网从事窃取或者破坏他人信息、损害他人合法权益的活动;

(三)故意制作、复制、传播计算机病毒或者以其他方式攻击他人电信网络等电信设 施;

(四)危害电信网络安全和信息安全的其他行为。

第五十九条 任何组织或者个人不得有下列扰乱电信市场秩序的行为:

(一)采取租用电信国际专线、私设转接设备或者其他方法,擅自经营国际或者香港 特别行政区、澳门特别行政区和台湾地区电信业务;

(二)盗接他人电信线路,复制他人电信码号,使用明知是盗接、复制的电信设施或 者码号;

(三)伪造、变造电话卡及其他各种电信服务有价凭证;

(四)以虚假、冒用的身份证件办理入网手续并使用移动电话。

第六十条 电信业务经营者应当按照国家有关电信安全的规定,建立健全内部安全保障制度,实行安全保障责任制。

第六十一条 电信业务经营者在电信网络的设计、建设和运行中,应当做到与国家安全和电信网络安全的需求同步规划,同步建设,同步运行。

第六十二条 在公共信息服务中,电信业务经营者发现电信网络中传输的信息明显属于本条例  第五十七条所列内容的,应当立即停止传输,保存有关记录,并向国家有关机关 报告。

第六十三条 使用电信网络传输信息的内容及其后果由电信用户负责。

电信用户使用电信网络传输的信息属于国家秘密信息的,必须依照保守国家秘密法的 规定采取保密措施。

第六十四条 在发生重大自然灾害等紧急情况下,经国务院批准,国务院信息产业主管部门可以调用各种电信设施,确保重要通信畅通。

第六十五条 在中华人民共和国境内从事国际通信业务,必须通过国务院信息产业主管部门批准设立的国际通信出入口局进行。

我国内地与香港特别行政区、澳门特别行政区和台湾地区之间的通信,参照前款规定 办理。

第六十六条 电信用户依法使用电信的自由和通信秘密受法律保护。除因国家安全或者追查刑事犯罪的需要,由公安机关、国家安全机关或者人民检察院依照法律规定的程序对电 信内容进行检查外,任何组织或者个人不得以任何理由对电信内容进行检查。

电信业务经营者及其工作人员不得擅自向他人提供电信用户使用电信网络所传输信息 的内容。

第六章 罚 则

第六十七条 违反本条例  第五十七条、  第五十八条的规定,构成犯罪的,依法追究刑事责任;尚不构成犯罪的,由公安机关、国家安全机关依照有关法律、行政法规的规 定予以处罚。

第六十八条 有本条例  第五十九条  第(二)、(三)、(四)项所列行为之一,扰乱电信市场秩序,构成犯罪的,依法追究刑事责任;尚不构成犯罪的,由国务院信息产 业主管部门或者省、自治区、直辖市电信管理机构依据职权责令改正,没收违法所得,处违法所得3倍以上5倍以下罚款;没有违法所得或者违法所得不足1万元 的,处1万元以上10万元以下罚款。

第六十九条 违反本条例的规定,伪造、冒用、转让电信业务经营许可证、电信设备进网许可证或者编造在电信设备上标注的进网许可证编号的,由国务院信息产业主管部门或者 省、自治区、直辖市电信管理机构依据职权没收违法所得,处违法所得3倍以上5倍以下罚款;没有违法所得或者违法所得不足1万元的,处1万元以上10万元以 下罚款。

第七十条 违反本条例规定,有下列行为之一的,由国务院信息产业主管部门或者省、自治区、直辖市电信管理机构依据职权责令改正,没收违法所得,处违法所得3倍以上5 倍以下罚款;没有违法所得或者违法所得不足5万元的,处10万元以上100万元以下罚款;情节严重的,责令停业整顿:

(一)违反本条例  第七条  第三款的规定或者有本条例  第五十九条  第 (一)项所列行为,擅自经营电信业务的,或者超范围经营电信业务的;

(二)未通过国务院信息产业主管部门批准,设立国际通信出入口进行国际通信的;

(三)擅自使用、转让、出租电信资源或者改变电信资源用途的;

(四)擅自中断网间互联互通或者接入服务的;

(五)拒不履行普遍服务义务的。

第七十一条 违反本条例的规定,有下列行为之一的,由国务院信息产业主管部门或者省、自治区、直辖市电信管理机构依据职权责令改正,没收违法所得,处违法所得1倍以上 3倍以下罚款;没有违法所得或者违法所得不足1万元的,处1万元以上10万元以下罚款;情节严重的,责令停业整顿:

(一)在电信网间互联中违反规定加收费用的;

(二)遇有网间通信技术障碍,不采取有效措施予以消除的;

(三)擅自向他人提供电信用户使用电信网络所传输信息的内容的;

(四)拒不按照规定缴纳电信资源使用费的。

第七十二条 违反本条例  第四十二条的规定,在电信业务经营活动中进行不正当竞争的,由国务院信息产业主管部门或者省、自治区、直辖市电信管理机构依据职权责令改 正,处10万元以上100万元以下罚款;情节严重的,责令停业整顿。

第七十三条 违反本条例的规定,有下列行为之一的,由国务院信息产业主管部门或者省、自治区、直辖市电信管理机构依据职权责令改正,处5万元以上50万元以下罚款;情 节严重的,责令停业整顿:

(一)拒绝其他电信业务经营者提出的互联互通要求的;

(二)拒不执行国务院信息产业主管部门或者省、自治区、直辖市电信管理机构依法 作出的互联互通决定的;

(三)向其他电信业务经营者提供网间互联的服务质量低于本网及其子公司或者分支 机构的。

第七十四条 违反本条例  第三十四条  第一款、  第四十条  第二款的规定,电信业务经营者拒绝免费为电信用户提供国内长途通信、国际通信、移动通信和信息服务 等收费清单,或者电信用户对交纳本地电话费用有异议并提出要求时,拒绝为电信用户免费提供本地电话收费依据的,由省、自治区、直辖市电信管理机构责令改 正,并向电信用户赔礼道歉;拒不改正并赔礼道歉的,处以警告,并处5000元以上5万元以下的罚款。

第七十五条 违反本条例  第四十一条的规定,由省、自治区、直辖市电信管理机构责令改正,并向电信用户赔礼道歉,赔偿电信用户损失;拒不改正并赔礼道歉、赔偿损失 的,处以警告,并处1万元以上10万元以下的罚款;情节严重的,责令停业整顿。

第七十六条 违反本条例的规定,有下列行为之一的,由省、自治区、直辖市电信管理机构责令改正,处1万元以上10万元以下的罚款:

(一)销售未取得进网许可的电信终端设备的;

(二)非法阻止或者妨碍电信业务经营者向电信用户提供公共电信服务的;

(三)擅自改动或者迁移他人的电信线路及其他电信设施的。

第七十七条 违反本条例的规定,获得电信设备进网许可证后降低产品质量和性能的,由产品质量监督部门依照有关法律、行政法规的规定予以处罚。

第七十八条 有本条例  第五十七条、  第五十八条和  第五十九条所列禁止行为之一,情节严重的,由原发证机关吊销电信业务经营许可证。

国务院信息产业主管部门或者省、自治区、直辖市电信管理机构吊销电信业务经营许 可证后,应当通知企业登记机关。

第七十九条 国务院信息产业主管部门或者省、自治区、直辖市电信管理机构工作人员玩忽职守、滥用职权、徇私舞弊,构成犯罪的,依法追究刑事责任;尚不构成犯罪的,依法 给予行政处分。

第七章 附 则

第八十条 外国的组织或者个人在中华人民共和国境内投资与经营电信业务和香港特别行政区、澳门特别行政区与台湾地区的组织或者个人在内地投资与经营电信业务的具体办 法,由国务院另行制定。

第八十一条 本条例自公布之日起施行。

附:电信业务分类目录

一、基础电信业务

(一)固定网络国内长途及本地电话业务;

(二)移动网络电话和数据业务;

(三)卫星通信及卫星移动通信业务;

(四)互联网及其它公共数据传送业务;

(五)带宽、波长、光纤、光缆、管道及其它网络元素出租、出售业务;

(六)网络承载、接入及网络外包等业务;

(七)国际通信基础设施、国际电信业务;

(八)无线寻呼业务;

(九)转售的基础电信业务。

第(八)、(九)项业务比照增值电信业务管理。

二、增值电信业务

(一)电子邮件;

(二)语音信箱;

(三)在线信息库存储和检索;

(四)电子数据交换;

(五)在线数据处理与交易处理;

(六)增值传真;

(七)互联网接入服务;


(八)互联网信息服务;


(九)可视电话会议服务。

标签:

你的Google搜索A-Z是什么?

2010年2月5日 2 条评论

今天看到一个帖子原文),尝试用各个搜索引擎搜索A-Z的字母,看看出来的结果,用来评价公司的有名程度。不过一个说法是Google会根据用户的习惯对搜索引擎的排序进行优化,所以可能每个人看到的顺序并不一样,所以我自己来试一下,果然很不一样。

下面这个表是原文的帖子当中加上的一列,看看我的Google搜索习惯和大众到底有多大差别。

字母 baidu.com google.cn google.com 我的google.com
a Acfun(视频网站) Adobe Reader Amazon(亚马逊) Apache Foundation
www.apache.org/
b baidu baidu Best Buy(百思百) boards.4chan.org/b/
这个这个,很不和谐……
c cf(腾讯游戏) CCTV(谷歌更爱国 :-)) Craigslist C编程语言
en.wikipedia.org/wiki/C_(programming_language)
d dnf(腾讯游戏) dnf Dictionary.com D编程语言
digitalmars.com/d/
e ems(邮政) ems(邮政) eBay www.eonline.com
这是啥?
f flash facebook Facebook 福特汽车的股票行情
finance.yahoo.com/q?s=F
g google google earth Gmail Gmail
h hao123 hao123 Hotmail H&M
www.hm.com/us/
i is IP地址查询 IMDB(电影资料) Apple iTunes!!!!
www.apple.com/itunes/
j java java J.C. Penny(百货商店) J编程语言
www.jsoftware.com
k kaixin(开心网) kugou(酷狗) Kohl’s(百货商店) www.krecs.com
l lady gaga lv Lowe’s(装修商店) www.chicago-l.org/
芝加哥快速交通
m msn msn MySpace www.myspace.com/mward
n nba nba Netflix(DVD出租) www.metanetsoftware.com/
o oppo office 2003 Office Depot(装修商店) www.cirquedusoleil.com/en/shows/o/default.aspx
又是一个不知道是什么的东东……
p pps(网络电视) pps Pandora(音乐) twitter.com/p
paolo i. (p) on Twitter!!!!
q qq 空间 qq QVC(网上零售) www.kju-app.org/
又是一个不知道做什么的网站……
r 人人(人人网) real player Realtor.com(房地产网站) www.r-project.org/
又是一个编程语言……

s sina(新浪) sina Southwest airline(航空) www.macys.com/
美国著名百货商店
t taobao(淘宝) taobao Target(百货商店) www.mbta.com/
这是啥??
u u9 ut US Post service(邮局) www.utah.edu
University of Utah (U of U)
v vs verycd Verizon Wireless(电信) abc.go.com/shows/v
软件?
w wow(魔兽世界) www.baidu.com(谷歌这儿是百度,百度那儿倒不是) Wal-Mart(沃尔玛) www.imdb.com/title/tt1175491/
电影W
x xiaonei xiaonei Xbox 360(微软游戏机) www.x.org/
著名开源软件XWindow
y yy yahoo Youtube yahoo
z zol(中关村在线) zol Zillow(房地产网站) www.cpuid.com/cpuz.php
CPU-Z软件

一片狼藉啊。

标签:

《劫后天府泪纵横》入围奥斯卡

2010年2月3日 6 条评论
China's Unnatural Disaster

劫后天府泪纵横

今天的消息,曾经获得15次美国艾美奖的记录片名导约翰-爱尔伯特(Jon Alpert)与助理马修-欧尼尔(Matthew O’Neill)拍摄的反映四川5.12地震的纪录短片《劫后天府泪纵横》入围奥斯卡最佳纪录短片奖提名。当然了,这个消息在国内一定是不起眼找不到的。即使是三大门户唯一列出完整提名列表的搜狐,其中也只有一部没有中文名字的《China’s Unnatural Disaster》的电影,其他的门户,则干脆没有了。(最近更新:搜狐的也404了)。可以相信,大家今年不要指望能够看到现场直播的奥斯卡颁奖了,如果能够看到,中间有一段也一定会是信号中断的。

我一直觉得,这部被翻译成《劫后天府泪纵横》的电影,其英文主标题其实更有味道:Natural Disaster叫做天灾,那么Unnatural Disaster叫什么呢?也许直白地翻译成《中国人祸》更加合适。

先不写影评,有兴趣的可以下下来看看。

ED2K | BT | 本blog缓存版

标签: , ,