我们为什么要强调被吐槽“八股文”的计算机基础理论

349 阅读18分钟
// 一、综述回答题目

首先,统一一下计算机基础理论涵盖范围,主要包括了计算机考研要求的基础课程《操作系统》、《网络原理》、《计算机组成》、《数据结构和算法》,额外的还涵盖一些《编译原理》、《数据库原理》、《软件工程》、《分布式系统》和一点点数电、模电、集成电路等知识等。

其次,一个事实是无论校招还是社招,我们强调计算机基础理论的核心原因就如同计算机考研的目的一样,希望尽可能的找到那些能在计算机研发这条路上能够走的稳、走的快且走的远的人。

然后我们会有一个疑问,为什么无论是企业招聘还是研究生升学,大家这么一致的认为通过“八股文”选拔人才有更大概率找到目标候选人呢?我从个人认知和理解来尝试回答一下这个问题:

1、基础理论好的,技术适应性比较高。

2、基础理论好的,问题解决能力比较强。

3、基础理论好的,反映了较好的学习能力、钻研态度和专业承诺。

4、基础理论好的,职业发展潜力大。

// 二、展开说明原因

在工程技术领域,对技术适应性都是有较高要求的。因为“工程”要求工程师做项目可实施、可观测、易迭代、好运维和稳定生产。想要达到这样的目标,工程师需要掌握大量的技术栈和工程实践经验。而“技术”的特点是领域多且迭代快,基础理论好的工程师触类旁通,能快速举一反三,易于掌握和用好新工具和新技术。反之基础差的工程师碰到一个新工具就真的是全新的工具,重新学一遍“如何用、如何能用好、有问题如何解决、工具原理和实现是什么”,是非常低效的。新技术和新工具可是层出不穷的,如《庄子·内篇》所说:“吾生也有涯,而知也无涯。以有涯随无涯,殆已!”。应该如何做呢?庄子紧接着讲了“庖丁解牛”的故事,我们应该从“始解牛之时,所见无非牛者。”这样的,转变到“以神遇而不以目视,官知止而神欲行。”,就是说工程师学习技术栈不要见到个A工具就只是学习A工具,应该停止简单的“知道、会用”学习,要心领神会深入原理,形成理论认知,为触类旁通做储备。

这里举三个小例子。

第一个,很多同学刚学语言的时候会听到“堆空间、栈空间”,然后又会被教材告知什么数据存到了堆空间,什么数据存到了栈空间,什么指令存到了栈空间,学的是晕头转向也不容易理解,根本消化不了也不知道干啥用的。如果学了编译原理,这时候才会恍然大悟,“噢~计算机程序原来是这样执行的啊”,甚至还能触类旁通的理解了一些语言为什么提供堆外空间,进而能够更高效的理解堆空间的管理、如何使用堆内堆外空间提升程序执行效率,也就清楚了语言虚拟机的存在目的和意义,进而看不同的语言有了一种更高层次的视角,不会有学习不同语言的困难感和抵触感。

再一个,比如大家都用数据库,市面上常用的大大小小的数据库或者数据存储管理工具少说一二十种,可预见的未来,随着业务场景的变化还会出现新的数据库工具,难道一个一个学下去吗?大家能都明白这样明显的不科学。那如何科学的学呢?你就还是从《数据库原理》和《计算机组成》原理里学,然后就懂了数据存储的地方无非是内存和外存,数据库工具也可以简单的划分成内存型的、外存型的和内存外存结合型的。外存型的通过学习计算机组成就明白了磁盘或SSD的工作原理,知道了为了提效硬件是按块存储和读写的,你自己设计数据库工具的时候也该明白为了提效,数据库的数据存储应该按块存储和管理,即使占不满一个或N个数据块也要完整的占用这么多空间,这时候你再看比如MySQL、MongoDB类的外存型数据库就明白它们的底层存储数据的组织方式,为什么也都是分页存储了,甚至操作系统的文件系统也是分页存储数据的,如果《算法和数据结构》学得好,软件层面涉及的内容也是一眼就会一眼就懂的。

再举例,就是工作和面试里经常会碰到多进程/多线程安全类的问题,如果不学《操作系统》和《计算机组成》的内容,单纯的去理解线程安全这事,一个是比较不好理解,第二就是简单理解了也理解的不深,会导致实践中用不好进程/线程/协程,用不好是指性能、成本和安全三方面都达不到理想状态。操作系统提供了并发编程的支持,包括进程/线程的创建、调度、同步和通信,还有上下文切换、并发控制机制等,搞懂这些才能理解并发编程的安全问题是如何产生的,如何规避和解决。学了《计算机组成》就能明白现在多核CPU硬件的架构,理解CPU、多级缓存和内存之间的I/O机制,才能理解比操作系统层面更深一层的并发控制,才能理解为什么编程过程中会需要使用类似Java语言中的synchronized和volatile关键字,不然你都看不懂volatile涉及到的变量可见性是什么意思。

有了上边举例的解释,也就很容易解释为什么“基础理论好的,问题解决能力比较强”,在工程实践中,看到的问题和表现都是症状,只有深入理解底层的原理和细节,才能根据表面的症状找到根音,对症下药,解决问题。否则只能自己乱试或网络搜索,虽然网络搜索症状大多数时候也能解决问题,但这会有一个深层次的坑,就是一个症状表现的根因可能是多个,这次你用一种搜到的方式解决了,下次可未必会生效,碰到少见的问题没有借鉴信息的时候就傻眼了。并且表面的解决了一些问题表现,是不是掩盖了或者埋下了更大隐患这还两说呢。

再聊一下“基础理论好的,反映了较好的学习能力和钻研态度”。基础理论的学习需要长时间的投入积累和深入理解,学习能力差或者效率低的,想学好和理解深是比较困难的,所以能很好的反映了候选人的学习能力和钻研态度。同时这么长时间的投入,意味着投身技术领域不是投机取巧随便试试,是想长远发展的,是一个很好的投名状和隐形承诺。做招聘的企业和导师更愿意相信已经花了很多精力和时间投入并且有一定结果的候选人,这个还是比较好理解的。

最后就是“职业发展潜力大”。工程技术领域是持续发展,拥有深厚的理论基础,技术适应性好的人能长久发展这是比较显而易见的;同时,可以在工作中不仅仅是执行任务,还能够提出新的想法和更好的解决方案,基础理论是帮助你站在了巨人肩膀上,更好的推动技术创新,甚至发论文影响某个领域或整个行业发展;如果希望晋升管理层,那做技术决策的核心依据,除了工程经验理和工程环境外,基础理论论知识为技术决策提供了坚实的依据,理论上的深入理解可以帮助在关键问题上做出更明智的选择;最后一点也很重要,个人影响力对职业的发展很重要,基础理论扎实并且有很好的工程经验就可以做知识传播、人才培养和团队建设。这些点对职业的发展都有非常积极的影响。

如果你认真的看到了这里,相信你已经认同了理论基础对工程师来说非常重要的观点,不再对基础理论抱有“八股文”一样的偏见,那么接下来就可以继续聊聊如何学习好计算机基础理论。我会先简单对各个基础理论做个简单介绍,然后给出一个泛化的学习地图,毕竟计算机工程也是有众多不同领域的,不同领域学习路径多少可能有些差异,但基础这块我理解都是需要且变化不大的。

// 三、基础理论简述
  • 操作系统:计算机系统软硬件的管理和用户交互。核心要学习操作系统是如何做资源管理的,比如进程管理、内存管理、文件系统,对应的硬件是CPU、内存和外存,管理的细节要弄清楚,比如进程是如何调度、通信和生命周期等;内存的分配和回收等;外存的存储索引构建和读写管理等。
  • 网络原理:网络原理涉及计算机网络的设计、实现、管理和维护。要学习七层模型架构和每一层的核心作用、网络的组网方式和寻址过程、常用的应用层协议等。
  • 计算机组成:核心介绍了冯诺依曼架构(计算器、内外存和输入输出)的基本构成和工作原理。要搞清楚计算器的架构和计算过程、数据的组织方式和格式、存储系统的架构以及数据在各个核心芯片间的流转等。
  • 数据结构和算法:学习数据的组织结构以及基于该结构的算法设计。搞清楚时间/空间复杂度的原理和作用,常见的数据结构(栈、队列、链表、树、图等)和算法思想(分治、递归、动态规划、剪枝、回溯、模糊计算等)要清楚。
  • 编译原理:核心研究如何将一种语言转换成另一种语言或其他形式的中间代码。除了常说的词法/语法/语义分析外,还要弄清楚中间代码和虚拟机的作用和原理。
  • 数据库原理:按数据模型(关系、非关系)学习数据的组织、存储、检索和管理,数据一致性问题和事务等。
  • 软件工程:学习软件开发的生命周期和管理方法、软件架构和设计模式、规范设计和原则等。
  • 分布式系统:理解分布式系统面临的问题,如可靠性问题、数据一致性问题、容错容灾、事物管理、服务发现、负载均衡等等;知道各个问题的解决方案和使用场景,如保障数据一致的共识算法、分区备份容灾等等;
  • 数电、模电、单片机和集成电路知识:了解数字电路、模拟电路、单片机和集成电路的知识可以帮助软件工程师更好地理解计算机的底层工作原理,从而能更好的理解操作系统和底层工具的产生原因、解决思路和实现方法,进而帮助应用层工程师编写更适和和高效的代码;学习并理解一些硬件的性能指标和限制,例如时钟频率、功耗功率、并行处理能力等,这些也会直接影响软件性能和资源利用效率;另外现在大数据方向也有较多的异构计算架构的设计,对CPU、GPU等有充足的理解,才能从软件应用层面做到类似的创新和实践。
// 四、学习路径地图建议

大部分人的正反馈特点就是要快,尤其是在反人性的 “学习”这件事上,一定要快的有正反馈,快的见到收益。本着这个原则,学习路径的规划也就比较简单清晰了。按正反馈快慢和收益大小排个序,差不多路径也就有了,从反馈快逐步去学收益大的。

// 这是一份后端开发学习路线,前端路线可以类推。

学习的起点一定是先学习一门编程语言。选语言最好也选高级一些的语言,不要从C语言学起,入门困难收益又低,直接把初学者学没了。Java、Go、Python、PHP都挺好。主要学习相关的语法和围绕这个语言的常用工具库。

然后就是学习数据库工具,比如MySQL和Redis,至此就可以应对一些互联网应用的后端开发工作了。围绕这俩工具学会SQL语法,同时弄清楚关系型数据库和非关系型数据库的特点,外存型和内存型数据库的优劣势、应用场景等。

为了能让应用上线,还需要学习一些类似Nginx的Server工具、类似Linux的服务运行环境和类似Prometheus的监控告警工具。

随着数据的积累,率先碰到的问题可能是数据库单机存储空间不够了,这时候要学习一些数据库水平扩容的技术。另外可能也要考虑分布式数据库如MongoDB、HBase之类的。同时也可能碰到日志记录存储的问题,试试ELK套件。

随着业务的扩展和系统复杂度的提升,会有多业务系统出现,系统之间交叉依赖。这时候会需要做多服务管理了。对内可以学习用一些实现RPC框架的工具,如gRPC、Thrfit,对外学习一些网关管理类的工具,这中间还需要有一些配套工具,如服务发现和注册使用的分布式存储工具(ETCD、ZooKeeper等)。

都已经系统分化到需要做RPC和网关了,各种服务和工具都需要部署生产,想必运维的压力也随着而来了,可以学一些容器化技术Docker,容器管理工具kubernetes。

到这里你已经是一名非常合格的初中级后端研发工程师了,此时的你有了较为全面的技术栈并能很好的应用在实际生产中。但碰到的问题还一直是系统复杂度的提升,可能已经碰到了一些大大小小的麻烦,即便没碰到未来也肯定会碰到。

接下来就是要处理这些麻烦,要做系统优化(包括性能、运维、稳定性、扩展性和复杂度优化)的事情,做到这一步才是升级到中高级研发工程师的阶段。这个阶段就必须开始研究基础理论知识和系统中各个环节和使用的工具的细节了,要深入到原理和源码这个程度的研究,这样才有机会把系统优化的问题解决好。比如大家常听到的MySQL规范或者XX条SQL高效技巧,这些性能优化的手段都是治标不治本且效率低下,需要你以瞎猫撞死耗子的方式去尝试和实验,不去研究工具原理和源码而在应用层做实验,这是本末倒置的;再比如系统扩展性设计,前人都总结的很好了,无需自己摸索,先学好软件工程、设计模式以及常见的架构设计,再读几本类似《代码整洁之道》的书,实践好前人的总结成功,再谈创造创新。这里建议大家碰到哪个方面的计算机基础理论知识需求就学习和钻研哪方面就行,还是之前说的原则,学习必须要有正反馈,要学以致用才不会厌学。到这里还有个常见问题要解决,就是“如何高效的阅读源码”?GPT给的总结挺好的,直接贴过来。额外补充一点:多用一下GPT帮助你阅读源码。

如何高效地阅读复杂系统的源码? (以下为GPT回答)

  1. 理解系统架构和设计 在开始阅读源码之前,首先要尽可能地了解系统的架构和设计。这包括:
  • 系统的组成:了解系统由哪些模块或组件构成。
  • 模块职责:明白每个模块或组件的功能和职责。
  • 交互关系:理解不同模块间的交互方式和数据流动。
  • 设计原则和模式:识别系统设计中使用的原则和模式。
  1. 使用源码阅读工具 选择合适的源码阅读工具可以大大提高效率。这些工具可能包括:
  • IDEs:集成开发环境如IntelliJ IDEA, Visual Studio等,提供强大的源码阅读和导航功能。
  • 源码阅读器:如Sourcegraph,能够在浏览器中提供代码搜索和引用查找。
  • 版本控制工具:如Git,了解代码的历史变更和代码审查。
  1. 从概览到细节 在阅读源码时,要遵循从宏观到微观的原则:
  • 阅读文档:首先查看任何可用的文档,包括README文件、开发者指南、API文档等。
  • 代码结构:浏览代码库的结构,了解文件和目录的组织方式。
  • 入口点:找到系统的入口点,如main函数或启动脚本,开始跟踪程序的执行流程。
  • 核心模块:专注于核心模块的源码,理解它们的实现和互动。
  • 逐步深入:逐步深入到具体的函数和类,理解细节实现。
  1. 实践和实验 通过实际编码来加深对源码的理解:
  • 修改和调试:尝试修改源码并调试,看看变化如何影响系统行为。
  • 写小型功能:尝试在现有系统上添加小型功能或修复bug,这有助于理解源码的具体应用。
  • 单元测试:阅读和编写单元测试,这可以帮助你理解代码的预期行为。

5. 交流和讨论

在团队或社区中交流可以提供源码阅读的新视角:

  • 代码审查:参与代码审查,看看别人是如何理解和评价代码的。
  • 讨论区和论坛:加入相关的讨论区和论坛,与其他开发者交流心得。
  • 开发者会议:如果可能,参加开发者会议和技术交流会,直接向经验丰富的开发者学习。

6. 定期复习和总结

源码阅读是一个持续的过程,定期复习和总结所学可以巩固记忆:

  • 编写笔记:在阅读源码时编写笔记,记录关键点和个人理解。
  • 绘制图表:绘制系统架构或代码流程图,帮助可视化复杂的概念。
  • 分享所学:通过博客、讲座或教程分享你的源码阅读经验,教学相长。

通过上述方法,你可以更高效、友好地阅读和理解复杂系统的源码。记住,源码阅读是一个学习和成长的过程,不断实践和挑战自己是提高的关键。

再下一个阶段就是高级和技术专家阶段了。在这个阶段,你已经有了丰富的技术栈和工程实践经验了,对计算机基础理论和优秀工程实践的积累已经足够,此时需要

技术创造和创新 深入理解现有工具的基础上,站在巨人的肩膀上,从自身生产落地的场景出发,尝试进行技术创新,改进或开发新的解决方案,以获取已有工具无法达到的收益结果。这个阶段想要做的好,除了技术本身的要求外,更需要工程人员对业务场景的深入理解,也需要明白业务的商业模式和长远发展目标。

最后,无论你处于什么阶段,都有要求的是:持续学习和更新你的知识库,要关注新技术、新框架的发展,例如云原生技术、无服务器架构等;要阅读行业内的学术论文和技术报告,了解前沿技术和理论研究成果。