2010年12月31日星期五

网络(非GPS)定位原理

有次在地铁上,跟朱朝卓同学聊到这样一个话题,即可装13又省钱的方法:买个可以做3G 路由的廉价手机(比如华为U8500),再买个ipod touch,用手机打电话和wifi热点,这样不到3000元就可以享受Apple产品,经济实惠!恩,的确是个很不错的途径!但是当时我有过顾虑:ipod touch使用wifi,LBS(物理定位服务)恐怕都用不成了,因为不能用GPS了嘛!但是朱朝卓告诉我是可以的,ucweb通过wifi可以定位!当时我的想法是,难道是通过ip定位?肯定很不准确吧?


今天中午,和双木成林同学聊天得知:用wifi定位,很准确,我还是觉得应该公司ip比较固定的缘故!但是双木成林接着说,在他家里定位也很准确!这就奇怪了,难道Google跟中国运营商有合作?这样需要满足以下条件才能定位:



  1. 当用户连接网络时,运营商给用户分配地址的同时,同事记录了你的连接账号和分配ip的映射关系,通过用户的账号自然知道用户的地理位置,因为用户申请账号的时候都填写家庭或者公司地址

  2. Google可以随时根据ip去访问运行商的接口获知详细地理位置。既然google都可以,那相关部门肯定也可以啊!


如果真是这样,那就危险了?自己在任何地方,都没有隐私可言?被跨省也易如反掌啊!接着认真查看了Google Geolocaion API,发现并不是这样!



Many devices do not have native access to GPS or other location data. Additionally, GPS can take a long time to get an accurate location fix, drains battery, and does not work indoors. Because of these problems, the location API also has the ability to send various signals that the devices has access to (nearby cell sites, wifi nodes, etc) to a third-party location service provider, who can resolve the signals into a location estimate.



大概的过程是这样的:Google是很大型的公司,每天都会分配很多工作人员到各城市满大街乱跑,去收集无线热点信息(包括cell sites和wifi nodes),将那些固定、持久的无线热点以及地理信息保存到Google地理信息库中。当有浏览器或者应用程序请求地理信息时,会收集电脑或者手机附近的无线热点,发送给Google解析,从而获取详细的地址信息。


因此可以说,如果你的电脑或者手机没有无线设备或者禁用了无线设备,单凭ip是获取不到详细地理信息的!


测试:使用支持html5的浏览器(Firefox 或者 chrome)访问http://html5demos.com/geo,点击“允许”浏览器跟踪您的地理信息,如果你当前打开了无线网卡,看看定位是否准确?我的电脑是台式机,不能定位,我让我女朋友测试,不知情的她说:我是特工。


References & Resoucres:


2010年12月19日星期日

Thinking in yield

yield作为python的一个关键字,如果在函数体使用,那么这个调用这个函数返回一个“生成迭代器”(generator iterator),当然在平时都称呼为“生成器”。xrange函数就是我们常见的调用后返回生成器(当然这个是已经经过包装的生成器),xrange与range的区别是什么?常见的回答当然是:xrange省内存,在遍历的时候需要的时候才会生成所需要的时,而不像range,首先把所有的值都生成好保存在一个list中。恩,这的确是他们的最表面的区别。


为了更深入的了解生成器,还是先介绍下生成器有的特性和特点吧:



  • 可以迭代(恩,这个特性是废话,从名字都可以看出来,但是这的确是生成器最基本的一个特性)

  • 通过调用next函数或者send函数(其实next() = send(None) ),会执行到yield语句,就会被冻结,冻结后就返回它的caller;直至调用下次send函数从上次冻结点接着执行

  • 当生成器对象引用计数为0被回收时,如果发现生成器对象仍然被冻结,就会调用close函数,close函数的作用就是抛出一个GeneratorExit的异常

  • 生成器只能被迭代一次(有点惊讶?我看源码的时候,发现这个的时候也有点)


那generator是如何实现的呢?看了源码其实很简单,在初始化一个生成器对象,都需要一个参数: PyFrameObject * f。这个f就是生成器函数的栈帧,当函数被冻结时,记录这个栈帧的栈点stacktop 以及虚拟机字节码位置f_lasti。下次执行的时候直接从这个字节码位置和栈点执行。一切很简单吧!


一切谜底都要从python源码Python/ceval.c中的PyEval_EvalFrameEx开始,初始化代码:


 


    /* An explanation is in order for the next line.

f->f_lasti now refers to the index of the last instruction
executed. You might think this was obvious from the name, but
this wasn't always true before 2.3! PyFrame_New now sets
f->f_lasti to -1 (i.e. the index *before* the first instruction)
and YIELD_VALUE doesn't fiddle with f_lasti any more. So this
does work. Promise. */
next_instr = first_instr + f->f_lasti + 1;
stack_pointer = f->f_stacktop;
assert(stack_pointer != NULL);
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */

 


那yield或者说生成器都有哪些应用呢:



  • 生成迭代器(靠,又是废话)

  • 与with_statement结合使用

  • 著名的"Trampoline in python"

  • 轻量级任务

  • 其他……


Resouces & References:


2010年12月13日星期一

Git与SVN的不同

以前写过一个blog:Git初学习,现在读来觉得当初对于git的理解真的很肤浅,现在就来说说git与svn的不同:



  • 分布式。这是git最明显的一个特征,分布式SCM。分布式也就要求,分布式中的每台机都保存了整个代码仓库所需要的所有代码和资源,而不仅仅是当前最新版本。分布式也带来很多好处:可以在离线的情况下版本控制;另外与集中式SCM不同,即使服务器挂掉也无妨,可以采用任何一台机进行恢复。

  • 版本。svn的版本有着revision的概念,版本号是递增的。版本号能够递增,也是因为svn是集中式SCM的缘故。当多个用户同时提交时,svn服务器会将用户之间的提交操作串行。git是分布式SCM,没有递增的版本号,git采用的做法是:在保存到git之前,git将所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引,作为版本号。

  • 储存方式。svn每个版本提交时,保存的是当前版本与上个版本之间的diff。而git提交时,则是快照。比如git commit了文件A和B,则提交完成后,至少创建了5个对象,新的文件BLOB对象A和B;一个记录着目录树内容及其各个文件对应BLOB对象索引的tree对象;一个包含指向tree对象(根目录)的索引和其他提交信息元数据的commit对象。如果当前代码仓库还有文件C,由于文件C并没有修改,所以tree对象中保存的便是上个版本文件C的BLOB对象的索引。但是一旦文件被修改,比如A和B,就会创建一个新的BLOB文件对象,而不是仅仅的快照。git这种做法虽然牺牲了存储空间(现在存储空间很廉价的吧),但是当比较两个版本或者合并分支的时候,速度上会有明显的提升。

  • 真正的分支和tag。使用过svn都知道,svn中的分支和tag都是我们认为赋予的概念,都是通过svn copy复制到另外一个目录,通过目录或者人为命名标记为分支或者tag,并且人为的规定:tag是只读的,不能修改,而实际上可以修改的。而git则实现了真正的创建一个分支很简单,仅仅是创建一个branche对象,其中branche对象包含有指定commit对象的校验和(即版本号),当随着开发的进行,提交新版本时,branche对象直接修改版本号即可。创建tag也是如此,新建一个tag对象,包含指定commit对象的校验和和其他数据信息。这也就是为什么在git下,创建分支和标签为什么这么快的根本原因。

openvpn随机启动并自动连接

在写这篇blog前,首先还得感谢郭嘉,因为Google App Engine 解封了,手机可以正常发推了,博客不用翻墙也可以登录了。


上周五一冲动还是买了一个ssh代理,虽然自己有个速度很快的免费openvpn,主要还是考虑:



  • vpn的确有着自身的一些软肋:所有境外ip都要走vpn,启动需要手动输入账号密码,修改route比较麻烦

  • 免费申请的openvpn规定不能观看youtube,虽然在技术上并没有做任何限制,但是由于免费申请来的,自己经常看,也不好意思

  • 上周五,免费申请的openvpn突然不给力了,速度突然慢起来

  • 自己免费申请的openvpn速度都很赞,想着如果自己买个岂不是更快


所以就在敏感词上的ssh广告买了一个“45元/366天规格“的ssh服务,当时测试下载速度很快,500kb/s左右,在账号为到之前,我觉得用了这个ssh代理会秒开twitter,facebook,流畅观看youtube。晚上做梦的时候还是翻墙。


可是第二天账号到的时候,才发现自己错了,购买的ssh代理服务只是比gappproxy快点而已,没有自己免费申请的openvpn,开始还以为是自己的客户端软件myEnTunnel的plink核心做了限速,谁知修复了这个问题或者换了Tunnerler都不管用,现在我才意识到下载测试速度跟真是翻墙代理的速度不是一个概念,很有可能是代理服务器对账号做了限速。自己不得不换回免费申请的openvpn。


使用openvpn时,觉得有几点不爽:



  • 每次都要手动输入账号密码,官方认为将账号和密码保存起来不太安全

  • 不能自动连接,虽然可以随机启动


上网google了下,果然得到了解决。


首先说在windows下,虽然官方没有推出保存保存账号和密码的openvpn-GUI版本,但是已经有人为了方便,自己修改了openvpn源码,重新编译好了,供我们下载。使用起来也很方便,直接将client.ovpn中的auth-user-pass改成auth-user-pass mypass.pwd即可。其中mypass.pwd是保存着账号密码的文件,第一行为账号,第二行为密码。在windows下系统启动并自动连接,直接修改注册表即可,即:


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
$OPENVPN_PATH --connect client.ovpn --sclient-connection 1

在ubuntu下,随机启动并且自动连接也很简单,也是跟上面一样修改clien.ovpn中的auth-user-pass,但是需要在编译安装openvpn的时候,需要指定enable-user-pass。不过幸运的是,如果你采用sudo apt-get install openvpn,已经默认支持保存账户密码了。


Resources & References: