导读

这是一篇杂谈,也算是今年的一篇总结。
也许是很久没工作的原因,心里有些异样,既是烦躁,又是欣喜,
烦躁是没有目标,
欣喜是能又一次触碰那些曾经被忽略的细节。


学习篇

学习对于一个程序员来说,并不是一蹴而就的过程。
需要实时温故而知新,日积月累的巩固总结。
要考验一个程序员水平的最好办法并不是做一场测验,
抑或是写一个项目,
而是在经过数月的遗忘之后,
还剩下多少,
这剩余的部分既是经验的结晶,
也是知识的根,
根和经验会决定的便是需要多久才会重新恢复到最佳状态。


语言篇

闲着的这段时间,也并非什么都没有做,
只是用的比较多的反而是 c# php js python等语言。
反倒是最擅长的 c/c++ 反而用的少了些。
老实说,在上述(除开c/c++)中个人认为顺畅程度

js >= php > c# >= python

python

我讨厌python的制表符,虽然用起来方便,但着实也让我头疼,
另一个便是它的不向下兼容,虽然对其能摆脱历史包袱不惜断臂的行为略表倾佩,
但着实给使用者也带来了莫大不便,
a依赖需要a版本,b依赖需要b版本的时候,编程似乎成了选择题,
反而违背了python自身哲学

Although that way may not be obvious at first unless you're Dutch.
尽量找一种,最好是唯一一种明显的解决方案

也或许是我并没有写太多的缘故,并无法从更深的层面去解读这个语言。

csharp

我讨厌c#不明确的 引用 传递,确实给我带来了一些麻烦,
这一点上他就和 go 语言上一样讨厌,比起go
我也同时讨厌c#编译的程序不能开箱即用这点,
java 一样需要一套虚拟机 + 运行时框架支撑,
虽说这不是必须的,这里也只聊通常情况而已。
当然我很喜欢 c# 的语法糖,很甜,有毒!

为何在提及运行环境时不提 jsphp 呢?
因为这两个语言几乎总运行他们固定的环境,
而它们的客户都几乎存在它们的环境。
这里js特指运行于浏览器中的前端,
虽说运行在后端的 js 并无太大区别,
碍于我对 nodejs 接触有限,言多必败。

php

对于 php,个人认为它是个万金油,大抵可以看作是一门经过简化并脚本化的 c++
适用于中小型企业项目大部分场景。
在写 php 项目时,可以使用一种相对放松的状态,
既不考虑内存开销,也不考虑运行效率,也许是我的程度还不足以考虑这些,
但若只是把着眼点放在框架设计及实现上,会让人有一种在创造,而不是深陷泥潭的感觉。

javascript

未来是前端的天下,这是我在数年前就感受到的。
无论是企业还是自由开发者,最根本上需要解决的问题是展示。
对于客户来说,无论是哪种客户,都有一个核心类型的界面交互需求。
而这点上,持续数十年的主流C/S 都比不上 B/S 来的灵活。
根据现有的硬件 网速 增长速度可以预见,未来 B/S 将替换绝大部分 C/S,一步一步蚕食。
ie 时代,我还记得自己建的第一个网站,
几个简单的<a href="#"></a>
我们用 flashactiveX 去提升表现力。
那个时候的我们无法预见 js会对这个行业带来这么大的冲突。
这一切都源自于google的支持,源自 v8 引擎的种种黑科技,
这个项目的意义不亚于 unix 诞生。
这也是侧面证明js这门语言本身的优秀而已,否则也不足达到如此效用。
在讨论前端问题时,总免不了要讨论浏览器。
chrome 浏览器本身就是一个工程奇迹,其代码的冗余程度也是相当的高,
windows内核不相上下,
想必如此之大的一个项目在管理上也是无比复杂。
扯远了,回到js的话题。
js本身具备了大多数脚本语言的优点和缺点,
对于 c/c++ 程序员来说,是一门极易上手的语言。
其在前端应用上,更像一种插件,各自为政。

c/c++

它们是我学习使用时间最长的语言,可谓是爱恨交加。
其在某些方面表现的非常矛盾。

先说c,灵活且清晰
常规的代码,甚至能在编写阶段预测出其生成的汇编,当然是未经过优化的。
一方面,我们希望语言能提供更为高级的语法,
另一方面,我们又希望高级的语法能不破坏语言本身的清晰度,不会背着你生成一些多变且难以理解的内容。
c 语言是一门需要尽可能和系统打交道的语言,否则其使用价值会被大大降低。
从工程角度,我们即希望它灵活清晰又希望它高度可控,
在一定工程量时,c语言能对二者平衡处理的得当。
然而在构建超过一定工程量的大型工程时,
会同时失去二者,只能局部可控,整体上并不可控,
把握工程的难度会倍增,并且会丧失掉原本的灵活清晰,变得举步维艰。
无论是命名冲突还是数不尽的头文件和它错综复杂的函数视图,都让管理上变得极为困难,
同时又需要竭力避免冗余,这又会导致工程组织上的困难。
虽然我们可以依据一些条款规则来处理,但其依旧是复杂的,
越往后写,越有一种往自己身上套枷锁的感觉。
也就是说,对初期预估工程规模,整体框架设计上都有着非常严峻的要求。
对于面向过程来说,最大的问题还是在于初期把握不足,后期需求变更而产生的重构需求。
其代价之大不逊于从头开始设计。

再说c++,灵活且高级
由于引入面向对象等其他高级概念,
在面对上面问题时依旧能游刃有余。
只是灵活和高级注定是把双刃剑,
它们产生了复杂,我们需要对抗这种复杂,
一方面我们希望尽可能的避免与实现打交道,
另一方面为了能使代码清晰可靠,
我们又不得不在一定程度上知道语言在背着我们做哪些事。
真正能掌握这个平衡的人,需要对其所有特性都具备一定深度的理解,这种人基本上能称为 -- 宗师。
无论你是否承认,我们在写c++时,是背着枷锁的,或大或小,取决于你的认知,
也取决于你对高级特性的依赖程度。
也有人选择视而不见,抛弃掉部分灵活与高级以换取安全与稳定。
总之c++是一门极其复杂的语言。

语言篇小结

还有很多之前略微学过用过的语言,像 go java lisp
一个是相隔久远,另一个是理解太过肤浅,就不一一去写了。
并不存在什么最好的语言。
它们各自有着各自的思想哲学及应用场景。
能做的也只是在实际项目中取舍,
选择一个在当下更为适用的语言。

系统篇

通常情况下,我工作于 windows平台,
部分情况下,会使用 linuxmac.

windows

由于长时间在windows 平台上开发,
对于这个平台也算是略有理解。
曾拜读过 WRKwindows内核相关代码 ,
其有一种严谨的逻辑上的美感和沧桑,
着实让人感动。
也许是历史包袱,其内部使用了c语言去运筹面向对象思想,
增加可控性的同时增加了复杂度。
其设计思想在试图抛开 posix设计思想的时候,也就脱离了极简美学。
这也影响了一个阵营的开发思想。

举个例子

HANDLE CreateFileA(
  LPCSTR                lpFileName,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwCreationDisposition,
  DWORD                 dwFlagsAndAttributes,
  HANDLE                hTemplateFile
);

其几乎每一个 API 都像一把瑞士军刀,
提供了复杂的参数,
同时包揽了很多原本可以拆开的东西。
没错这就是 windows开发哲学
能自己做的事情,绝不给别人添麻烦。

也导致很多windows程序员并不太喜欢包含过多依赖,甚至抵制。
希望用户得到产品就是一个不需要部署,不需要其他复杂操作,
下载运行即可,真正意义上的开箱即用。
使用者并不需要知道到底如何实现,他们只需要点点鼠标就能用。

在早些版本的windows系统中曾存在很多稳定性问题,
Vista 版本之后,其稳定性已经不再是大问题。

但是无论如何,windows 都存在一个必然问题,
这既是它的黑科技,也是它漏洞的源泉。

windows的视窗部分,使用了一种将 r0内存 映射至 r3内存并予以操作的技术,正是这种技术保证了视窗的流畅,
然而这个部分是极易受到攻击的,最容易出现的便是本地提权漏洞,最近的记忆中在18年间就曝出过相关漏洞,也正是由于这点,
windows桌面版的流畅度明显高于 linux 桌面版。

linux

我只阅读过少数几个版本的linux内核源码,
个人认为作为学习,比较值得一读是 1.2.0 版本,也就是移植了 net 模块之后的版本。
当然后续的 vfs netfilter 等等也都是值得一读的。

我在linxu上面开发的时间并不长,可能只有一年左右。
给我感觉还算比较舒服,除了调试。
其给我展现的哲学便是
做好自己的事情,学会依赖他人

是的,linux 上面开发,更需要的是专而精。
每个人都做好自己那块,既是接受者也是给予者,杜绝重复工作。

这也导致了,我们在安装软件时,免不了一大堆依赖。
另外各发行版之间那些细微的不兼容问题也是非常的头疼。

mac

我在mac上开发的项目非常有限,
maclinux非常接近,
大抵是macfreeBSD 有些渊源的缘故,
同时应该采用了某种方式提高图形方面的效率,
另外是加入了安全模块等。
mac 是个极其闭塞的系统,
其开发文档远不及 linuxwindows
通常一个问题既需要参考自身的开发文档,又需要参考 linux 的开发文档。
老实说,
我宁可使用 linux桌面版也不愿意使用mac,其除了接口较为统一,实际上不太适合开发,
可能更适合设计相关的职位使用。

系统篇小结

系统的设计哲学,直接会影响开发人员的开发哲学。
其熟练度会影响跨平台开发的能力,
作为开发人员,
无论是否有需要,
都应对常用系统略知一二。

无论是使用
vim/emacs 还是 vscode
无论是使用
gbd/cgdb 还是 windbg
无论编译工具链抑或是IDE如何变化
最根本的还是思想,
其他的都是可替代的。


设计篇

设计是个大话题。
不应拘泥于表象,
除了各种设计模式和各种设计原则外,
更重要的是如何融入实践之中。
这二者本是一体的。
编程没有银弹,滥用设计模式,不如不用。
关于这方面的知识,
以我的设计经验,也说不出什么太过深奥的东西来。
如何划分职责,如何解耦合,如何黏合等等,
每一个问题,都有只有在每一次实践中才能领悟到一点。
推荐一本书吧,还不觉得自己能讲的比这本书清楚:
《代码大全2》

说点感受吧,
好的设计,会让实现过程如虎添翼,越写越顺。
而烂的设计,会让整个开发提心吊胆,
时刻担心项目崩盘。
至于过度设计,则会大大影响开发周期。
而简化设计,又可能导致遗漏。
总之没有最好的设计,只有当前水平能做到的设计。


逆向篇

原本是没打算写这篇的,
关于逆向工程并没有什么能说的东西,
逆向本身是一种凭借正向经验猜测并通过工具验证的过程。
其命中率本身也取决于正向能力,当然也可以穷举。
如果不熟悉正向开发,不熟悉框架,倒不是说无法逆向分析,
只是在面对语义鸿沟问题时会得到另一种答案罢了。
即把原本的面向对象的,抽象的问题转换为了面向过程的,低级的。在面对大型工程时对摸清脉络会产生些许影响。
没有脉络作为导向标,很容易在低级代码流中迷失。

至于运行时的保护和反保护,并不是逆向的一部分,
属于一种工具上的对抗。

另外静态分析中的保护,例如二进制中的膨胀或虚拟机,这两者一般同时存在,是一种思想上的保护,对于这种保护,要么是通过优化的方式对抗膨胀,要么是通过分析虚拟机指令集还原。

编译过程:
机器码 -> 汇编 -> 编译器 -> 虚拟机指令
还原过程:
虚拟机指令 -> 反编译器 -> 汇编

虚拟机指令执行过程:
虚拟机指令 -> 虚拟机 -> 汇编 -> 机器码

对于不同虚拟机,这个解法又是大不一样,一样的部分只有这个逆过程,相关知识可以在阅读脚本语言的虚拟机实现时得知,区别仅在于源语言以及一些为了加大分析难度的设计。

又例如脚本语言中常见的混淆等保护,通过重命名等方式逐一还原等。关于保护这个话题只能简单提及几句,否则就扯远了。

总之逆向是一个需要经验,耐心的方向。


总结

通过几个篇章,梳理了自身的部分知识脉络。
提炼了部分对于学习或开发的感悟。
同时希望在新的一年能更进一层。