前言
此文写于2016年,虽然目前为止微软已经有些下埔路的趋势,但不可否认过去几十年的微软是无敌的,PC用户依然被微软垄断,随着越来越多的移动终端出现,应用越来越复杂,微软也无法逆转长江后浪推前浪的趋势,只能紧跟时代步伐,推出winPhone,免费升级win10,.Net开源,推广XBOX视频游戏机。
帝国终将毁灭,错并不在微软的运营之类的因素,因为微软无法阻止人类的善变和好奇,无法阻止新技术的出现。从编程角度考虑,我设计了一种框架可以很好处理未来10年的所有能想到需求,但可能五年后需求已经衍生至我没有考虑到的地方了,怎么办,难道放弃掉实现好的东西,迁到更好的框架中去吗?不可能,我只能眼睁睁看着别人在新的框架中很便利的处理问题(甚至别人框架很多地方都借鉴了我的)。
说了那么多没用的,还是说说windows吧,由于微软的地位,大部分PC应用和服务都是基于windows的,windows操作系统最大的特点就是商业化,当电脑都没有普及的时候,他靠着图形界面为主的操作系统以及丰富的软件应用向企业用户和个人用户推广,最终走向成功。从很早前(其实也就一年前)我就在考虑,这样一个图形界面是如何设计实现的,最近终于有了大致思路。
由于只是臆想,肯定有些不成熟的思路和不准确的概念,所以此文只是自己的一些理解,就不去引用太多权威论证了。
正文
从按下机箱的开机键开始到载入操作系统这部分我就不说了(基础不行),好了,现在进入桌面了,windows的图形界面映入眼帘,我靠,酷,仅仅通过鼠标和键盘就能实现与操作系统的交互。为什么我鼠标移动之后,显示器上的东西也可以跟着动呢?我以前在学了半个月编程后其实大致就有个思路,操作系统中有个东西在检视着鼠标的动作,按下键,松开键,移动。
但随之而来的疑问有两个:
- 如果我点的是不同的应用程序,比如我在浏览器的网页中右键,系统是如何知道我想要操作浏览器还是其他应用程序的
- 如果真的有一个东西在监视我的操作的话,那它应该是很蠢的轮询操作,就是
while(ture)
这种应该是很损耗性能的才对吧?
后来过了大概一年吧,我换了一份工作,那时候我对windows的理解已经更深了一层,对进城,线程,窗体,句柄都有些感悟了,于是以第一个疑问迎刃而解,操作确实会产生一个消息,比如鼠标点击会产生一个点击事件,这个消息中包含着事件的触发者,句柄等信息,可以理解为这个消息的发出者,以及发出地点,这样就可以区分出对不同应用程序的操作。但是第二个疑问还是没有解决,我也没在意就想别的事情去了
直到前不久我接触到nodejs时候,了解到nodejs用的事件驱动模型,才发现这个模型跟windows图形界面的消息队列机制是类似的,只不过windows使用在界面操作消息分发上,而nodejs用在网络I/O请求分发上,简单来看他们都时为了处理异步消息的非阻塞模型,除了用途不同外,还有个很大的区别就是nodejs是单线程处理(如果你是开发者,肯定会有疑问单线程如何实现异步,详细可以戳这里),而windows是多线程处理,那么,谈谈我现在对windows消息机制的理解吧
首先Windows肯定有一个全局的消息队列,用来监听操作者的操作,比如键盘输入或者鼠标点击,如上图的SystemQueue;每个应用程序也拥有自己的队列,当我点击鼠标后,会在消息全局消息队列中插入一条点击的消息(也可能直接插入到应用程序队列中,这里不考虑这种情况),系统会先看消息产生的句柄,来判断是我来处理,还是给别人处理,假设我是在网页上点击 的,那就把这个消息推给浏览器消息队列中,浏览器发现了消息,决定自己要做什么样的处理。如果这样的话,岂不是每个应用程序都要轮询监视着自己的消息队列来判断是否有新的信息需要处理 ?
其实不是的,这涉及到编程中的委托和事件,有编程经验的肯定见过 事件+=委托
这种写法,一个事件可以绑定多个委托,其实就是一种订阅模式,windows中的消息队列中也是用了同样的原理
其实浏览器一开始就定义好了所有的事件和委托,比如一个关闭按钮,他的事件是鼠标的click动作,委托是关闭窗口的实现,浏览器会先把他们注册到消息队列中去,他会告诉消息队列,如果发生了这样的事件,你就用这个委托关闭窗体。浏览器消息队列心里哪受得了这个,不干了,你让我一辈子就监视你们这点破事吗?于是他跑去系统的全局消息队列注册,他跟全局消息队列说,如果有浏览器相关的事件发生,你叫我一下,我好处理,ok?好,我去睡觉了(线程阻塞)。这样所有的事件和委托全部关联上了,傻乎乎的全局消息队列监视着用户的一举一动,并把消息事件按照注册好的委托一个个执行。
看上去整个流程似乎是没什么问题,其实只是后面没问题了,系统全局消息队列真的会傻乎乎的轮询自己的消息队列吗?我觉得肯定不是,他大概也用到 一样的机制,把自己所有的事情通过手段直接注册到鼠标键盘等输入设备上。于是最终的结果就是,他们都不傻!!都不是靠傻乎乎的轮询操作,通过订阅注册机制建立起各种各样的链接,用户的一举一动通过一条条线条最后落到每个应用程序的具体实现!!
以上过程忽视了完成委托后的回调过程,实际上最底层完成委托后还要一层层回调的上层(不是所有地方都需要回调),有了回调就以为了异步,假如某个应用程序消息队列有数个消息等待处理,那么他会会为每个消息开一个子线程处理委托,再通过回调函数向上传递结果,所以我们实际使用中经常遇到一个应用程序未响应,但是其他的都好好的,因为他们肯定是在不同的线程中。但有的时候很多程序一起未响应,那可能是虽然线程不同,但是他们用到了同一个资源未响应,导致他们全部卡主无法回调上层。但是问题肯定不止这么简单,还有很多细节需要思考,这只是现阶段我的理解而已
后记
本来感冒了准备吃好药就去睡觉的,但还是写到了很晚才结束,独自一人来外地工作还是做技术,相信这种感受一般人很难理解,尽在不言中。
每当我读到那些优秀的技术博文时,都会看下发布时间,很多都是五六年前的,甚至更早,我不禁思考,技术究竟是什么,他并非凭空产生,而是从人类社会中抽象而来,不断的积累沉淀衍生,想要在某个层次做好,就必须要了解底层的实现,就像消息机制一样,层层调用。对于我等开发来说就是四个字,学无止境。就像此文,作为一个.Net开发者,不得不了解自己底层的操作系统某些原理,你想真正在某个领域做到极致是不可能的,因为领域间并不存在完全的界限,所以我对此的态度就是八个字尽力而为,问心无愧。
这次离职后下一份工作可能会做一份java相关的,因为java现在的资源情况比.Net好太多了,之后我将不拘泥代码细节,更多精力花在设计、部署和其它开源技术方向。