Chrome源码剖析精编版

Chrome源码剖析精编版
Chrome源码剖析精编版

另有”GoogleChrome浏览器架构解析”

Chrome源码剖析【一】

2009-10-16 15:17

【序】

开源是口好东西,它让这个充斥着大量工业垃圾代码和教材玩具代码的行业,多了一些艺术气息和美的潜质。它使得每个人,无论你来自米国纽约还是中国铁岭,都有机会站在巨人的肩膀上,如果不能,至少也可以抱一把大腿。。。

现在我就是来抱大腿的,这条粗腿隶属于 Chrome(开源项目名称其实是Chromium,本来Chrome这个名字就够晦涩了,没想到它的本名还更上一层楼...),Google 那充满狼子野心的浏览器。每一个含着金勺子出生的人都免不了被仰慕并被唾骂,Chrome也不例外。关于Chrome的优劣好坏讨论的太多了,基本已经被嚼成甘蔗渣了,没有人愿意再多张一口了。俗话说,内行看门道外行看热闹,大部分所谓的外行,是通过使用的真实感受来评定优劣的,这无疑是最好的方式。但偏偏还是有自诩的内行,喜欢说内行话办外行事,一看到Chrome用到多进程就说垃圾废物肯定低能。拜托,大家都是搞技术的,你知道多进程的缺点,Google

也知道,他们不是政客,除了搞个噱头扯个蛋就一无所知了,人家也是有脸有皮的,写一坨屎一样的开源代码放出来遭世人耻笑难道会很开心?所谓技术的优劣,是不能一概而论的,同样的技术在不同场合不同环境不同代码实现下,效果是有所不同的。既然Chrome用了很多看上去不是很美的技术,我们是不是也需要了解一下它为什么要用,怎么用的,然后再开口说话?(恕不邀请,请自行对号入座...)。。。

人说是骡子是马拉出来遛遛,Google已经把 Chrome这匹驴子拉到了世人面前,大家可以随意的遛。我们一直自诩是搞科学的,就是在努力和所谓的艺术家拉开,人搞超女评委的,可以随意塞着屁眼用嘴放屁,楞把李天王说是李天后,你也只能说他是艺术品位独特。你要搞科学就不行,说的不对,轻的叫无知,重的叫学术欺诈,结果一片惨淡。所以,既然代码都有了,再说话,就只能当点心注点意了,先看,再说。。。

我已经开始遛Chrome这头驴了,确切一点,是头壮硕的肥驴,项目总大小接近2G。这样的庞然大物要从头到脚每个毛孔的大量一遍,那估计不咽气也要吐血的,咱又不是做Code review,不需要如此拼命。每一个好的开源项目,都像是一个美女,这世界没有十全十美的美女,自然也不会有样样杰出的开源项目。每个美女都有那么一两点让你最心动不已或者倍感神秘的,你会把大部分的注意力都放在上面细细品味,看开源,也是一样。Chrome对我来说,有吸引力的地方在于(排名分先后...):

1. 它是如何利用多进程(其实也会有多线程一起)做并发的,又是如何解决多进程间的一些问题的,比如进程间通信,进程的开销;

2. 做为一个后来者,它的扩展能力如何,如何去权衡对原有插件的兼容,提供怎么样的一个插件模型;

3. 它的整体框架是怎样,有没有很NB的架构思想;

4. 它如何实现跨平台的UI控件系统;

5. 传说中的V8,为啥那么快。

但Chrome是一个跨平台的浏览器,其Linux和Mac版本正在开发过程中,所以我把所有的眼光都放在了windows版本中,所有的代码剖析都是基于windows版本的。话说,我本是浏览器新手、win api白痴以及并发处理的火星人,为了我的好奇投身到这个溜驴的行业中来,难免有学的不到位看的走眼的时候,各位看官手下超生,有错误请指正,实在看不下去,回家自己牵着遛吧。。。

扯淡实在是个体力活,所以后面我会少扯淡多说问题。。。

图1 Chrome的线程和进程模型

1. Chrome的线程模型

仔细回忆一下我们大部分时候是怎么来用线程的,在我足够贫瘠的多线程经历中,往往都是这样用的:起一个线程,传入一个特定的入口函数,看一下这个函数是否是有副作用的(Side Effect),如果有,并且还会涉及到多线程的数据访问,仔细排查,在可疑地点上锁伺候。。。

Chrome的线程模型走的是另一个路子,即,极力规避锁的存在。换更精确的描述方式来说,Chrome的线程模型,将锁限制了极小的范围内(仅仅在将Task放入消息队列的时候才存在...),并且使得上层完全不需要关心锁的问题(当然,前提是遵循它的编程模型,将函数用Task封装并发送到合适的线程去执行...),大大简化了开发的逻辑。。。

不过,从实现来说,Chrome的线程模型并没有什么神秘的地方(美女嘛,都是穿衣服比不穿衣服更有盼头...),它用到了消息循环的手段。每一个Chrome的线程,入口函数都差不多,都是启动一个消息循环(参见MessagePump类),等待并执行任务。而其中,唯一的差别在于,根据线程处理事务类别的不同,所起的消息循环有所不同。比如处理进程间通信的线程(注意,在Chrome中,这类线程都叫做IO线程,估计是当初设计的时候谁的脑门子拍错了...)启用的是MessagePumpForIO类,处理UI的线程用的是MessagePumpForUI类,一般的线程用到的是MessagePumpDefault类(只讨论windows, windows, windows...)。不同的消息循环类,主要差异有两个,一是消息循环中需要处理什么样的消息和任务,第二个是循环流程(比如是死循环还是阻塞在某信号量上...)。下图是

一个完整版的Chrome消息循环图,包含处理Windows的消息,处理各种Task(Task是什么,稍后揭晓,敬请期待...),处理各个信号量观察者(Watcher),然后阻塞在某个信号量上等待唤醒。。。

图2 Chrome的消息循环

当然,不是每一个消息循环类都需要跑那么一大圈的,有些线程,它不会涉及到那么多的事情和逻辑,白白浪费体力和时间,实在是不可饶恕的。因此,在实现中,不同的MessagePump类,实现是有所不同的,详见下表:

MessagePumpDefault MessagePumpForIO MessagePumpForUI

是否需要处理系统消息否是是

是否需要处理Task是是是

是否需要处理Watcher否是否

是否阻塞在信号量上否是是

2. Chrome中的Task

从上面的表不难看出,不论是哪一种消息循环,必须处理的,就是Task(暂且遗忘掉系统消息的处理和Watcher,以后,我们会缅怀它们的...)。刨去其它东西的干扰,只留下Task的话,我们可以这样认为:Chrome中的线程从实现层面来看没有任何区别,它的区别只存在于职责层面,不同职责的线程,会处理不同的Task。最后,在铺天盖地西红柿来临之前,我说一下啥是Task。。。

简单的看,Task就是一个类,一个包含了 void Run()抽象方法的类(参见Task类...)。一个真实的任务,可以派生Task类,并实现其Run方法。每个MessagePump 类中,会有一个 MessagePump::Delegate的类的对象(MessagePump::Delegate的一个实现,请参见MessageLoop 类...),在这个对象中,会维护若干个Task 的队列。当你期望,你的一个逻辑在某个线程内执行的时候,你可以派生一个Task,把你的逻辑封装在 Run方法中,然后实例一个对象,调用期望线程中的PostTask 方法,将该Task对象放入到其Task队列中去,等待执行。我知道很多人已经抄起了板砖,因为这种手法实在是太常见了,就不是一个简单的依赖倒置,在线程池,Undo\Redo等模块的实现中,用的太多了。。。

但,我想说的是,虽说谁家过年都是吃顿饺子,这饺子好不好吃还是得看手艺,不能一概而论。在Chrome中,线程模型是统一且唯一的,这就相当于有了一套标准,它需要满足在各个线程上执行的几十上百种任务的需求,因此,必须在灵活行和易用性上有良好的表现,这就是设计标准的难度。为了满足这些需求,Chrome 在底层库上做了足够的功夫:

1.它提供了一大套的模板封装(参见task.h),可以将Task摆脱继承结构、函数名、函数参数等限制(就是基于模板的伪function实现,想要更深入了

解,建议直接看鼻祖《Modern C++》和它的Loki库...);

2.同时派生出CancelableTask、ReleaseTask、DeleteTask等子类,提供更为良好的默认实现;

3.在消息循环中,按逻辑的不同,将Task又分成即时处理的Task、延时处理的Task、Idle时处理的Task,满足不同场景的需求;

4.Task派生自tracked_objects::Tracked,Tracked是为了实现多线程环境下的日志记录、统计等功能,使得Task天生就有良好的可调试性和可统计性;这一套七荤八素的都搭建完,这才算是一个完整的Task模型,由此可知,这饺子,做的还是很费功夫的。。。

3. Chrome的多线程模型

工欲善其事,必先利其器。Chrome之所以费了老鼻子劲去磨底层框架这把刀,就是为了面对多线程这坨怪兽的时候杀的更顺畅一些。在Chrome的多线程模型下,加锁这个事情只发生在将Task放入某线程的任务队列中,其他对任何数据的操作都不需要加锁。当然,天下没有免费的午餐,为了合理传递Task,你需要了解每一个数据对象所管辖的线程,不过这个事情,与纷繁的加锁相比,真是小儿科了不知道多少倍。。。

图3 Task的执行模型

图4 Chrome的一种异步执行的解决方案

4. Chrome多线程模型的优缺点

一直在说Chrome在规避锁的问题,那到底锁是哪里不好,犯了何等滔天罪责,落得如此人见人嫌恨不得先杀而后快的境地。《代码之美》的第二十四章“美丽

Chrome源码剖析【二】

2009-10-16 15:12

【二】Chrome的进程间通信

1. Chrome进程通信的基本模式

进程间通信,叫做IPC(Inter-Process Communication),在Chrome不多的文档中,有一篇就是介绍这个的,在这里。Chrome最主要有三类进程,一类是Browser 主进程,我们一直尊称它老人家为老大;还有一类是各个Render进程,前面也提过了;另外还有一类一直没说过,是Plugin进程,每一个插件,在Chrome中都是以进程的形式呈现,等到后面说插件的时候再提罢了。Render进程和Plugin进程都与老大保持进程间的通信,Render进程与Plugin进程之间也有彼此联系的通路,唯独是多个Render进程或多个Plugin进程直接,没有互相联系的途径,全靠老大协调。。。

进程与进程间通信,需要仰仗操作系统的特性,能玩的花着实不多,在Chrome中,用到的就是有名管道(Named Pipe),只不过,它用一个IPC::Channel类,封装了具体的实现细节。Channel可以有两种工作模式,一种是Client,一种是 Server,Server和Client分属两个进程,维系一个共同的管道名,Server负责创建该管道,Client会尝试连接该管道,然后双发往各自管道缓冲区中读写数据(在Chrome中,用的是二进制流,异步IO...),完成通信。。。

Channel中,有三个比较关键的角色,一个是Message::Sender,一个是Channel::Listener,最后一个是MessageLoopForIO::Watcher。 Channel本身派生自Sender和Watcher,身兼两角,而Listener是一个抽象类,具体由Channel的使用者来实现。顾名思义,Sender就是发送消息的接口,Listener就是处理接收到消息的具体实现,但这个Watcher是啥?如果你觉得Watcher这东西看上去很眼熟的话,我会激动的热泪盈眶的,没错,在前面(第一部分第一小节...)说消息循环的时候,从那个表中可以看到,IO线程(记住,在Chrome 中,IO指的是网络IO,*_*)的循环会处理注册了的Watcher。其实Watcher很简单,可以视为一个信号量和一个带有 OnObjectSignaled方法对象的对,当消息循环检测到信号量开启,它就会调用相应的OnObjectSignaled方法。。。

图5 Chrome的IPC处理流程图

一图解千语,如上图所示,整个Chrome最核心的IPC流程都在图上了,期间,刨去了一些错误处理等逻辑,如果想看原汁原味的,可以自查Channel类的实现。当有消息被Send到一个发送进程的 Channel的时候,Channel会把它放在发送消息队列中,如果此时还正在发送以前的消息(发送端被阻塞...),则看一下阻

Chrome源码剖析【三】

2009-10-16 15:11

【三】 Chrome的进程模型

1. 基本的进程结构

Chrome是一个多进程的架构,不过所有的进程都会由老大,Browser进程来管理,走的是集中化管理的路子。在Browser进程中,有xxxProcessHost,每一个host,都对应着一个Process,比如RenderProcessHost对应着RenderProcess,PluginProcessHost对应着PluginProcess,有多少个host的实例,就有多少个进程在运行。。。

这是一个比较典型的代理模式,Browser对Host的操作,都会被Host封装成IPC消息,传递给对应的Process来处理,对于大部分上层的类,也就隔离了多进程细节。。。

2. Render进程

先不扯Plugin的进程,只考虑Render 进程。前面说了,一个Process一个tab,只是广告用语,实际上,每一个web页面内容(包括在tab中的和在弹出窗口中的...),在 Chrome中,用RenderView表示一个web页面,每一个RenderView可以寄宿在任一一个RenderProcess中,它只是依托 RenderProcess帮助它进行通信。每一个RenderProcess进程都可以有1到N个RenderView实例。。。

Chrome支持不同的进程模型,可以一个tab一个进程,一个site instance一个进程等等。但基本模式都是一致的,当需要创建一个新的RenderView的时候,Chrome会尝试进行选择或者是创建进程。比如,在one site one process的模式下,如果存在此site,就会选择一个已有的RenderProcessHost,让它管理这个新的RenderView,否则,会创建一个RenderProcessHost(同时也就创建了一个Process),把RenderView交给它。。。

在默认的one site instance one process的模式中,Chrome会为每个新的site instance创建一个进程(从一个页面链开来的页面,属于同一个site instance),但,Render进程总数是有个上限的。这个上限,根据内存大小的不同而异,比如,在我的机器上(2G内存),最多可以容纳20个 Render进程,当达到这个上限后,你再开新的网站,Chrome会随机为你选择一个已有的进程,把这个网站对应的RenderView给扔进去。。。

每一次你新输入一个站点信息,在默认模式下,都必然导致一个进程的诞生,很可能,伴随着另一个进程的死亡(如果这个进程没有其他承载的RenderView的话,他就自然死亡了,RenderView的个数,就相当于这个进程的引用计数...)。比如,你打开一个新标签页的时候,系统为你创造了一个进程来承载这个新标签页,你输入https://www.360docs.net/doc/d8656214.html,,于是新标签页进程死亡,承载 https://www.360docs.net/doc/d8656214.html,的进程诞生。你用baidu搜索了一下,毫无疑问,你基本对它的搜索结果很失望,

于是你重新输入 https://www.360docs.net/doc/d8656214.html,,老的承载baidu的进程死亡,承载google的进程被构建出来。这时候你想回退到之前baidu的搜索结果,乐呵乐呵的话,一个新的承载baidu的进程被创造,之前Google的进程死亡。同样,你再次点击前进,又来到Google搜索结果的时候,一个新的进程有取代老的进程出现了。。。以上现象,你都可以自己来检验,通过观察about:memory页面的信息,你可以了解整个过程(记得每做一步,需要刷新一下about:memory页面)。我唧唧歪歪说了半天,其实想表达的是,Chrome并没有像我YY的一样做啥进程池之类的特殊机制,而是简单的履行有就创建、没有就销毁的策略。我并不知道有没有啥很有效的多进程模型,这方面一点都没玩过,猜测Chrome之所以采取这样的策略,是经过琢磨的,觉得进程生死的代价可以承受,比较可行。。。

3. 进程开销控制算法

说开销无外乎两方面的内容,一为时间,二则空间。Chrome没有在进程创建和销毁上做功夫,但是当进程运行起来后,还是做了一些工作的。。。

节约工作首先从CPU耗时上做起,优先级越高的进程中的线程,越容易被调度,从而耗费CPU时间,于是,当一个页面不再直接面对用户的时候,Chrome会将它的进程优先级切到Below Normal的级别,反之,则切回Normal级别。通过这个步骤,小节约了一把时间。。。

当然这只是一道开胃小菜,满汉全席是控制进程的工作集大小,以达到降低进程实际内存消耗的目的(Chrome为了体现它对内存的节约,用了“更为精确”的内存消耗计算方法...)。提到这一点,Chrome颇为自豪,在文档中,顺着道把单进程的模式鄙视了一下,基本意思是:在多进程的模式下,各个页面实际占用的内存数量,更容易被控制,而在单进程的模式下,几乎是不能作出控制的,所以,很多时候,多进程模式耗费的内存,是会小于多线程模式的。这个说法靠不靠谱,大家心里都有谱,就不多说了。。。

具体说来,Chrome对进程工作集的控制算法还是比较简单的。首先,在进程启动的时候,需要指明进程工作的内存环境,是高内存,低内存,还是中等内存,默认模式下,是中等内存(我以为Chrome会动态计算的,没想到竟然是启动时指定...)。在高内存模式,不存在对工作集的调整,使劲用就完事了;在低内存的模式下,调整也很简单,一旦一个进程不再有页面面对观众了,尝试释放其所有工作集。相比来说,中等模式下,算法相对复杂一些,当一个进程从直接面对观众,沦落到切换到后台的悲惨命运,其工作集会缩减,算法为:TargetWorkingSetSize = (LastWorkingSet/2 + CurrentWorkingSet) /2;其中,TargetWorkingSetSize指的是预期降到的工作集大小,CurrentWorkingSet指的是进程当前的工作集(在 Chrome中,工作集的大小,包含私有的和可共享的两部分内存,而不包含已经共享了的内存空间...),LastWorkingSet,等于上一次的 CurrentWorkingSet除以DampingFactor,默认的DampingFactor为2。而反之,当一个进程从幕后走向台前,它的工作集会被放大为 LastWorkingSet * DampingFactor * 2,了解过LastWorkingSet的含义,你已经知道,这就是将工作集放大两倍的另类版写法。。。

Chrome的Render进程工作集调整,除了发生在tab切换(或新页面建立)的时候,还会发生在整个Chrome的idle事件触发后。 Chrome有个计时器,统计Chrome

空闲的时长,当时长超过30s后(此工作会反复进行...),Chrome会做一系列工作,其中就包括,调整进程的工作集。被调整的进程,不仅仅是Render进程,还包括Plugin进程和Browser进程,换句话描述,就是所有Chrome进程。。。

这个算法导致一个很悲凉的状况,当你去蹲了个厕所回到电脑前,切换了一个Chrome页面,你发现页面一片惨白,一阵硬盘的骚动过后,好不容易恢复了原貌。如果再切,相同的事情又会发生,孜孜不倦,直到你切过每一个进程。这个惨案发生的主要原因,就是由于所有Chrome进程的工作集都被释放了,页面的重载和Render需要不少的一坨时间,这就大大影响了用户感受,毕竟,总看到惨白的画面,容易产生不好的情绪。强烈感觉这个不算一个很出色的策略,应该有一个工作集切换的底限,或者是在Chrome从idle中被激活的时候,偷偷摸摸的统一扩大工作集,发几个事件刺激一下,把该加载的东西加载起来。。。

整体感觉,Chrome对进程开销的控制,并不像想象中的有非常精妙绝伦的策略在里面,通过工作集这总手段并不算华丽,而且,如果想很好的工作的话,有一个非常非常重要的前提,就是被切换的页面,很少再被继续浏览。个人觉得这个假设并不是十分可靠,这就使得在某些情况下,产生非常不好的用户体验,也许Chrome需要进一步在这个地方琢磨点方法的。。。

Chrome源码剖析【四】

2009-10-16 15:11

【四】Chrome的UI绘制

1. Chrome的窗口控件

Chrome提供了自己的一个UI控件库,相关文档可以参见这里。用Chrome自己的话来说,我觉得市面上的七荤八素的图形控件库都不好用,于是自己倒腾倒腾实现了一套。。。

广告虽如此说,不过,Chrome的图形控件结构,我还未发现有啥非常非常特别的地方。Chrome的窗口、按钮、菜单之类的控件,都直接或间接派生自View,这个是控件基类。Chrome的View具有树形结构,其内部有一个子View数组,由此构成一个控件常用的组合模式。。。

有一个比较特殊的View子类,叫做RootView,顾名思义,它是整个View控件树的根,在Chrome中,一个正确的树形的控件结构,必须由RootView作为根。之所以要这样设计,是因为RootView有一个比较特殊的功能,那就是分发消息。。。

我们知道,一般的Windows控件,都有一个HWND,用与占据一块屏幕,捕获系统消息。Chrome中的View只是保存控件相关信息和绘制控件,里面没有HWND句柄,因此不能够捕获系统消息。在Chrome中,完整的控件架构是这样的,首先需要有一个ViewContainer,它里面包含一个RootView。ViewContainer是一个抽象类,在Window中的一个子类是HWNDViewContainer,同时,HWNDViewContainer还是MessageLoopForUI::Observer的子类。如果你看过本文第一部分描述的线程通信的内容的话,你就应该还记得,Observer是用于监听本线程内系统消息的东东。。。

当有系统消息进入此线程消息循环后,HWNDViewContainer会监听到这个情况,如果和View相关的消息,它就会调用RootView的相关方法,传递给控件。在RootView的内部,会遍历整个控件树上的控件,将消息传递给各个控件。当然,有的消息是可以独占的,比如鼠标移动发送在某个View所管辖的范围内,它会告知RootView(通过方法的返回值...),这个消息我要了,那么RootView会停止遍历。。。

在设计的时候,View对消息的处理,采取的是大而全的接口模式。就是说在View内部,提供了所有可能的消息处理接口,并提供了默认实现,所有子类只需要覆盖自己需要的消息处理函数即可。如果对MFC的消息映射有了解的话,可以知道两者的区别。MFC在设计的时候,觉得无法提供大而全的接口,因为消息总类实在太多,而且还是可扩展的,于是就有了消息映射着一套繁琐的宏。但Chrome的图形框架,显然没有做一个通用的Framework的打算,因此,可以采用这样的策略,使得子类的派生变得简单而自然。。。

每一个View的子类控件,比如Button之类的,会存储一些数据,根据消息做一些行为,并且绘制出自己。在Chrome中,画图的东西是ChromeCanvas这个类,在其内部,通过Skia和GDI实现绘制。 Skia是Android团队开发的一个跨平台的图形引擎,在Chrome中负责除了文字之外,所有内容的绘制;而文字绘制的重担,在Windows中交到了GDI的手上。这样的设计会给跨平台带来一些困难,估计是由Skia实现文本绘制会比较繁琐,才会带出如此一个设计的模式。。。另外一个历史遗留产物,就是在Windows下的图形控件,还有一些是原生的,就是说带有HWND那种传统的控件,这是Chrome身上不多的赶工期的痕迹,随着时间的宽裕,这样的原生控件会被淘汰进历史的垃圾箱,而全部变为从View派生的控件。。。

其实,对于Chrome这套控件架构我还没算摸得很熟悉,估计等到做一次插件之后会了解的更透彻,因此,只说了点皮毛,聊表心意。。。

2. Chrome的页面加载和绘制

上面这些UI控件,都是用在窗口上的(比如浏览器的外框,菜单,对话框之类的...)。我们在浏览器中看到的大部分内容,是网页页面。页面的绘制(绘制,就是把一个HTML文件变成一个活灵活现的页面展示的过程...),只有一半轮子是Chrome自己做的,还有一部分来自于WebKit,这个Apple打造的Web渲染器。。。之所以说是一半轮子来源于WebKit,是因为WebKit本身包含两部分主要内容,一部分是做Html渲染的,另一部分是做JavaScript解析的。在Chrome中,只有Html的渲染采用了WebKit的代码,而在JavaScript上,重新搭建了一个NB哄哄的V8引擎。目标是,用WebKit + V8的强强联手,打造一款上网冲浪的法拉利,从效果来看,还着实做的不错。。。

不过,虽说Chrome和WebKit都是开源的,并联手工作。但是,Chrome还是刻意的和WebKit保持了距离,为其始乱终弃埋下了伏笔。Chrome在WebKit上封装了一层,称为 WebKit Glue。Glue层中,大部分类型的结构和接口都和WebKit类似,Chrome中依托WebKit的组件,都只是调用WebKit Glue层的接口,而不是直接调用WebKit中的类型。按照Chrome自己文档中的话来说,就是,虽然我们再用WebKit实现页面的渲染,但通过这个设计(加一个间接层...)已经从某种程度大大降低了与WebKit的耦合,使得可以很容易将WebKit换成某个未来可能出现的更好的渲染引擎。。。

当你键入一个Url并敲下回车后,Chrome会在Browser进程中下载Url对应的页面资源(包括Web页面和Cookie),而不是直接将Url发送给Render进程让它们自行下载(你会越来越发现,Render进程绝对是100%的名符其实,除了绘制,几乎啥多余的事情都不会干的...)。与各个Render进程各自为站,各自管好自己所需的资源相比,这种策略仿佛会增加大量的进程间通信。之所以采用,按照这篇文档的解释,主要有三个优点,一个是避免子进程与网络通信,从而将网络通信的权限牢牢握在主进程手中,Render进程能力弱了,想造反干坏事的可能性就降低了(可以更好控制各个Render进程的权限...);另一个是有利于Cookie等持久化资源在不同页面中的共享,否则在不同Render进程中传递 Cookie这样的事情,做起来更麻烦;还有一点很重要的,是可以控制与网络建立HTTP连接的数量,以Browser为代表与网络各方进行通信,各种优化策略都比较好开展(比如池化)。。。

当然,在Browser进程中进行统一的资源管理,也就意味着不再方便用WebKit进行资源下载(WebKit当然有此能力,不过再次被Chrome抛弃了...),而是依托WinHTTP来做的。WinHTTP在接受数据的过程中,会不停的把数据和相关的消息通过IPC,发送给负责绘制此页面的Render进程中对应的RenderView。在这里,路由消息中的那个ID值起了关键的作用,系统依照此ID,能够准确的将相关的消息发送到相关的View头上,这玩意发错了地方还真不是和有人把钱错到你账户上一样,因为错收的进程基本上无福消受这个意外来客,轻者页面显示混乱,重者消化不良直接噎死。。。

RenderView接收到页面信息,会一边绘制一边等待更多的资源到来,在用户看来,所请求的页面正在一点一点显示出来。当然,如果是一个通知传输开始、传输结束这样的消息,通过序列化到消息参数里面,经由IPC发过来,代价还是可以承受的,但是,想资源内容这样大段大段的字节流,如果通过消息发过来,浪费两边进程大量空间和时间,就不合适了。于是这里用到了共享内存。Browser进程将下载到的资源写到共享内存中,并将共享内存的句柄和共享区域的大小序列化在消息中发送给Render进程。Render进程拿到这个句柄,就可以通过它访问到共享内存相关的区域,读取信息并进行绘制。通过这样的方式,即享用到了统一资源管理的优点,由避免了很高的进程通信开销,左右逢源,好不快活。。。

3. Chrome页面的消息响应

Render进程是一个娇生惯养的进程,这一点从上面一段已经可以看出来了。它自己的资源它自己都不下载,而是由Browser进程来帮忙。不过Render进程也许比你想象的还要懒惰一些,它不但不自己下载资源,甚至,连自己的系统消息都不接收。。。

Render进程中不包含HWND,当你鼠标在页面上划来划去,点上点下,这些消息其实都发到了Browser进程,它们拥有页面呈现部分的HWND。Browser会将这些消息转手通过IPC发送给对应的Render进程中的RenderView,很多时候WebKit会处理此类消息,当它发现出现了某种值得告诉Browser进程的事情,它会组个报回赠给Browser进程。举个例子,你打开一个页面,然后拿鼠标在页面上乱晃。Browser这时候就像一个碎嘴大婶,不厌其烦的告诉Render进程,“鼠标动了,鼠标动了”。如果Render对这个信息无所谓,就会很无聊的应答着:“哦,哦”(发送一个回包...)。但是,当鼠标划过链接的时候,矜持的Render 进程坐不住了,会大声告诉Browser进程:“换鼠标,换鼠标~~”,Browser听到后,会将鼠标从箭头状换成手指状,然后继续以上过程。。。

比较麻烦的是Paint消息,重新绘制页面是一个太频繁发生的事情,不可能重绘一次就序列化一坨字节流过去。于是策略也很清楚了,就是依然用共享内存读写,用消息发句柄。在Render进程中,会有一个共享内存池(默认值为2...),以size为key,以共享内存为值,简单的先入先出淘汰算法,利用局部性的

特征,避免反复的创建和销毁共享内存(这和资源传递不一样,因为资源传递可以开一块固定大小的共享内存...)。Render进程从共享内存池中拿起一块(二维字节数组...),就好像拿着一块屏幕似的,拼了命往上绘制,为了让Render安心觉着有成就感,Browser会偷偷帮Render把这些内容绘制到屏幕上,造成Render进程直接绘制屏幕的假象。这可就苦了屏幕取词的工具们,因为在HWND上压根就没啥字符信息,全部就是一坨图像而已,啥也取不着。于是Google金山词霸,网易有道词霸各自发挥智慧,另辟蹊径,也算是都利用Chrome做了一把广告。。。

为什么不让Render进程自己拥有HWND,自己管理自己的消息,既快捷又便利。在Chrome的官方Blog上,有一篇解释的文章,基本上是这个意思,速度是必须快的发指的,但是为了用户响应,放弃一些速度是必要的,毕竟,没有人喜欢总假死的浏览器。在Browser进程中,基本上是杜绝任何同步Render进程的工作,所有操作都是异步完成。因为Render进程是不靠谱的,随时可能牺牲掉,同步它们往往导致主进程停止响应,从而导致整个浏览器停下来甚至挂掉,这个代价是不可以容忍的。但是,Windows有一个恶习,喜欢往整个HWND继承体系中发送同步消息(我不是很清楚这个状况,有人能解释么?...),这时候,如果HWND 在Render进程中,就务必会导致主进程与Render进程的同步,Chrome无法控制 Windows,于是,它们只能够控制Render,把它们的HWND搬到主进程中,避免同步操作,换取用户响应的速度。。。

4. 结论

整个Chrome的UI架构,就是一个权责分配的问题。可以把Browser进程看成是一个类似于朱元璋般的勤劳皇帝(详见《明朝那些事一》...),把大多数的权利都牢牢把握在手中,这样,虽然Browser很操劳,但是整体上的协调和同步,都进行的非常顺畅。Render进程就是皇帝手下的傀儡宰相们,只负责自己的一亩三分地,听从皇帝的调配即可。这这样的环境下,Render进程的生死变得无足轻重,Render的死亡,只是少了一个绘制页面的工具而已,其他一切如故。通过控制权力,换取天下太平,这招在coding界,同样是一个不错的策略,但是,唯一的意外来自于Plugin。按照规范,Chrome的Plugin是可以创立窗口的(HWND),这必然导致同步问题,Chrome没有办法通过控制权力的方式解决这个问题,只能想些别的亡羊补牢的招来搞定。。。

Chrome源码剖析【五】

2009-10-16 15:10

【五】 Chrome的插件模型

1. NPAPI

为了紧密的与各个开源浏览器团结起来,共同抗击IE的垄断,Chrome的插件,也遵循了NPAPI(Netscape Plugin Application Programming Interface)标准,支持这个标准的浏览器需要实现一组规定的API供插件调用,这组API形如NPN_XXX,比如NPN_GetURL,插件可以利用这些API进行二次开发。而NPAPI插件以一个Dll之类的作为物理载体(windows下dll,linux下是so...)进行提供,里面同样也实现了一组规定的API。形式包括NP_XXX和NPP_XXX,NP_XXX是系统需要默认调用的方法,用于认知这个插件,比如NP_Initialize,而NPP_XXX是用于插件完成一些实际功能,比如NPP_New。。。

所有的插件dll都需要放置在指定目录下(根据操作系统的不同而不同...),每个插件可以处理一种或多种MIME格式的数据,比如application/pdf,说明该插件可以处理pdf相关的文档。在Chrome中键入about:plugins,可以查看当前Chrome中具有的插件信息。。。

NPAPI是一个很经典的插件方案,用dll进行注入,用协定的API进行通信,用字符串描述插件能力。插件宿主(在这里就是浏览器...),会根据能力描述,动态加载插件,并负责插件调用的流程和生命周期管理。而插件中,负责真实逻辑的处理,并可以构造 UI与用户交流。以此类方式实现的插件系统,往往是处理的逻辑比较固定适用范围一般(用API写死了逻辑...),但可扩展性不错(用字符串描述能力,可无限扩展...)。。。

在Chrome中nphostapi.h中,定义了所有NPAPI相关的函数指针和结构,这个文件放置在glue目录下,如果看过前面碰过的文章就知道,在WebKit内肯定也有一套相同的东西;在npapi.h/.cc中,提供了Chrome浏览器端的NPN_XXX系列函数的实现;每一个插件物理实例,用PluginLib类来表示,而每一个插件的逻辑实例,用PluginInstance类来表示。这个概念牵强附会的可以用windows中的句柄来类比,当你想操作一个内核对象,你需要获得一个内核对象的句柄,每个进程中的句柄肯定不相同,但后面的内核对象却是同一个,内核对象的生命周期通过句柄的计数来控制,有人用则或,无人用则死(当然这个类比相当的牵强,主要是想说明引用计数和逻辑与物理的关系,但一个关键性的区别在于,PluginLib与PluginInstance都是在一个进程内的,不能跨越进程边界...)。在Chrome中,PluginLib负责加载和销毁一个dll,拿到所有导出函数的函数指针,PluginInstance对这些东西进行了封装,可以更好的来调用。。。

关于NPAPI的更多细节,Chrome并没有提供任何文档,但是,各个先驱的浏览器们都提供了大量丰富的文档。比如,你可以到这里,查看firefox中的NPAPI

文档,基本通用。。。

2. Chrome的多进程插件模型

Chrome的插件模型,与早先的浏览器的最大不同,是它采用了多进程的方式,每一个插件,都有一个单独的进程来承载(Shift + Esc打开Chrome进程管理器,可以看到现在已经加载的插件进程...)。当WebKit进行页面渲染的时候,发现了未知的MIME类型数据,它会告知给Browser进程,召唤它提供一个插件来解析。如果该插件还未加载,Browser会在指定目录中搜寻出具有此实力的插件(如果没有此类人才只能作罢...),并为它创建一个进程,让它负责所有的该插件相关的任务,然后建立起一个IPC通路,与它“保持通话”。这套流程一定不会太陌生,因为它与 Render进程的创建大同小异换汤不换药。。。

Plugin进程与Render进程最大的区别在于,Render需要与Browser进程大量通信,因为它的HWND归Browser老大掌管着,相关所有内容都需要通信完成。但Plugin不需要与Browser频繁联系,它大部分的通信都是与Render进程发生的。如果Plugin与Render之间的通信,还需要走Browser中转一下,这就显得有些脱裤子放屁了,虽然Browser是大头,但不是冤大头,它不会干这种吃力不讨好的事情。他只是做了一回Render与Plugin间的媒婆而已。当Plugin与Browser 建立好了IPC通路后,它会让Render建立一个新IPC通路,用以与Plugin通信,IPC的有名管道名,经由Browser通知给Plugin。完成名字协商后,Render 与Plugin的通信关系就建立好了,它们之间就可以直接进行通信了。。。

整个通信模式,可以看这里。这是一个很标准的代理模式的应用,稍有了解的都可以跳过我后面会做的一段罗嗦的描述,一看官方文档中的图便能知晓。在Render 进程端,WebPluginImpl是WebPlugin的一个子类,WebPlugin是供Webkit进行调用的一个接口,利用依赖倒置,实现了扩展。在Plugin进程端,实现了一个WebPluginDelegateImpl类,该类会调用PluginInstance的相关接口实现真实的插件功能。这样的话,只需要WebPluginImpl调用 WebPluginDelegateImpl中的相应方法,就可以实现功能。但问题是WebPluginImpl与 WebPluginDelegateImpl天各一方各处于一个进程,很显然,这里需要一个代理模式。这里沿用了COM的架构,Delegate + Stub + Proxy。WebPluginImpl调用代理WebPluginDelegateProxy,该代理会将调用转换成消息,通过IPC发送给Plugin进程,在Plugin

端,通过WebPluginDelegateStub监听消息,并转换成对真实WebPluginDelegateImpl的调用,从而完成了跨进程的一个调用,反之亦然。。。

3. Chrome的可扩展性

总所周知,firefox通过三种方式进行自定义,插件、扩展和皮肤。其中,插件是使得浏览器能用,不会出现一大块一大块的无法显示的区域;扩展是使得浏览器好用,可以简单方便的进行功能的定制和个性化配置;皮肤是帮助浏览器变得好看,毕竟罗卜白菜,给有所爱。。。

与之对比,来看Chrome。Chrome有了插件,有了皮肤,但是没有扩展。这就意味着,你很难为Chrome定制一些特色的功能。目前,所有对Chrome的功能扩展,都是通过书签抑或是修改内核来实现的。前者能力太弱,后者开发起来太麻烦,容易出错不提,还必须要与时俱进,跟上版本的变化,并且还不能自由的选择或关闭。因此,这都不是长远之计,Chrome提供一套类似于firefox的扩展机制,也许才是正道。据传说,Chrome团队正在琢磨这件事,不知道最终会出来个怎么样的结果,是尽力接近firefox降低移植成本,还是另立门户特立独行,我想可以拭目以待一把。。。

在多进程模式下,Chrome的插件还有一个问题,前面提到过,就是关于UI控件的。由于NPAPI的标准,是允许插件创建HWND窗口的,这就使得当Plugin繁忙,且Browser进程发起 HWND的同步的时候,主进程被挂起,这个浏览器停滞。在Render进程中,解决这个问题的思路是控制权限,不然Render创建HWND,到了Plugin中,这招不能使用,只能够使用另一招,就是监管。不停的检查Plugin是否太繁忙,无法响应,一旦发现,立即杀死该Plugin及其所处的页面。这就好比你想解决奶中有三氯氰胺的问题,要么控制奶源,不从奶站购买全部用自家的,要么加强监管,提高检查力度防止隐患。两种策略的优缺点一眼便知,依照不同环境采取不同策略即可。。。

总体说来,Chrome的可扩展性着实一般,不过Chrome还处于Beta中,我们可以继续期待。。。

侯捷stl源码剖析注释之42 sgi-stl-slist

完整列表 The Annotated STL Sources 1 G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_slist.h 完整列表 /* * Copyright (c) 1997 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * */ /* NOTE: This is an internal header file, included by other STL headers. * You should not attempt to use it directly. */ #ifndef __SGI_STL_INTERNAL_SLIST_H #define __SGI_STL_INTERNAL_SLIST_H __STL_BEGIN_NAMESPACE #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) #pragma set woff 1174 #endif // 單向串列的節點基本結構 struct __slist_node_base { __slist_node_base* next ; }; // 全域函式:已知某一節點,安插新節點於其後。 inline __slist_node_base* __slist_make_link (__slist_node_base* prev_node, __slist_node_base* new_node) { // 令 new 節點的下一節點為prev 節點的下一節點 new_node->next = prev_node->next; prev_node->next = new_node; // 令 prev 節點的下一節點指向new 節點 return new_node; } // 全域函式:找出某一節點的前一個節點。 inline __slist_node_base* __slist_previous (__slist_node_base* head, const __slist_node_base* node) {

STL源码剖析总结_第二章-空间配置器

2.空间配置器 2.1具备次配置力(sub-allocation)的SGI空间配置器 SGI含有两个空间配置器类,std::allocator内存分配类符合标准,但是仅仅是对operator new和operator delete简单封装一下而已;其次是SGI特殊的内存分配器std::alloc,其中实现采用了内存池,对于分配大量小容量的对象,可以大大减少内存碎片。 SGI标准的空间配置器std::allocator 这是对应的模板内联内存分配函数。实现起来也很简单,注意这里分配的内存仅仅是一块没有使用的空间而已,在上面并没有构造对象,后面讲解如何在上面构造对象。 模板内联内存释放函数,直接调用全局的operator delete释放对应的内存。

SGI特殊的空间配置器Std::alloc class Foo{…} Foo* pf = new Foo;//配置内存,然后构造对象delete pf;//将对象析构,然后释放内存 new的算是包含两个阶段:

1)调用::operator new 配置内存 2)调用Foo::Foo()构造对象内容 Delete算式也包含两个阶段 1)调用Foo::~Foo()将对象析构 2)调用::operator delete释放内存 为了精密分工,STL allocator将两个阶段的操作分开来,内存配置操作由alloc::allocate()负责,内存释放操作由alloc::deallocate()负责;对象构造由::construct()负责,对象析构由::destroy()负责。

2.stl_alloc.h 内存空间的分配和释放 内部使用malloc在堆中申请内存,其中制造了一个内存池,可以减少小型区块过多而造成的内存碎片问题。 SGI设计了双层级配置器,第一级配置器直接使用malloc()和free(),第二级配置器则视情况采用不同的策略:当配置区块超过128bytes时,采用第一级配置器,当配置区块小于128bytes时,采用第二级配置器,采用复杂的memory pool。它内存池实际上就是内部维护了16个自由链表,预先已经分配好了,当需要从内存池中取内存时候,直接从对应链表取出即可;当释放内存到内存池时候,直接将内存插入链表即可。每个链表中节点分别占用8、16、24、32、40、48、52、64、72、80、88、96、104、112、120、128字节。

STL源码剖析总结_第八章配接器

8 配接器 8.1 配接器之概观与分类 1、设计模式中对配接器的定义如下:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes可以一起运作。 2、容器配接器(应用于容器) stack和queue是两个容器配接器,底层默认由deque构成。stack封住了所有的deque对外接口,只开放符合stack原则的几个函数;queue封住了所有的deque 对外接口,只开放符合queue原则的几个函数。 3、迭代器配接器(应用于迭代器) 3.1 insert iterators 可以把一般迭代器的复制操作转变为插入操作。 insert iterators包括back_insert_iterator(专门负责尾端插入),front_insert_iterator (专门负责头端插入)和insert_iterator(可以负责任何位置执行插入)。主要观念是,每个insert iterators内部都维护有一个容器;容器有自己的迭代器,当客户端对insert iterators做赋值操作时,就在insert iterators中转为对该容器的迭代器做插入操作,其他的迭代器功能则被关闭。 3.2 reverse iterators reverse iterators将迭代器的行进方向逆转,使原本应该前进的operator++变成了后退操作,原本后退的operator—操作变成了前进操作。当迭代器被逆转,虽然实体位置不变,但逻辑位置必须改变,主要是为了配合迭代器区间的“前闭后开“习惯。

3.3 IOStream iterators IOStream iterators可以将迭代器绑定到某个iostream对象身上。绑定一个istream object(例如:std::cin),称为istream_iterator,拥有输入功能。绑定到ostream object (例如:std::cout),称为ostream_iteratpr,拥有输出功能。内部维护一个istream member,客户端对这个迭代器做的operator++操作,会被导引调用内部所含的那个istream member的输入操作。绑定一个ostream object,就是在ostream iterator内部维护一个ostream member,客户端对这个迭代器做的operator=操作,会被导引调用内部所含的那个ostream member的输出操作。 3.4 运用实例

python数据分析过程示例

python数据分析过程示例

引言 几年后发生了。在使用SAS工作超过5年后,我决定走出自己的舒适区。作为一个数据科学家,我寻找其他有用的工具的旅程开始了!幸运的是,没过多久我就决定,Python作为我的开胃菜。 我总是有一个编写代码的倾向。这次我做的是我真正喜欢的。代码。原来,写代码是如此容易! 我一周内学会了Python基础。并且,从那时起,我不仅深度探索了这门语言,而且也帮助了许多人学习这门语言。Python是一种通用语言。但是,多年来,具有强大的社区支持,这一语言已经有了专门的数据分析和预测模型库。 由于Python缺乏数据科学的资源,我决定写这篇教程来帮助别人更快地学习Python。在本教程中,我们将讲授一点关于如何使用Python 进行数据分析的信息,咀嚼它,直到我们觉得舒适并可以自己去实践。

目录 1. 数据分析的Python基础 o为什么学Python用来数据分析o Python 2.7 v/s 3.4 o怎样安装Python o在Python上运行一些简单程序2. Python的库和数据结构 o Python的数据结构 o Python的迭代和条件结构

o Python库 3. 在Python中使用Pandas进行探索性分析 o序列和数据框的简介 o分析Vidhya数据集——贷款的预测问题 4. 在Python中使用Pandas进行数据再加工 5. 使用Python中建立预测模型 o逻辑回归 o决策树 o随机森林 让我们开始吧 1.数据分析的Python基础 为什么学Python用来数据分析 很多人都有兴趣选择Python作为数据分析语言。这一段时间以来,我有比较过SAS和R。这里有一些原因来支持学习Python:

如何成为一个程序员

如何成为一个程序员:想成为一个游戏程序员需要有以下资料 疯狂代码 https://www.360docs.net/doc/d8656214.html,/ ?: http:/https://www.360docs.net/doc/d8656214.html,/GameDevelopment/Article36086.html 、书籍: 算法和数据结构: 数据结构(C语言版)——严蔚敏、吴伟民 清华出版社 我觉得其配套习题集甚至比原书更有价值每个较难题都值得做下 Introduction to Algorithms第 2版 中文名算法导论 有关算法标准学习教材和工程参考手册在去年CSDN网站WebSite上其翻译版竟然评为年度 2十大技术畅销书同时员杂志上开设了“算法擂台”栏目这些溯源固本举动不由得使人对中国现今浮躁不堪所谓“IT”业又产生了线希望这本厚厚书幸亏打折我才买得起虽然厚达千页但其英文通俗晓畅内容深入浅出可见经典的作往往比般水准书还耐读还能找到MIT视频教程第节课那个老教授嘻皮笑脸后面就是长发助教上课了 C语言名题精选百则 窍门技巧篇——冼镜光 机械工业出版社 作者花费年时间搜集了各种常见C段极具窍门技巧性编程法其内容都是大有来头而且给出了详细参考资料如个普通Fibonacci数就给出了非递归解、快速算法、扩充算法等步步深入直至几无油水可榨对于视速度如生命连个普通浮点数转化为整数都另辟蹊径以减少CPU cycle游戏员怎可不看? 计算机算法基础(第 2版)—— 佘祥宣等 华中科大出版社 我看到几个学校研究生拿它作教材(研究生才开算法太开玩笑了吧)这本书薄是薄了点用作者话来说倒也“精辟

”其实此书是Fundamentals of Computer Algorithms缩写版不过原书出版太久了反正我是没找到 The Art of Computer ProgrammingVolume 1-3 作者Donald E. Knuth是我心目中和冯.诺依曼、Dijkstra、Shannon并列 4位大师这本书作者从读大学本科时开始写直写到博士时十年磨剑足见其下足了功夫可作为计算机技术核心——算法和数据结构终极参考手册创新处也颇多譬如常见Shell排序他在书中提出可用(3i-1)/2间隔这使其稍快于O(n1. 5)当然这套书描述高度数学化为此恐怕般人(我?)最好还得先看本数学预备书Concrete Mathematics(直译为混凝土数学?^-^)再说可惜是这套书才出到第 3卷并没有覆盖全部常见算法内容不过好在对于游戏员来说越常见算法用得越多这也不算是什么要命损失 STL源码剖析—— 侯捷 华中科大出版社 侯捷不用介绍了华人技术作家中旗舰说其有世界级水准也不为过这本书我以为是C和数据结构葵花宝典(欲练此功必先自宫)也就是说不下几层地狱很难看懂它要求预备知识太多了如STL、数据结构、泛型编程、内存管理都要很扎实(为此是不是还要看看有内存管理设计模式的称Small Memory Software这本书呢?)但是旦看懂真会是所向披靡 Data Structures for Game Programmers 每个数据结构例程都是个小游戏还用SDL库实现了个算法演示系统虽然内容失的于浅但起码让人了解了数据结构在游戏中作用 其实游戏并不比其它特殊甚至要求基本功更加扎实所以花时间做些看似和实际应用不甚相干习题对今后工作是大有裨益而且有些应用很广算法如常被人津津乐道[Page]A*算法及其变种牵涉到图检索周游和分枝-限界法恐怕还得读些艰深论文才能充分明白运用如Donald E. KnuthAn analysis of alpha-beta cutoffs其实还有不少此类

深入分析STL标准模板·vector

深入分析STL标准模板——vector ——计算机学院--马晶义 摘要:通过阅读c++的STL标准模板库中vector的源代码,分析STL 中的内部成员、接口、内存管理、封装等方面。Vector 是一种顺序性的容器,按照严格线性存储各种对象。它其实就是一种动态的数组,正如数组,vector有他们存储在存储单元相邻元素,这就意味着他们的元素可以被存取不只有使用迭代器还定期使用指针抵消元素。但是不像普通的数组,存储在向量自动处理,允许它的扩展和简约的需要。 关键字:STL、Vcector、内部成员函数、接口函数、封装 一、vector的声明及内部常用函数 1.vector的声明 vector c; 创建一个空的vector vector c1(c2); 创建一个vector c1,并用c2去初始化c1 vector c(n) ; 创建一个含有n个ElemType类型数据的vector; vector c(n,elem); 创建一个含有n个ElemType类型数据的vector,并全部初始化为elem; c.~vector(); 销毁所有数据,释放资源; 2.vector容器中常用的函数。(c为一个容器对象) c.push_back(elem); 在容器最后位置添加一个元素elem

c.pop_back(); 删除容器最后位置处的元素 c.at(index); 返回指定index位置处的元素 c.begin(); 返回指向容器最开始位置数据的指针 c.end(); 返回指向容器最后一个数据单元的指针+1 c.front(); 返回容器最开始单元数据的引用 c.back(); 返回容器最后一个数据的引用 c.max_size(); 返回容器的最大容量 c.size(); 返回当前容器中实际存放元素的个数 c.capacity(); 同c.size() c.resize(); 重新设置vector的容量 c.reserve(); 同c.resize() c.erase(p); 删除指针p指向位置的数据,返回下指向下一个数据位置的指针(迭代器) c.erase(begin,end) 删除begin,end区间的数据,返回指向下一个数 据位置的指针(迭代器) c.clear(); 清除所有数据 c.rbegin(); 将vector反转后的开始指针返回(其实就是原来的end-1) c.rend(); 将vector反转后的结束指针返回(其实就是原来的begin-1) c.empty(); 判断容器是否为空,若为空返回true,否则返回false

out......C语言

C++程序设计之四书五经(下篇) 荣耀/文我在上篇中“盘点”了TCPL和D&E以及入门教程、高效和健壮编程、模板和泛型编程等方面共十几本C++好书。冬去春来,让我们继续C++书籍精彩之旅。 标准库 当我还在研究院工作时,与同院另外两家研究所合作开发过一个大型水利枢纽调度集成项目。我们三家软件系统之间都要相互通信。在调试通讯模块时,细心的客户(一名好学的系统管理员)发现对于同一通信规约的解释代码,我的不超过30行,而对方的则超过了150行且很难看懂。这位系统管理员很纳闷,我说大家编程风格和习惯不一样,我使用了标准库,而他使用了传统C编程风格以及他所习惯的另外一些技术。 别误会!我绝无贬低这位合作伙伴的意思。事实上,我对那些真正有着深厚的C编程功力的程序员常常怀有钦佩之心。毕竟,C++能有今天的成功在很大程度上缘于它深深地植根于C。作为一名C++程序员,倘若不熟悉C++中的C,我往往会认为他的基本功是不扎实的,他的技术底气是不足的。 不过话又说回来,C++是一种多范型(paradigm)编程语言,具体采用哪种编程风格,专业程序员应该知道视具体情况而定。作为一名经常需要在现场做即兴开发的项目负责人,为了短平快地解决当务之急,我习惯尽量采用现有的库(和组件)。效率(以及强健性)久经验证的C++标准库已经摆在那儿了,何乐而不用呢? Nicolai M. Josuttis,《The C++ Standard Library: A Tutorial and Reference》原文版、中文版:《C++标准程序库:自修教程与参考手册》 这是一本百科全书式的C++标准库著作,是一本需要一再查阅的参考大全。它在完备性、细致性以及精确性方面都是无与伦比的。本书详细介绍了每一标准库组件的规格和用法,内

Disk-Based Container Objects

Disk-Based Container Objects Tom Nelson A container that's very large, or that must persist between programs, really needs to live on disk. C++ container class libraries have become a staple item in many programmers' toolkits. The introduction of templates has made these libraries notably robust and simple to use. However, their transient (memory-based) nature still imposes certain restrictions on their use. First, they cannot grow to arbitrary size if needed; second, they lack persistence, hence they disappear when the program shuts down. The second restriction is usually easier to cure than the first. To make a transient container "hold water," the container class could use a persistent streams facility to write the essential data members of each contained object to a disk file before shutting down. The next program invocation need only initialize another transient container, then sequentially extract each object from the persistent store and add it to the new container. In some cases, though, you can't guarantee that your run-time storage requirements won't overflow available memory. A priority or scheduling queue, for example, might need to process and store an unanticipated quantity of incoming data. You could write some objects to disk to free space in the container. However, to effectively process additions and deletions, a transient container normally requires the presence of all contained objects in memory at one time. When you write contained objects to a file, the logical structure of the container (pointers, state info, etc.) is lost. One solution consists of moving the entire container out to a disk file so that it becomes, in effect, a database, or "Containerized Database." This article will demonstrate techniques for building and using such disk-based container classes. They allow you to employ container objects of virtually any size regardless of memory constraints, which you can maintain indefinitely. Even when persistence is not necessary, the technique still permits arbitrary growth of the containers by storing overflow in temporary files, restricted only by available disk space. I will concentrate here on developing disk-based implementations for three fundamental structure types (lists, vectors, and binary trees). I discuss a few abstract types derived from them, and provide an example of a disk-based binary tree sort. Design and Performance Considerations

python语言经典基础级案例(含源代码)

python语言经典案例(基础级) 案例1: 题目:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。 程序分析:利用while 或for 语句,条件为输入的字符不为'\n'。 实例- 使用while 循环 #!/usr/bin/python # -*- coding: UTF-8 -*- import string s = raw_input('请输入一个字符串:\n') letters = 0 space = 0 digit = 0 others = 0 i=0 while i < len(s): c = s[i] i += 1 if c.isalpha(): letters += 1 elif c.isspace(): space += 1 elif c.isdigit(): digit += 1 else: others += 1 print'char = %d,space = %d,digit = %d,others = %d' % (letters, space,digit,others) 实例- 使用for 循环 #!/usr/bin/python

# -*- coding: UTF-8 -*- import string s = raw_input('请输入一个字符串:\n') letters = 0 space = 0 digit = 0 others = 0 for c in s: if c.isalpha(): letters += 1 elif c.isspace(): space += 1 elif c.isdigit(): digit += 1 else: others += 1 print'char = %d,space = %d,digit = %d,others = %d' % (letters, space,digit,others) 以上实例输出结果为: 案例2: 题目:一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高? 程序分析:无 程序源代码:

基于Python的Abaqus二次开发实例讲解

基于Python的Abaqus二次开发实例讲解 (asian58 2013.6.26) 基于Python的Abaqus的二次开发便捷之处在于: 1、所有的代码均可以先在Abaqus\CAE中操作一遍后再通过rp文件读取,然后再在此基础上进行相应的修改; 2、Python是一种解释性语言,读起来非常清晰,因此在修改程序的过程中,不存在程序难以理解的问题; 3、Python是一种通用性的、功能非常强大的面向对象编程语言,有许多成熟的类似于Matlab函数的程序在网络上流传,为后期进一步的数据处理提供了方便。 为了更加方便地完成Abaqus的二次开发,需进行一些相关约定: 1、所有参数化直接通过点的坐标值进行,直接对几何尺寸的参数化反而更加繁琐; 2、程序参数化已不允许在模型中添加太多的Tie,因此不同零部件的绑定直接通过共节点来进行,这就要求建模方法与常规的建模方法有所区别。思路如下: 将一个整机拆成几个大的Part来建立,一个Part中包含许多零件,这样在划分网格式时就可以自动实现共节点的绑定。不同的零件可通过建立不同的Set来进行区分,不同Part 的绑定可以通过Tie来实现。将一个复杂的结构拆成几个恰当的Part来建立,一方面可以将复杂的模型简单化,使建立复杂模型成为可能;另一方面,不同的Part可单独调用,从而又可实现程序的模块化,增加程序的适应范围,延长程序的使用寿命,也方便后期程序的维护和修改。 3、通过py文件建立起的模型要进行参数优化,已不适合采用Isight中Abaqus模块,需要用到Isight的Simcode模块。 下面详细解释一个臂架的py文件。 #此程序用来绘制臂架前段 #导入相关模块 # -*- coding: mbcs -*- from abaqus import * from abaqusConstants import * #定义整个臂架的长、宽、高 L0=14300 W0=1650 H0=800

STL源码剖析总结_第五章关联式容器

5 关联式容器 标准的STL关联式容器分为set(集合)和map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和multimap(多键映射表),这些容器的底层机制均以RB-tree(红黑树)完成,红黑树也是一个独立的容器,但并不开放给外界使用。 SGI STL还提供了一个不在标准规格之列的关联式容器:hash table(散列表),以及hash table为底层机制的hash_set(散列集合),hash_map(散列映射表),hash_multiset(散列多键集合),hash_multimap(散列多键映射表)所谓关联式容器:每笔数据(每个元素)都一个键值(key)和一个实值(value)。当元素插入到关联式容器时,容器内部结构(可能是RB-tree ,也可能是hash table)便依照其键值大小,以某种特定规则将这个元素置于适当位置。关联式容器没有所谓的头尾。 关联式容器的内部结构是一个平衡二叉树(balanced binary tree),以便获得良好的搜寻效率,平衡二叉树包括AVL-tree,RB-tree,AA-tree。 5.1 树导览 1)二叉搜索树BST(binary search tree) 二叉搜索树的放置规则是:任何节点的键值一定大于其左子树中的每一个节点的键值,并小于其右子树的每一个节点的键值。因此从根节点一直往左走,直至无左路可走,即得最小元素;从根节点一直往右走,直至无右可走,即得最大元素。

2)平衡二叉搜索树(balanced binary search tree) 平衡:没有任何一个节点过深(深度过大) 3)AVL-tree(Adelson-Velskii-Landis tree) AVL tree是一个“加上了额外平衡条件”的二叉搜索树。其平衡条件的建立是为了确保整棵树的深度为O(logN),AVL tree要求任何节点的左右子树的高度相差做多为1,这是一个较弱的条件,但仍能保证“对数深度”平衡状态。

STL源码剖析总结_第三章-迭代器与traits编程技法

3.迭代器(iterators)概念与traits编程技法 STL的中心思想在于:将数据容器(containers)和算法(algorithms)分开,彼此独立设计,最后在以一贴胶着剂将它们撮合在一起。 3.1 迭代器设计思维——STL关键所在 STL中心思想:将数据容器和算法分开,彼此独立设计,最后再以已贴粘着剂将他们撮合在一起 3.2 迭代器是一种smart pointer 迭代器是一种行为类似指针的对象,指针的最常见最重要的是内容提领(取出指标所指物体的内容dereference)和成员访问(member access)。所以,迭代器最重要的编程工作是对operator *和operator->进行重载(overloading)工作。 auto_ptr 是一个用来包装原生指针的对象,内存漏洞问题可以借此解决。用法如下,和原生指针一模一样。 3.3 迭代器相应类别(associated types) 最常用的相应类别有五种 value type:迭代器所指对象的型别。 difference type :表示两个迭代器之间的距离,可用于表示容器的最大容量。pointer type:代表迭代器所指对象的指针类型。简言之,operator->()的返回类型。reference type:代表迭代器所指对象的引用类型。简言之,operator*()的返回类型。分为两种,不允许改变“所指对象之内容”者称为constant iterators,例如const int* pic,允许改变“所指对象之内容”者称为mutable iterators,例如int* pi。*pic/*pi的型别constant T&/T&就是reference type。 iterator category type:提出5种迭代器的类型标识。

(完整版)Python100个小例子.docx

一、 Python 练习实例 1 题目:有四个数字: 1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位、十位、个位的数字都是 1、 2、 3、4。组成所有的排列后再去掉不满足条件的排列。 程序源代码: #!/usr/bin/python # -*- coding: UTF-8 -*- for i in range(1, 5): for j in range(1, 5): for k in range(1, 5): if (i != k) and (i != j) and (j != k): print(i, j, k) 二、 Python 练习实例 2 题目:企业发放的奖金根据利润提成。利润(I) 低于或等于10 万元时,奖金可提10%;利润高于 10 万元,低于 20 万元时,低于 10 万元的部分按 10%提成,高于 10 万元的部分,可提成 7.5%;20 万到 40 万之间时,高于 20 万元的部分,可提 成5%;40 万到 60 万之间时高于 40 万元的部分,可提成 3%;60 万到 100 万之间时,高于 60 万元的部分,可提成 1.5%,高于 100 万元时,超过 100 万元的部分按 1%提成,从键盘输入当月利润 I ,求应发放奖金总数? 程序分析:请利用数轴来分界,定位。注意定义时需把奖金定义成长整型。 程序源代码: #!/usr/bin/python # -*- coding: UTF-8 -*- i = int(input('净利润:')) arr = [1000000, 600000, 400000, 200000, 100000, 0] rat = [0.01, 0.015, 0.03, 0.05, 0.075, 0.1] r = 0 for idx in range(0, 6): if i > arr[idx]: r += (i - arr[idx]) * rat[idx] print((i - arr[idx]) * rat[idx]) i = arr[idx] print(r) 三、 Python 练习实例 3 题目:一个整数,它加上 100 后是一个完全平方数,再加上 168 又是一个完全平 方数,请问该数是多少? 程序分析: 假设该数为 x 。 1、则: x + 100 = n2, x + 100 + 168 = m2 2、计算等式: m2 - n2 = (m + n)(m - n) = 168

如何学习数据结构

如何学习数据结构 1、数据结构学习一定要自己独立完成代码实现,虽然有时候你理 解内容了,但是实现上面还是会愈要很多困难的,解决这些困难会帮助你提高程序设计的能力的。 2、数据结构是计算机专业最重要最基础的一门课,对于有过编程 经验的人,结合自己的编程体会,去领悟它的思想;对于初学者,捡一种自己最熟悉的语言去分析它,总之千万不要陷在语言的细节上,要高屋建瓴的去领会数据结构的思想。而且随着编程经历的丰富对它的体会越深入,最初接触是对一些思想可能只是生硬的记忆,随着学习的深入逐渐领悟了很多。对于实在弄不懂的东东,就先记住!!! 3、将各种数据结构算法烂熟于胸,这是一个优秀程序员的必须具 备的基本素质,是后来进步的基石。书上的例子自己看看,然后不看书自己想想做成代码,在以后使用的时候看看能不能用这些数据结构来解决问题。 4、自己试着把书上的数据结构尽量写成可复用的独立模板(模 块),以后用着方便,学得也深刻,以后复习不用看书了,反复温习即便自己的代码就行了,说实话,找工作面试的时候数据结构几乎是必问的! 5、我觉的学数据结构,应该从算法入手,不能急,我现在还在搞 数据结构呀!不过现在觉的不那么难了呀!因为主要是算法,一点一点理清,会有柳暗花明的时候的。

6、数据结构要反复看书,量变引起质变,可能一开始看不太懂, 单当看多了的时候,你会茅塞顿开! 7、我觉得数据结构要的是思想,学的也是思想,但你至少要熟练 一门语言,要么怎能检验你的思想是否正确,强烈推荐《STL 源码剖析》!!!结合STL中的源码去分析,STL是我看到的最全 的以数据结构为宗旨的一种库,还建议你去下一个STLPORT,之 中的源码比VC提供的好些,很全,基本上能够用到的数据结构 都涉及到了,并且在学这个库的过程当中还可以学习一些设计 模式,还可以学习VC中的范型运算思想,等等,开始行动吧!!! 8、怎样学习数据结构,最好方法是一起讨论。 9、1)如果你没有学过C语言,或者C语言学的不好的时候把数据 结构当成一本数学书来学,它所讲述的都是一些简单的图论。 在你的大脑中的主线不能丢失:线性结构,树结构和图结构。 当你不再考虑复杂的程序设计时,仅仅研究个个离散点之间的 关系,似乎数据结构也就不会那么难了。 2)学习好了抽象的 离散点关系后,再巩固一下你的C语言水平,书中描述的都是 类C。因此你只要学习简单的C定义、判断、循环语句就基本 能看的懂课本中所有程序了。3)以上都完成后,从数据结构的 线性表开始。线性表中顺序表,似乎是为你学习C语言设计的,学好线性表的链表是你起步的关键。后面的树结构,图结构, 排序,查找都少不了链式结构,往往这个也是最难的。 4)看 程序的时候一定要自己在纸上画画,最好先学会画程序的流程

侯捷stl源码剖析注释47 sgi-stl-vector

完整列表 The Annotated STL Sources 1 G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_vector.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Hewlett-Packard Company makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * * Copyright (c) 1996 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. */ /* NOTE: This is an internal header file, included by other STL headers. * You should not attempt to use it directly. */ #ifndef __SGI_STL_INTERNAL_VECTOR_H #define __SGI_STL_INTERNAL_VECTOR_H __STL_BEGIN_NAMESPACE #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) #pragma set woff 1174 #endif template // 預設使用 alloc 為配置器 class vector { public: // 以下標示 (1),(2),(3),(4),(5),代表 iterator_traits 所服務的5個型別。 typedef T value_type ; // (1) typedef value_type* pointer ; // (2) typedef const value_type* const_pointer ; typedef const value_type* const_iterator ;

Python100个小例子

一、Python 练习实例1 题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少? 程序分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去掉不满足条件的排列。 程序源代码: #!/usr/bin/python # -*- coding: UTF-8 -*- for i in range(1, 5): for j in range(1, 5): for k in range(1, 5): if (i != k) and (i != j) and (j != k): print(i, j, k) 二、Python 练习实例2 题目:企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%;60万到100万之间时,高于60万元的部分,可提成1.5%,高于100万元时,超过100万元的部分按1%提成,从键盘输入当月利润I,求应发放奖金总数? 程序分析:请利用数轴来分界,定位。注意定义时需把奖金定义成长整型。 程序源代码: #!/usr/bin/python # -*- coding: UTF-8 -*- i = int(input('净利润:')) arr = [1000000, 600000, 400000, 200000, 100000, 0] rat = [0.01, 0.015, 0.03, 0.05, 0.075, 0.1] r = 0 for idx in range(0, 6): if i > arr[idx]: r += (i - arr[idx]) * rat[idx] print((i - arr[idx]) * rat[idx]) i = arr[idx] print(r) 三、Python 练习实例3 题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少? 程序分析: 假设该数为 x。 1、则:x + 100 = n2, x + 100 + 168 = m2 2、计算等式:m2 - n2 = (m + n)(m - n) = 168

相关文档
最新文档