此处嵌入式特指基于linux平台,单片机和其余rtos不在讨论范围~前端
我从事嵌入式软件开发有6,7个年头,bsp、驱动、应用软件、android hall、framework等都有涉猎。平时除了关注嵌入式行业的发展,也多少对Web、后台服务端、分布式等方向的技术有一些关注。java
近期有萌生换个行业方向的想法,想作作后台服务器相关的开发,因为以前工做中并无这方面的实际需求,只是本身平时关注,了解了些知识,好比:NIO、epoll、ngnix、zeromq、libevent、libuv、高并发、分布式、redis、python、tornado、django,涉猎比较杂,都了解个皮毛,不精。意外的是屡屡被互联网行业鄙视,面试机会都寥寥无几。python
此时我想究竟是什么问题呢,难道嵌入式出身的已经这么不受待见了吗?想当初,嵌入式,驱动开发,但是趋之若鹜的行业(有点夸张,不过8,9年前嵌入式但是听着比作java web的要牛逼些哦)。linux
问题老是有缘由的,我说下本身的理解:android
嵌入式是否真的高大上?为何没有嵌入式软件架构师?程序员
打开各类招聘网站,搜索架构师,会出现各类系统架构师,web架构师,后台服务端架构师等等,可是惟独很难看到嵌入式软件架构师。嵌入式软件不须要架构吗,驱动不须要架构吗?答案是固然须要,不过为何没有这方面的职位?web
个人见解:目前国内的嵌入式开发主要分为嵌入式底层开发和嵌入式应用开发,嵌入式的底层开发通常叫作驱动开发,或者bsp开发,有时也有称之为linux内核开发,名字听着都很高大上的感受。面试
这么高大上的名字为何没有架构师呢?linux、 kernel的架构师是linus等一众linux kernel开发维护者,由于自己linux kernel或者操做系统就是一个通用的平台,解决通用的问题,linux开源届的大牛都已经制定好了架构规则,留给可发挥的地方并很少,大部分工做只须要按照规则框架填充就能够了。redis
以目前国内大部分公司的业务需求,只是在作外围设备的集成,嵌入式平台的porting、搭建裁剪、业务需求彻底不会超过kernel里提供的功能范围,致使没有什么新的架构须要开发人员去设计、实现。那嵌入式bsp开发人员都在作什么?除了调试多种多样的外设,替硬件擦屁股,就是解些稳定性的bug了(这里对具体工做不详细描述了,调试外设只会增长一些经验,增长广度,对提升深度贡献不大,只是按不会调试-》会调试-》调试的快这个路线发展,而解稳定性问题确实是须要一些积累经验)django
而嵌入式上的应用开发,通常业务逻辑比较简单,被不少人忽略,因此招聘方也会感受没有什么必要找架构师级别的了。
至此感受嵌入式行业的确不须要架构师,被互联网行业的鄙视也没什么大惊小怪的。
但的确是这样子的吗?对于嵌入式底层的开发,有能力对kernel、驱动架构提出架构层优化的,国内的开发人员应该很少,因此对于大部分普通人,仍是不要“妄想”作Linux kernel的架构师了(固然我相信国人中必定存在有这个能力的大牛),发现,解决一些bug,到更靠谱些。
那么对于嵌入式应用层的开发,咱们真的不须要架构吗?
以本身的实际经历讲述下曾经对一个嵌入式设备应用软件的架构设计和优化:我曾经接手过一个项目,项目采用单进程多线程的模型,项目中包括几个模块,以a, b, c, d,e表明。这个项目的业务逻辑决定这几个模块有很多关联。
例如:最初的设计中a模块是一个状态监测模块,它会基于监测到的状态调用b,c模块的接口实现一些功能(多线程的好处就是直接调用很方便,因此开发人员大多这么干,简单粗暴)。可是需求老是变幻无穷,加入一个f模块,f模块也须要对a模块监测的状态进行一个处理,按照以前的套路,完成这个功能分两步:
-
在f模块提供接口。
-
在a模块中调用该接口,至此新需求已经“完美”的解决了。
前面提到需求老是变幻无穷的,新的需求又来了,客户提出定制需求,须要加入另外一个g模块,一样处理a模块监测的状态,可是该定制需求不须要刚刚加入的f模块,此时最简单粗暴的方式是,定义一个宏,区分该定制需求和以前的通用需求,build两个程序版本。这样的作法看似简单,但后面若是定制需求逐渐增多,维护这么多定制版本程序就是个噩梦,代码管理和通用性也会是很大的问题,同时代码中充斥着对不一样宏定义的差别化处理。
比较好的作法是加入设备型号版本的动态监测,用一个build程序版本动态支持全部的定制需求,这样减小了对不一样build程序的维护。可是这种作法只解决build程序的版本维护工做,没有解决宏定义差别化处理的问题,只是会将以前的宏判断,改成动态设备版本号判断,若是这些差别化的判断只集中在一处进行,也不会引发大的复杂化的问题,但显然这个很差保证,有可能这些差别化的处理会蔓延到整个项目的各个角落,这样项目维护起来就会变成一场噩梦。
不须要什么高深的软件思想,大部人都会想到把差别化的部分提取出来,放在一个统一的地方集中管理,对差别化的修改只集中在这个统一管理的地方。
通用作法就是采用callback设置钩子,而后在callback中定制差别化的需求,对callback的处理作差别化的配置,对应到上面例子,就是在a模块添加一个钩子,而后在系统初始化时,根据设备版本号的不一样,差别化定制callback处理函数,同时要将这些定制callback处理函数放在同一地方处理,不然仍然分散在各个角落里就没有意义(前一种方式不放置钩子是没法将这些差别化配置放在一块儿的),这样处理带来的另一个好处是,咱们对功能性需求的改变,不会影响到a模块的处理,也就是咱们添加功能,不须要修改a模块的代码了(前一种方式要修改a模块的调用流程),这样也就实现了一个模块的分离。
至此第二种的方案的架构(其实也谈不上架构了)相比第一种方案已经有了很多提高,至少让开发人员稍微轻松了些,对于其余定制需求,开发人员之须要修改这个callback处理,关注差别化部分就能够了。
软件是须要不断进化的,第二种方案是最优解吗,固然不是,还有优化空间吗?
下面先跑个题,谈谈多线程/多进程模型的优缺点,主要谈多进程的优势了:
教科书上的解释就不提了,首先我对大的项目是推崇多进程模型,无关性能,主要缘由有:
1.模块的解耦:不少开发人员维护开发的多线程模型项目应该都多少会存在下面的问题:跨模块间的直接调用,若是不相信,好,你的项目必定是分模块的吧,如今随机的删掉一个模块,build下看能build经过吗(只须要build不须要运行),我相信大部分状况下必定会遇到某个函数调用,某个全局变量找不到的状况,这种状况说明你的模块间存在强耦合了。
因为多线程自然的优点,地址空间的相互可见,致使直接调用十分容易,不少经验尚浅的工程师,很容易就写出直接调用的简单粗暴的接口,若是遇到个static接口的函数,图方便也会把static去掉,直接拿过来用了。这样整个工程随着功能不断的添加,模块间的交叉愈来愈多,耦合越高。
而我之因此推崇多进程的缘由就是,多进程能从物理上隔绝了这种“方便”的通信方式,致使在想实现一个模块交互时,会多思考下这个交互是必要的吗,若是是必要的,则会进一步思考接口定义是否简单明了(由于进程间的通信相对会麻烦些,开发人员会本着能减小交互,明确接口的想法去仔细考虑接口,协议的定义,不然折腾的是本身了),这如同人生,若是一直顺风顺水,人们可能不会想太多,思考太多,而若是道路上有些坎坷,则会有另外一种感悟吧。
因此个人想法是多进程的模型会逼迫你去更多的思考想程序的设计,物理上减小模块的耦合。
抽象通用组件,分离通用功能和业务逻辑功能:当把一个多线程模型修改成多进程模型的过程当中,常常会发现有些接口代码重复的出如今多个进程模块中,由于以前接口函数是在一个进程空间,你们均可以直接调用的,好比接口A被模块a,b调用,模块a,b分离为两个独立的进程后,接口A须要在a,b中分别实现了,无需解释,重复代码这个在软件工程中是大忌,必须消除。作法也很简单,将这些被多个模块调用的接口分离处理作成lib,供其余模块调用,当你完成这部分工做后,你发现了什么,是否是剥离的接口,能够做为整个项目的通用组件存在了,完美的状况下,lib下的代码是通用基础组件,各个模块中是独立的业务处理模块。
2.方便定位问题:多线程模型中当又一个线程异常退出,会致使整个进程退出,固然经过一些crash信息,能够定位是哪一个线程死掉。但若是这些线程模块是由多个小组、人员维护,当整个进程崩溃掉后,如何判断由哪一个小组解决,会是一个大的问题。并且有时还会出现的现象是挂在一个线程,但实际上是另一个线程模块引发的(耦合的祸端),遇到这种状况,不免出现小组间的扯皮,推诿。(自信的工程师都认为个人代码没有问题)
而若是采用多进程的模型,好吧,你的服务进程挂了,你本身找缘由吧,没什么可争辩的了。
3.方便性能测试:多线程种单个线程的资源占用不是很好查看(至少有些嵌入式系统没有完善的命令),当整个进程资源消耗很高时,如何判判定位时哪一个模块线程的问题,同前边问题同样难以抉择。而若是是多进程的模型,谁的进程占了好多资源,谁就去查下吧,其实这个仍是个颗粒度的问题。一样的系统,划分红多个进程,单个进程的复杂度必定比只有一个进程的复杂度低的多,复杂度下降,也就更容易定位查找各类问题。
4.分布式部署:互联网行业一直强调的分布式,云啊什么的,嵌入式行业就很苦逼了,貌似不须要什么分布式吧,其实也对,大部分状况下,嵌入式采用单芯片,独立运行,分布式遇到的不多。但若是万一那天你在一个设备中,将原本一个芯片完成的功能分散到两个芯片中处理呢,多进程的扩展就容易的多了。
这只是举个特殊的例子,其实嵌入式设备就是个分布式的行业,只是一开始就已经实现分离了,而不是从集中到分布式的路线发展起来的。
方便公司的代码权限隔离:其实我鄙视这种作法,公司要相信本身的员工,但鉴于诚信在中国已经。。。作些隔离也无可厚非了。
多线程模型下,前面讲到若是去除一个模块,你可能都不能build了,那么是要把全部代码暴露给全部的工程师吗,显然不能,因此各个模块只能提供库的形式了,不过我以为将通用功能接口组织成通用库是正常的作法,而若是把和业务相关的模块也提供成库,就有点。。。。
至此在补充一下,以上全部的优势,其实都不是很关键的点,都不可以让多进程有绝对的优点压倒多线程模型,只是从我的的角度以为,多进程模型更能强迫工程师思考解决一些问题。(而这些问题有经验的工程师不管什么模型都会思考的)
上面说了这么多,该考虑下把以前项目的例子改为多进程模型,不然就只是纸上谈兵了。
首当其冲的问题就是:选择多进程的通信方式,多线程间的直接调用是不能用了,那么如何选择多进程的通信方式呢?
linux下提供不少ipc方式,此处不一一列举,对于非大数据量的控制,通信消息的传递,比较好的方式是采用socket,本机上更多采用unix socket方式,(这种方式有什么好处?当你有须要把单一系统作成分布式系统时,优点就明显了)
可是仅仅采用socket来实现前面例子的功能,一样会存在一些问题:仍是前面的例子,首先说明前面咱们优化后的第二种方案在多进程模型已经不能在继续使用了,缘由比较简单,应该不须要解释。
简单的作法即基于方案一,把直接调用改成socket通讯(定义好通讯协议便可),可是熟悉socket开发的工程师都清楚,开始socket通讯要先进行一些前期的工做(主要就是链接,将两个模块关联起来),因此前面的例子会变成这个样子,模块a要和模块b,c创建链接,若是加入f模块,模块a还要和f模块创建链接。这样状况在内心画一张链接图就会发现好像咱们织了一张蜘蛛网,节点间的关系错综复杂,并且和方案一同样,咱们添加一个和a关联的模块,就要修改模块a的代码,并且这种状况比多线程模型还有繁琐复杂的多了。这种作法绝对是个噩梦。
如何解决?我想不少人必定想到了采用总线分发的方式。了解android系统开发的会想到binder,了解openwrt的会想到ubus,了解桌面会想到dbus,互联网行业的开发者必定也知道redis里提供的sub/pub模块。
上面的binder,ubus等原理很简单,就是创建一个消息中心,构建一个转发路由模型,全部其余模块之间不直接交互,而是采用消息中心转发,路由,而如何决定路由规则,则采用订阅/发布的观察者模式来进行规则的定义。(嵌入式开发或者c语言开发者,常常会误觉得设计模式是和面向对象语言关联的,是面向对象语言独有,虽然有不少大牛作了这方面的普及,但鉴于有些开发者的信息渠道比较闭塞,致使这种想法仍然十分盛行)
基于这个模型,咱们上面例子的需求就很好解决了,加入一个消息中心模块,全部须要通讯的模块只同该消息中心模块链接,而后订阅本身感兴趣的事件,当事件发生时,只须要进行相应的处理就能够了。
这样上面的模块b,c订阅模块a的事件,当模块a检测到某事件时,发布该事件,该事件先到达消息中心,在由消息中心转发给模块b,c,而对于新加入的模块f,也只须要订阅该模块,而不须要在修改到模块a的代码,使功能的扩展十分方便。
同时对于前面提到的定制化开发一样获得了简化,若是定制化版本须要加入模块g,这样只须要定制化版本中将模块g做为一个独立进程启动,而后订阅模块a的事件便可,而定制版本和通用版的区别就在因而否启动模块g的进程,从而实现了软件工程的一个目标:功能的添加如同搭积木同样,只须要把一个模块插入(启动)或拔出(不启动)便可,功能的改变只局限在一个或某几个模块间,对主体框架不会有任何影响。
以上大概描述了对一个项目需求逐步优化的过程,例子看似是基于嵌入式项目,但貌似对软件工程一样适用。
来到互联网行业:
查看下各大网站架构师对本网站技术架构变革分享的文章,首先提到的通常都是,基于业务将以前的一个应用服务器功能拆分,更加细化(好比电商对登陆,注册,交易,商品,卖家等业务服务的拆分),而后将拆分出来的服务部署在多台服务器上,来提供并发。这里是否有些耳熟,和前面讲到的多线程到多进程的划分是否有类似呢。
拆分后一样遇到通讯的问题,此时不少消息中间件应运而生,好比阿里的duboo,简单了解下这些中间件的原理,无外乎订阅发布,RPC等机制,能够说大同小异,而难点在于协议的制定和性能处理的提高。
在对照下互联网行业的负载均衡方案,仿佛那个负载均衡的前端也像一个消息中心了。
上面说了这么多,只是想说明一个问题,软件的设计是相通的,基于的思想是相同的,虽然嵌入式行业的业务逻辑相对比较简单,但其实在仔细思考后,仍然会有不少架构上的改进,设计。
可是让我感到悲哀的是,有些嵌入式开发者,鉴于业务逻辑的简单,感受采用一些不那么好的处理方式也能解决问题,不去思考如何去优化,改进。好比上面例子的方案一,若是在定制需求很少的状况下,维护起来也没太大问题,即便定制需求多了,再招些初级程序员也能维护的过来,一我的一套代码负责一个项目的公司也不是不存在。
一样互联网行业和嵌入式行业也不该该存在一个不能够逾越的高墙,咱们更应该关注的是通用的软件工程思想。