Memcached的线程模型及状态机

2011年9月5日 6 条评论 14,222 人阅读过  

Memcached是一种应用较广泛的分布式内存对象缓存系统,应用之余总想了解它的实现机理,这也就是开源的好处,以至于每接触一款优秀的开源软件都有去阅读它源代码的冲动,Memcached-1.4.7的代码量还是可以接受的,只有10K行左右,我比较关心的两个方面还是它的进程(线程)管理机制和内存管理机制,这里先简单写一下我对Memcached进程管理方面的理解。

Memcached使用libevent实现事件循环,libevent在Linux环境下默认采用epoll作为IO多路复用方法,这个不重要,接下来要讨论的是Memcached的进程管理模型。

Memcached采用了很典型的Master-Worker模型,采用的是多线程而不是多进程,而线程之间没有冗余的共享数据,这样便降低了多线程进行线程同步的开销,核心的共享数据是消息队列,主线程会把收到的事件请求放入队列,随后调度程序会选择一个空闲的Worker线程来从队列中取出事件请求进行处理。

在main()函数里面,Memcached为主线程调用event_init()创建了一个libevent base对象,随后调用thread_init()来初始化线程,我们来看下这个函数的实现。

...
分类: NoSql 标签: , ,

Hybrid开发手记之聊天窗口的WebKit支持

2011年8月5日 16 条评论 10,179 人阅读过  

近两天给Hybrid(https://github.com/levin108/hybrid)的聊天窗口加上了WebKit支持,之前没有实际用过WebKit,而且Web前台开发功力也不强,草草做了一个界面,但相比用GtkTextView来实现看上去还是要舒服好多,先上个图吧:

本篇没有什么高深的东西,作为一个简单的开发文档。

...
分类: C/C++ 标签: ,

新开源项目Hybrid开发手记

2011年7月30日 31 条评论 23,409 人阅读过  

博客有两个月没更新了吧,先说说这段时间都在做些什么吧,六月初的时候实验室项目验收完,以为可以轻松下来了,于是开了一个新的开源项目在做,这个稍后再说,没过多久就被拉去给新项目做设计文档去了,这活做起来比硬编码要麻烦得多,于是大多数时间都在为这个事情心烦,稍微有点闲下来的时间就去写点代码,接下来说下最近在做的这个开源项目。

之前的Openfetion虽然也受到了一些开源社区朋友的好评,但软件质量怎么想我心里比谁都明白,在twitter上我也公开承认过Openfetion的代码质量以及软件架构都非常差,说到代码质量,最初在开发Openfetion的时候没有想过会把它作为一个通用的软件拿出来给大家用,而是自己纯粹在写着玩,于是写得很随意,当然这也得怪我这种恶劣的编程习惯,专业的coder即便是写一个测试用的小代码也会严格按规范来,这是一种习惯,我承认之前做得不够好。再说到软件架构,对于IM软件我之前一样没有什么经验,在没有经过调研的情况就盲目开始编码,完全没有参考现有的开源软件架构,甚至没有个合适的事件循环,所以Openfetion目前的状态是勉强能用。

之前考虑过重构Openfetion,但其实重构的成本要远远高于重写,于是我决定终止Openfetion项目,不再为Openfetion增加/修改代码,也不鼓励其它人对它进行修改。

...
分类: C/C++ 标签: ,

武汉Deepin Linux DAU会议见闻

2011年6月1日 7 条评论 9,429 人阅读过  

上次在ubuntu11.04 release party北京站遇到deepin linux负责人刘文欢,他也就是deepin本人,邀请我去参加上个周末在武汉举行的deepin linux DAU大会,当时没有马上答应他,因为怕实验室太忙时间安排不开,后来跟领导商量了下领导表示支持,于是就趁周末跑了过去,一方面是希望能见到开源社区的朋友,另一方面也是很想看看deepin这个出自国人之手的linux发行版会做到什么水准。

会议到场人数很多,我作为特邀嘉宾很惭愧地坐在了前排,会议第一部分是deepin主题演讲,deepin操作系统创始人hiweed介绍深度的发展以及现状,接着是深度软件中心的主要开发者王勇介绍软件中心的相关情况,我个人感觉软件中心还是一个很不错的软件,虽然看上去很容易让人联想到360的软件中心,但不能不说这确实是一个很符合用户使用习惯的软件,从软件的搜索和安装都比ubuntu软件中心要舒服一些,当然这也是我个人的感受,据deepin介绍,软件中心的开发者王勇只有22岁,而且并没有读过大学,当时我就对他特别敬佩,像我们这些读了这么多年书现在还是这样技术平平的人真应该感到惭愧。最后由deepin本人介绍开源社区的管理与运营,让大家对深度这个社区有了一个比较深入的认识。

会议的第二部分是主题嘉宾演讲,第一位是国内开源界的名人袁萌教授,讲永中Office的事情,袁萌可以说是相当有争议的人物了,之前在ubuntu 10.10和11.04的release party现场都见过袁老出现,而且对于这种开源活动袁老似乎每次都不会缺席,这种活动每次出现的为数不多的老年人身影也就只有袁老了,但不管是他在新浪博客上还是在其它地方,大家对他的评论都是褒贬不一,我之前对袁老不熟悉,后来在twitter上打听袁老的事情,有人表示袁老人不错,但口头老挂着永中和U盘的事情就让人有点烦了,确实,袁老讲话我听不太明白(口音原因),但言谈中总会提到永中和U盘的事情,我对此到没什么特别的看法,可能年纪大了人会比较偏执,遇到自己觉得好的东西就会一直推崇。再看他的新浪博客,有人支持也有人很负面地骂,确实让人感觉到袁老真是中国开源界最有争议的人物,其实我觉得像袁老这样一...

分类: Linux 标签: ,

Apache MPM Prefork设计方法浅析

2011年5月16日 2 条评论 9,958 人阅读过  

最近几天翻阅了apache的MPM(Multi-Processing Module)机制相关的代码,虽然还有很多细节没有搞明白,但对apache的服务器模型有了一个大体的概念,对于不同的操作系统,apache提供了不同的默认MPM模型,下表是不同操作系统默认的MPM模型:

BeOS beos
Netware mpm_netware
OS/2 mpmt_os2
Unix prefork
Windows mpm_winnt

Unix平台则对应着prefork模型,prefork从名字上看意思是预先生成子进程,所以这种模型大致上是怎么工作的我们心里差不多有些认识了,prefork是一种很重要的服务器程序设计模型,对应的还有prethread,prefork一般应用在Unix平台上,因为在服务器启动时需要预告fork出一些空闲的子进程,由它们共同监听客户端的请求,这样来实现快速高并发的特性,这种机制之所以不适合Windows等平台,是因为在Windows等平台上进程的代价太高。

apache的进程管理中有一个叫做scoreboard(记分牌)的概念,主进程在进入MPM循环以前会先在进程池中创建一个scoreboard对象,该对象定义如下:

...
分类: C/C++ 标签:

Openfetion for ubuntu messaging menu开发手记

2011年5月3日 7 条评论 11,771 人阅读过  

自从ubuntu11.04发布之后Openfetion就遇到了一个比较麻烦的问题,把Openfetion飞信最小化到托盘之后就找不到了,没用过ubuntu11.04,不过据说它的unity桌面貌似没有status icon这回事,所以把Openfetion塞进Messaging Menu也成了一个很重要的任务,在这里把开发过程和大家分享。

首先要感谢@YunQiangSu提供的关于ubuntu messaging menu的资料,是在ubuntu wiki上关于Messaging Menu的介绍,链接在此:https://wiki.ubuntu.com/MessagingMenu/,这篇文章是关于Messaging Menu的一个介绍,以及它的行为和样式的一个指南,虽然没有涉及到具体的开发细则,但也不失为一个很重要的参考。很惭愧地说,在这之前我甚至不知道在ubuntu右上角看到的那个信封到底叫什么名字,后来知道它原来是一个Indicator,ubuntu的status icon区域很多软件都是用libappindicator来实现的,所以它们的行为和其它的发行版不太一致,比如Dropbox和Transimission左键点击Status Icon就可以弹出菜单,而在Slackware里面就只能用右键才能弹出菜单,这就是它们的不同,一个是Indicator,一个是普通的Gnome Status Icon,而这里面提到的右上角的那个信封便是Indiactor的一种,名字叫做Messaging Menu。好吧,我相信很多ubuntu用户会过来鄙视我的,写下这些给那些和我一样的小白扫扫盲,有误请指正。

我们可以把软件安装到Messaging Menu里面,这样即使软件没有启动也可以在Messaging Menu里面找到该软件,并可以从那里启动该软件,方法很简单。下面是pidgin的做法:

mkdir -p debian/pidgin/usr/share/indicators/messages/applications
echo /usr/share/applications/pidgin.desktop > \
		 debian/pidgin/usr/share/indicators/messages/applications/pidgin

这两句话的功能一眼就看地出来,无须解释了。

...
分类: C/C++ 标签: ,

Apache基础数据结构(tables)代码浅析

2011年4月29日 1 条评论 7,429 人阅读过  

提到Apache社区脑子里立马会呈现出一系列的Java项目集合,尽管@gnawux师兄教导我不要纠结于语言,但对于Java的抵制还是很难一下子就消失的,所幸Apache社区最重量级的项目Apache开源HTTP服务器httpd的源代码是完全使用C语言开发的,尽管近年来涌现出种种轻量级高性能Web服务器,Apache仍以它的功能广泛和真正的高性能而处于无可取代的位置。

Apache也经常受人诟病,矛头直指它的低效,我没有太多这方面的经验,因此没有过多的发言权,看到一些大牛们对这个问题的评论是觉得Apache低效是因为对Apache缺少了解。我个人也觉得这样一款多年风行Web服务器领域的软件肯定有它存在的理由,我们认为它不好可能是我们对它的了解不够。

在网上看到过关于Apache源码是否值得阅读的讨论,很多人认为Apache源码组织结构不好,不太适合阅读,更多人推荐nginx等源码,其实我觉得像Apache这种项目正是”重剑无锋,大巧不工“,Linux源码也有人评价说组织的不好,可读它的代码也仍然非常有意义。我个人认为阅读Apache源码一方面能加深对Apache的了解,在使用的时候真正发挥出它的高性能,另一方面也能借鉴它作为大型服务器软件设计的方法。当然,如果要编写扩展模块来实现一些附加功能的话那阅读源码甚至是必须的工作。

以上都是我个人的理解,最近简单地翻阅了部分代码,长时间没有更新博客,这里就写一些Apache里面最基本的tables数据结构(和其它模块关联较小,相对比较独立,当然我也觉得读任何源码都应该先搞清楚它最基础的数据结构)。

...
分类: C/C++ 标签:

浅析Linux Kernel 哈希路由表实现(二)

2011年3月15日 1 条评论 16,202 人阅读过  

在向外发送数据包的时候,首先需要查询路由表来确定路由包的路由,主要由ip_route_output_key()函数来完成,该函数又调用了ip_route_output_flow(),而这个函数最终又调用了__ip_route_output_key()这个函数来进行路由的查询,下面主要来看一下这个函数:

int __ip_route_output_key(struct net *net, struct rtable **rp,
			  const struct flowi *flp)
{
	unsigned int hash;
	int res;
	struct rtable *rth;
 
	if (!rt_caching(net))
		goto slow_output;
 
	hash = rt_hash(flp->fl4_dst, flp->fl4_src, flp->oif, rt_genid(net));
 
	rcu_read_lock_bh();
	for (rth = rcu_dereference_bh(rt_hash_table[hash].chain); rth;
		rth = rcu_dereference_bh(rth->dst.rt_next)) {
		if (rth->fl.fl4_dst == flp->fl4_dst &&
		    rth->fl.fl4_src == flp->fl4_src &&
		    rth->fl.iif == 0 &&
		    rth->fl.oif == flp->oif &&
		    rth->fl.mark == flp->mark &&
		    !((rth->fl.fl4_tos ^ flp->fl4_tos) &
			    (IPTOS_RT_MASK | RTO_ONLINK)) &&
		    net_eq(dev_net(rth->dst.dev), net) &&
		    !rt_is_expired(rth)) {
			dst_use(&rth->dst, jiffies);
			RT_CACHE_STAT_INC(out_hit);
			rcu_read_unlock_bh();
			*rp = rth;
			return 0;
		}
		RT_CACHE_STAT_INC(out_hlist_search);
	}
	rcu_read_unlock_bh();
 
slow_output:
	rcu_read_lock();
	res = ip_route_output_slow(net, rp, flp);
	rcu_read_unlock();
	return res;
}
...
分类: Kernel 标签: ,

浅析Linux Kernel 哈希路由表实现(一)

2011年3月13日 1 条评论 13,812 人阅读过  

1. 路由表

目前Linux内核中支持两种路由表,一种是Hash路由表,另一种是Trie路由表,Trie算法查找效率很高,但它也因极其消毫内存资源而闻名,因此一般用在大型机上,否则在路由项过多时很可能造成内存的耗尽。在一般的机器上最好还是使用Hash路由表,之前有争论说Linux使用Hash路由表相比于二叉树效率太低,不适合商用,其实只要设计良好的哈希算法,尽量减少哈希碰撞,Hash路由表的查找效率也是很高的,在最好的情况下算法复杂算可以达到O(1),当然,最差也不过是O(n),我们有理由相信Linux中存在各种优秀的哈希算法,这些都是值得我们学习的。

Linux内核中IPv4路由表采用了分层的结构,第一层是是fib_table,表示路由表,最多可以支持256张路由表,用户可以根据策略路由的需求来创建自己的路由表,系统默认的两张路由表为RT_TABLE_LOCAL和RT_TABLE_MAIN。下面是系统保留的路由表标识符:

enum rt_class_t {
	RT_TABLE_UNSPEC=0,
/* User defined values */
	RT_TABLE_COMPAT=252,
	RT_TABLE_DEFAULT=253,
	RT_TABLE_MAIN=254,
	RT_TABLE_LOCAL=255,
	RT_TABLE_MAX=0xFFFFFFFF
};

下面是路由表的定义:

...
分类: Kernel 标签: ,

浅析Linux Kernel中的那些链表

2011年3月6日 14 条评论 10,631 人阅读过  

链表是Linux内核中最重要的数据结构,但Linux内核中的链表与传统的数据结构书中看到的链表结构略有不同。这里简单写一下我对于Linux内核中链表的理解,不足之处欢迎路过的大牛给出批评意见。

1.传统形式的链表

数据结构书中的链表一般是下面这种形式:

struct list {
	struct list *pre;
	struct list *next;
	void *data;
};

每一个链表结构中都包括两个同类型的指针,分别指向链表的上一个节点和下一个节点。这样当该节点处于一个循环链表中时,链表的首节点一般不用于保存数据,但首节点也需要是一个struct list类型,当结构庞大时首节点也必须分配同样大小的空间,这样就造成了内存的浪费。

2. Linux内核中的双向循环链表

Linux内核中定义了一个struct list_head类型用于保存链表信息指针:

struct list_head {
	struct list_head *next, *prev;
};

如此在链表中链表头只需用一个struct list_head类型来表示即可,不管链表中节点的数据结构多么庞大,链表头只需要占8个字节,链表中的节点则如下定义:

struct list {
	struct list_head *list_head;
	void *data;
};

如此一来类似于list_add()和list_del()这些函数只需要接收struct list_head类型的参数即可,不失通用性。

在链表这种结构中最好玩的地方在于对于这种形式的链表的遍历,Linux中定义的以下的宏:

#define __list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

这个宏其实没什么神奇的地方,很典型的对于循环链表的迭代,但对于Linux内核中的链表,迭代的指针只能是一个struct list_head类型,对于这个类型来说我们只能对它做一些移动或者添加的操作,并不能取出该list_head对应的节点。这个迭代过程对于节点的删除操作其实是不安全的,假设我们在迭代中移除了pos节点,则进入下一次迭代时,pos = pos->next这个就不知会指向哪里去了,Linux中也定义了对于删除安全的迭代宏:

#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)

这个宏中使用了n这个struct list_head类型的临时变量,每次迭代之后pos的下一个节点储存在临时变量n中,则在迭代中删除pos节点后,下一次迭代会重新给pos赋值为临时变量n,然后再做迭代。这样在迭代的过程中就可以安全地删除节点pos了。

刚才说过这...

分类: Kernel 标签: