文章归档

Lua之魂?

云风翻译了 《Masterminds of Programming: Conversations with the Creators of Major Programming Languages》中关于Lua发明人的一段对话,甚好,看来有必要一读此书

7. Lua

Lua 是一门非常之小,但五脏俱全的动态语言。它由 Roberto Ierusalimschy、Luiz Henrique de Figueiredo 和 Waldemar Celes在1993年创建。Lua 拥有一组精简的强大特性,以及容易使用的 C API ,这使得它易于嵌入与扩展来表达特定领域的概念。Lua在专有软件界声名显赫。例如,在诸多游戏中,比如 Blizzard(暴雪)公司的《魔兽世界》和 Crytek GmbH 公司的《孤岛危机》,还有 Adobe 的 Photoshop Lightroom ,都使用它来作脚本 和 UI 方面的工作。它继承了 Lisp 和 Scheme,或许还有 AWK 的血脉 ; 在设计上类似于 JavaScript、Icon 和 Tcl。

7.1 脚本的威力

你是如何定义 Lua 的?

LHF:一种可嵌入,轻量,快速,功能强大的脚本语言。

Roberto:不幸的是,越来越多的人们使用“脚本语言”作为“动态语言”的代名词。现在,甚至是 Erlang 或者 Scheme 都被称为脚本语言。这非常糟糕,因为我们无法精确的描述一类特定的动态语言。在最初的含义解释中,Lua 是一种脚本语言,这种语言通常用来控制其它语言编写的其他组件。

人们在使用Lua设计软件时,应该注意些什么呢?

Luiz:我想应该是用 Lua 的方式来做事。不建议去模拟出所有你在其它语言中用到的东西。你应该真的去用这个语言提供的特性,我想对于使用任何一门语言都是这样的。就 Lua 来讲,语言的特性主要指用 table 表示所有的东西,用 metamethod 做出优雅的解决方案。还有 coroutine 。

Lua 的用户应该是哪些人呢?

Roberto :我认为大多数没有脚本功能的应用程序都能从 Lua 中受益。

Luiz:问题在于,大多数设计者很长时间都不会意识到有这种需求。当已经有了诸多用 C 或 C++ 编写的代码,为时已晚。应用程序设计者应该从一开始就考虑脚本。这会给它们带来更多的灵活性。而且这样做还可以更好的把握性能问题。因为这样做以后,会迫 使他们去考虑程序中到底哪里是性能关键,而哪些地方无伤大雅。而这些性能不太重要之处,就交给脚本去处理,开发周期短,速度快。

从安全性的观点来看,Lua 能为程序员提供些什么呢?

Roberto:Lua 解释器的核心部分被构建为一个 “独立的应用程序(freestanding application)”。这个术语来自 ISO C,大意是说,这部分不使用任何跟外部环境有关的东西(不依赖 stdio、malloc 等)。所有那些功能都由扩展库来提供。使用这种体系结构,很容易让程序限制对外部资源的访问。具体来说,我们可以在 Lua 自身的内部创建出一个沙盒,把如何我们认为危险的操作从沙盒的外部环境中剔除。(比如打开文件等)

Luiz:Lua

»» 继续阅读全文

1、spinlock

关于MCS Spinlock的锁释放问题补充一点,关键的问题是,A在判断自己是否是最后一个申请者和如果是的话就释放mcs_lock,这两个操作无法保证原子性(而且必须是原子才行,因为这中间如果出现新的申请者,情况就会变化),所以 才要依赖cmp_and_swap操作。

ticket spinlock和mcs_spinlock其实思路是一样的。

还有很重要的一个就是:申请了锁却忘了释放绝对是致命的

2、RCU

go语言TCPConn.Close()方法处理不当引发的bug

今天服务器引发了一个很罕见的bug。原因是因为TCPConn在Close的时候,把fd给清空掉了,如下:

// Close closes the TCP connection. func (c *TCPConn) Close() error { if !c.ok() { return syscall.EINVAL } err := c.fd.Close() c.fd = nil return err }

而TCPConn是经常在多个goroutine里同时使用的。

// Read implements the Conn Read method. func (c *TCPConn) Read(b []byte) (n int, err error) { if !c.ok() { return 0, syscall.EINVAL } return c.fd.Read(b) }

这样就很有可能当某个goroutine,判断!c.ok(),为真,然后尝试读取数据时。注意,这个过程,中间,if语句之后和Read()数据之前,很可能TCPConn已经被关闭,fd被置为nil了,引发panic

go1版本有这个问题。新版已经fix了。不过我还觉得蛮奇怪的,想不清楚为什么作者要这么写。。。

Open Clound storage service

国庆这几天写的,套用阿里云的名称,就叫开放云存储服务吧,初衷只是想测试一下存储商的速度,后来发觉php写的SpeedTest不太好用,因为要依赖php和其他各个组件。

后来折腾了一下hiphop,这玩意依赖太过分了,用的很不爽,就用go重新实现了一遍存储的SDK。目前OCSS支持四个主流的云存储服务,包括盛大云,阿里云,又拍和七牛。它们的API是一样的(看起来是一样的,呵呵)

数据结构

在package fs里,有Object和Bucket,主要的结构如下,但不同的存储商的SDK又各有一些不同的地方,可看源码

type Object struct { EntryURI string Type string Mtime string Size int64 Body io.ReadWriter } type Bucket struct { EntryURI, Delimiter string Objs []*Object CommonPrefixes []string }

EntryURI通常由Bucket和Key组成,例如"/testbucket/resume.pdf"或者是"/testbucket/music/",Object可以表示一个文件或者目录。Bucket里的Delimiter是用来进行前缀查询的,会一直匹配到Delimiter才结束,然后返回的对象会保存到Objs和CommonPrefixes里,Objs通常可以理解成文件,CommonPrefixes可理解成当前路径下的文件夹名

存储API

所有的SDK都实现了如下的API,用法几乎是完全一样的,当然,还有其他的。

func (s *Service) Get(localfile, entryURI string) (code int, err error) func (s *Service) Put(entryURI, mimeType, localfile string) (code int, err error) func (s *Service) Delete(entryURI string) (code int, err error) func (s *Service) PutObject(o *fs.Object) (code int, err error) func (s *Service) GetObject(o *fs.Object) (code int, err error) func (s *Service) DelObject(o *fs.Object) (code int, err error) func (s *Service) NewBucket(b *fs.Bucket) (code int, err

»» 继续阅读全文

转让两部闲置手机

首先是HTC touch HD,wm 6.1系统,经典机型,已用4年,功能一切正常,是当年买的法国Orange的水货,原价2800,现在200出吧,:-) 其他参数就不说了,喜欢的可自行查一下参数

------- 送给喜欢怀旧的小伙伴们

另外一款就是诺基亚N97,用了不到三年,如下:

SILT: small index and Large table

SILT's multi-store design

The most common approach to build a hign performence key-value storage system uses two components:logstore and sortedstore. But in the implementation of SILT, it add a HashStore for sloving the high write amlication problem when merge logstore into sortedstore by traditional way. how to manage the sortedstore isn't key to performance, i think, just a little infulence.

In fact, SILT isn't a generic key-value store because of its keys are uniformly distributed 160bit hash value(e.g., pre-hashed key with SHA-1), but doing it fixed length as SILT have done here is so much convenient for compressing

»» 继续阅读全文

Libtask: c和linux/unix下的协程库

实现协程核心的文件只有几个,其中*-ucontext.h是特定平台上下文切换功能的实现,是几个用户态的API。以386平台为例:

extern int swapcontext(ucontext_t*, const ucontext_t*); extern void makecontext(ucontext_t*, void(*)(), int, ...); extern int getmcontext(mcontext_t*); extern void setmcontext(const mcontext_t*);

task.c/task.h是调度器的实现。其实明白上面这几个API,基本上就可以知道协程是怎么干活的了。下面主要分析task.c代码。

使用libtask构建程序不需要显式地使用main函数,只需一个taskmain即可,因为main函数是在task.c里实现的,也就是libtask的调度。

----- task.c ------ int main(int argc, char **argv) { .... taskcreate(taskmainstart, nil, mainstacksize); taskscheduler(); .... }

taskcreate创建了第一个协程,这个协程就是用户的taskmain函数,将其加入调度队列,然后进入调度函数taskscheduler(),taskscheduler()不会返回

static void taskscheduler(void) { int i; Task *t; for(;;){ if(taskcount == 0) exit(taskexitval); t = taskrunqueue.head; if(t == nil){ fprint(2, "no runnable tasks! %d tasks stalled\n", taskcount); exit(1); } deltask(&taskrunqueue, t); t->ready = 0; taskrunning = t; tasknswitch++; contextswitch(&taskschedcontext, &t->context); taskrunning = nil; if(t->exiting){ if(!t->system) taskcount--; i = t->alltaskslot; alltask[i] = alltask[--nalltask]; alltask[i]->alltaskslot = i; free(t); } } }

contextswitch切换到到协程t去执行了,从t返回的情况有两种,要么是协程执行完毕了,要么是用户主动的yield,然后等待下次继续被执行。

<busying, todo...>

LIRS缓存

参考文献:LIRS: An Efficient Low Inter-reference Recency Set Replacement Policy to Improve Buffer Cache Performance

LIRS算法主要计算两个参数,IRR和Recency,IRR表示一个页面最近两次被访问访问间隔,Recency表示页面最近一次被访问到现在的访问间隔。IRR越小,表明最近可能经常被访问,Recency越大,表明最近很久没被访问。IRR和Recency的计算方法如下:

这就是LIRS的理论模型,在任何一个时刻,缓存中的每个页面都会有一个IRR值和Recency值。LIRS的核心思想是,如果一个页面当前的IRR越大,将来的IRR会更大。

论文中没有对LIRS的实现作过多的描述,只是简单提到了一个基于Stack的实现方法。

一、替换规则

和LRU不同的是,就是当缓存达到阈值时

  1. 如果是LRU,就会将Recency值最大的那个页面剔除出去,因为这个页面是最近最久没有被访问的
  2. 如果是LIRS,优先剔除IRR值最大的页面,因为IRR越大,说明这个页面过去很少被访问,理所当然认为它在将来可能不会再被访问或者相对其他页面来说最不可能再被访问到。如果有两个页面的IRR相同,则剔除Recency值最大的页面。

显然,LIRS只是一个多了IRR参数的LRU

(待)

linear counting algorithm

最近看了篇文章:Big Data Counting: How to count a billion distinct objects using only 1.5KB of Memory

比较感兴趣,于是找了原论文来看看:A Linear-Time Probabilistic Counting Algorithm for Database Applications / HyperLogLog : the analysis of a near-optimal cardinality estimation algorithm. 无奈hyperloglog实在复杂,难以理解,哪天看懂了再分享吧。

linear counting的作用就是统计集合的容量。算法思想和bloom-filter差不多,很多时候精确计算是很难做到的,但是可以允许一定的误差,从而通过这个误差的概率分析来折射整个全局的概貌。

  1. Algorithm Basic Linear Counting:
  2. Let key_{i} = the key for the ith tuple in the relation.
  3. Initialize the bit map to "0"s.
  4. for i = 1 to q do
  5. hash_value = hash( key_{i} )
  6. bit map(hash_value) = "1"
  7. end for
  8. U_{n} = number of "0"s in the bit map
  9. »» 继续阅读全文

zeroqp, make the server network programming sample and interesting.

zeroqp 是我这两天做的一个小东西。将传统的服务器网络编程转为服务化。zeroqp适合特定的场景,比如我自身的需求。所以zeroqp不是万能的。在其他更多的场合可能反而会令程序变得更加蹩脚。用自己的工具,解决自己的问题。好吧。我承认我又在意淫了。。。

zeroqp 主要有三个简单的API,zeroqp_open(), zeroq_ctl(), zeroqp_wait()

zeroqp_open() 会返回一个专用的描述符,但是并不是一个真正的文件句柄,自然不能用read() write()等系统调用去操作它。zeroqp_ctl() 函数用来向系统注册一个服务,或者删除一个服务。而只要 zeroqp_wait() 一旦执行,系统就会处于监听状态。zeroqp 会启动所有的服务并打开相应的端口,等待客户端进行链接。我们需要做的,仅仅是在zeroqp_wait()启动之前注册一些服务而已。

  1. int32_t zeroqp_ctl(int32_t zfd, int32_t op, struct zeroqp_service *zs);
  2. #define ZEROQP_CTL_ADD 0x01
  3. #define ZEROQP_CTL_DEL 0x02

服务是由结构提 struct zeroqp_service 装载的,在安装服务之前需要一些服务的基本信息,struct zeroqp_service 的结构如下,

  1. struct zeroqp_service {
  2. int8_t inet_addr[INET_ADDRSTRLEN];
  3. int32_t inet_port;
  4. int8_t zqp_srv[ZQPS_NAMESTRLEN];
  5. _srv_callback *zqp_cbfunc;
  6. }

inet_addr 字段,保存的是IP地址串,可以是IPv4也可以是IPv6,inet_port为该服务器上的某个端口,zqp_srv是需要注册的服务名字,是zeroqp_service的唯一标识。zqp_cbfunc是服务的回调函数。

zeroqp 所有的服务都是异步执行的,单线程,适合快速响应的需求,如IO密集型服务,不适合计算密集型的场景。不过大家可以改成自己的,分享一下就更好了。

举个简单的例子来描述一下zeroqp的用途:

某个公司有个客服部门,主要的工作就是向客户提供服务。至于提供什么服务,是由公司来决定的。比如客户需要维修电脑,会先打个电话给该公司的客服部门,该公司的客服部门接受到需求之后,先分析这个服务是不是我们可以提供的,如果可以,就转交到相应的人员去处理,如果不是,这个过程就结束了。

在这里,公司就是使用zeroqp的开发人员,你可以给你的server定制各种服务,接受服务请求是epoll来异步通知的。struct zeroqp_service 结构的作用是相当于告诉操作系统,我需要在某个IP地址和端口上打开一条热线电话。实际上tcp_listen()了一个监听端口。然后在这个端口上绑定各种定制的服务。

zeroqp 允许你在同一个IP地址和端口上安装不同的服务。

zeroqp 同样允许你在不同的IP地址和端口上安装相同的服务。

甚至,zeroqp 允许你在相同的IP地址和不同的端口上安装相同的服务。。。哈哈,混乱了吧。。。

其实我知道你只有一个IP,所以zeroqp就是说,让你在不同的端口安装不同的服务,每个端口有自己的服务队列,不同端口的服务之间是相互独立的,不受影响的。

_srv_callback *zqp_cbfunc; zqp_cbfunc是服务执行的回调函数,就是说如果某个客户端链接上来了,需要请求某个服务,就执行这个callback function()。zeroqp 会自动分析和提取客户端提供的各种参数,然后通过 _srv_callback 的入口 argc 和 argv 传入给 cbfunc。

_srv_callback(int32_t fd, int32_t argc, slice **argv);

比如说,我在server上安装了一个名为 sum 的服务,专门负责计算各个参数的和。然后客户端链接server,需要使用这种服务,client提供了三个参数 23,43,33,当server检测到有需求请求时,分析这个请求是哪个服务的然后转向执行相应的回调函数,也就是上面的_srv_callback实例对象。client提供的参数会通过字符串传入 _srv_callback 里。在这里 argc = 3, argv[0]->data = "23",argv[1]->data =

»» 继续阅读全文

第 10 页,共 13 页« 最新...89101112...最旧 »