CS基础课不完全自学指南
阅读原文时间:2022年05月01日阅读:1

本文讲的是计算机学生怎么自学专业课,说长点就是该如何借助网络上已有的高质量学习资源(主要是公开课)来系统性的来点亮自己的CS技能树。这篇文章完全就是一篇自学性质的指南,需要对编程充满热情,起码觉得编程是件乐事。兴趣是大前提,后面讲解的很多知识都是比较“硬核”的,保持热情才能啃下去。

下面是小作文时间,不想看可以活用博客的悬浮目录功能跳到“分科目讨论”章节处:

这篇文章本来是写给 ACM 集训队里的学弟学妹们看的,一开始觉得写的并不是非常好,所以只在集训队内部分享过。后来发现虽然是烂文,但多多少少却是帮到了几个人,又觉得还是有点价值的,所以干脆丢到博客上来了。 -- 2022.4.30

最近几个打 ACM 的老队友交流时感叹,大家在大一大二时大部分精力都投入在了算法和数据结构的练习中,又因为学校课程缺少深度、缺少实操、缺少乐趣等原因,没有把专业技能树点开,对操作系统原理、计算机网络、数据库原理等专业科目了解不够深入。

但这些东西都是技术债,早晚要还的。实习前我还觉得有些知识是造火箭用的屠龙术,实习后发现如果只是图着混口饭吃那也确实是屠龙术,但稍微深入一点的工作那就不可避免地要和这些知识打交道。实习时被一致性哈希、单节点崩溃这些东西搞得满头雾水,实习完才发现这些东西都在分布式系统这门课都有详细讨论。还有次面试时被问到有没有看过 Linux 源码里 TCP 的 RTT 是怎么计算的,我当时被吓了一跳,根本没料到会问到这个深度,后面才了解到对方正在做用户态网络协议栈方面的工作,问的都是真的要用的知识。

如果大学真的有四年,那两年打 ACM,两年恶补专业课还挺合理的。但实际的时间点和预期不太一样,大三下学期一开始,找工作的要准备面试,考研的要专心复习,可自由支配的时间会一下少很多,很难抽出大块儿的时间来补这一块儿知识空洞。等找好工作或者考完研又突然多了一大把空闲时间,此时再回过头学虽然也可以,但总有点亡羊补牢的味道。

学校教授内容跟业界需求脱节这个现象在很多大学都存在,属于一种普遍性问题了。你可说这背后的原因有教育资源分布不均、高校教师评价体系重科研轻教学,但这些问题短期内也看不到根本性的改变,短期内的目标是实现自救,把自己的专业水平先提升上去。

但具体到怎么做上,还是没多少前人的经验能做参考。两年前我跟 ACM 的队友们探讨过:“我们学校这支刚组建一年的 ACM 队伍和别的学校组建多年的队伍差距为什么这么大?”当时讨论的结果是差在“沉淀”上,但沉淀这东西虚无缥缈,没人说的出来到底沉淀了啥。直到自己找工作时才意识到所谓的沉淀,就是一代代前人用自己的血泪填满的坑。有的坑前人填满了,可以踏着他们的尸体前进;有的坑前人没填,那只能自己跳进去当垫脚石,让后人不要重蹈覆辙了。

世界上本来只有坑,掉坑的人多了,也就变成了路。

​ ——我说的

这年头网上不缺各种学习资料,但问题是选择太多了,糟粕也太多了。这篇文章我就是按照一套思路和我自己的学习经验来筛选出一些计算机基础课学习资料。

筛选资料时主要考量有:

1)保证真实性:除了少部分特别注明的资料外,所有资料都是我自己学习经历整理出来的,即这些资料都是我实际学过一遍的,并且学习顺序大体上不变。不过因为个人基础不同,这些资料并不一定适合每个读者。

2)实践驱动:对于自学者来说能不能按照“理论 => 实践 => 总结”这个循环来推进,用写玩具项目、做 lab 的形式不断加深自己对知识的理解。

理论:字面意思上学习理论知识。主要考虑这门课的资料对自学者来说是否足够通俗易懂。

实践:主要说的就是编码 + 测试。这也是很多学校教授操作系统之类的课程所忽略的一环,计算机科学是应用性很强的一个领域,应该多敲代码来加深对理论的理解。所以优先选择那些带 lab (编程实验)的学习资料。测试数据也很重要,自学没有老师给批作业,所以至少要找点“参考答案”直到自己对知识的理解正不正确。

总结:知识的沉淀,直白点讲就是记笔记,人脑不靠谱,有些问题学习完后了几个月内还记得,但过一两年就忘了。你在我的博客里可以看到 ucore 和 chcore 两份讲操作系统的笔记,两份笔记相隔一年,是因为我大二初学操作系统觉得自己记性很好,所以笔记也记的蜻蜓点水。一年后再看发现很多重要的问题都没有详细记录,所以痛定思痛认认真真的重学了一遍。你点开几篇文章也可以看到两份笔记质量相差是非常大的。

3)实用性:考虑到部分读者有可能这篇文章才学了一半多,就得准备大三的三四月春招实习季了,所以优先选取那些能够把做 Lab 作为简历上的一个项目那种难度的课程。既满足了找工作的需要,也是自己学习成果的一个表现,属于是一举两得了。

另外前几门课基本都是全中文的,越往后英文比例会越高,主要是英文水平提升后敢去尝试些没有汉化的前沿玩意儿了。如果你已经有阅读大部头英文原著的能力的话那么 这篇文章 里的学习路线可能更适合你 。

碎碎念:这篇文章没有写完,在这贴个永久链接,不至于被爬虫爬了后找不到原文 https://www.cnblogs.com/kangyupl/p/cs-stack-self-study-guide.html

构建知识库

还是说一说记笔记这个事儿。记笔记的主要目的有二:

1)第二次遇到同样的问题能快速运用已有经验解决掉。小的比如某个程序要怎么配置,大的比如“进程的 CPU 占用率突增该怎么分析?”。这些东西虽然网上慢慢搜也能搜到,但还是不如翻自己的笔记来的快。不一定要自己从头手写一遍,收藏下前人写的博客链接也是一种记笔记的方法,重点是能不能快速翻阅知识库找到解决同样的问题的方法。

2)用来审视自己的知识体系,知道自己哪块儿薄弱。知识体系的构建是一个滚雪球的过程,最开始记笔记时只是零零散散的几篇,后来记得多了就开始分类摆放,慢慢的就有了一个体系的样子。

具体用什么工具记录完全看个人,喜欢搭博客就用博客记,追求方便可以用各种商业云笔记软件。我自己的考量是纯 markdown 方便迁移、云端本地双备份(断网也能看)、手机上也能看,所以在本地创建了一堆 markdown 文件,写的话就用 Typora,云端备份坚果云

如果你想要画几个技术文章里那种常见的流程图或者程序框图,我推荐使用 draw.io。既有网页版也有桌面版。

程序设计语言 & 数据结构

开头说了本文并不是完全面向小白的指南,所以这里只提一下想要按我这个学习路子走的话你目前应该有的水平:

会用一点 C 语言 + STL,然后浏览一下《C++ primer plus》学学 C++ 面向对象的语法(会继承,会用 RAII 管理内存),目前当成一本字典过一遍,后面用到知道去翻哪部分就行。

数据结构的能力也不好界定,我只是举个例子:能对着最小堆的百科一个小时复现一个出来就足够了。可以随机生成些数据和 C++ 的优先队列比较比较,结果相同就算是复现成功了。

这里我也给不了什么学习经验,我是在打 ACM 的过程中积累数据结构基础的,入门用的《算法竞赛入门经典》,对于不打算参加算法竞赛的人来说单纯为了学习数据结构看这本书是很划不来的。

基础工具

如果你已经有了一定的 Linux 和 git 使用经验,可以直接跳过这一部分内容。

不管是平常逛知乎还是逛 github,你基本上可以发现似乎是个程序员就会用 Linux 和 git。这属于那种早晚得学,但并没有专门的一门课讲的知识,运气好的可能在老师讲操作系统课时教一下,运气差的可能大四毕业也没碰过(我们专业就是后者)。所以在这里我要提一下,如果你没接触过的话那就先去把这两样工具给学了。

因为都是工具,没什么艰深的理论,跟着视频或者书籍敲一遍基本就能学会。B 站上这类“Linux 教程”或者“git 教程”一抓一大把,挑自己看得进去的跟着学就行,要有选择困难症的话可以用我下面推荐的(并不是最好的,只是我当时入门用过的)

Linux 基础操作

参考耗时:10h+

前置技能:无

个人推荐 蓝桥云课的 Linux 在线实验课

学习的重点就是关注各种命令的用法,目前不需要研究 Linux 的运行原理,知道怎么打开目录、怎么复制移动重命名文件、怎么压缩解压、怎么用 vi 或者 Nano 编辑文件(查找复制剪切粘贴保存退出就够用了,不需要记太多命令)就差不多够用了。其他的命令用到了再去重学,一口气全学完了反而容易忘。

此外还需要学着在 VMware 或 VirtualBox 上手动安装一个 Linux 发行版,推荐装最新版的 Ubuntu,主要好处是用的人多,参考资料网上一抓一大把。(你应该会用 B 站搜索“VMware 安装 Ubuntu”之类的问题吧?)有台虚拟机这步是必须的,后面学习组成原理、计网的代码都只能在 Linux 上跑。当然如果你有能力在实体机上装 Linux 或者在 Windows 里装 WSL 都可以。

git 基础操作

参考耗时:< 10h

前置技能:无

个人推荐 廖雪峰老师的 git 教程

学会怎么在 github 上创建仓库,怎么 push、pull、add、commit、创建切换合并分支基本就够了。

汇编语言

参考耗时: 10h+

前置技能:C 语言

我们编译好的程序都是以二进制机器码形式在操作系统上跑的,而与机器码最接近的就是汇编语言了。

所以要理解操作系统首先要理解汇编,我个人推荐直接 10 ~ 20 个小时啃完王爽写的《汇编语言》。每章节结尾有一道比较大的编程题,下载一个 emu8086 就可以愉快的编写和运行汇编代码了。

注意把每章的大编程写一遍这一步可不是建议,而是为了后面能理解操作系统内核源码和理解 CPU 里怎么运行机器码所必须的一步。

操作系统

参考耗时: 100h+

前置技能:汇编语言、C 语言、Linux 常用命令

注意:我个人是先学的操作系统再学的组成原理,但现在回想似乎当年学操作系统学的不是很顺利。如果读者按我这个顺序来学也觉得力不从心可以先去学学组成原理

学习操作系统解决的最大问题是“理解程序是怎么在计算机上运行的”。很多的计算机问题最终都会指向操作系统中,比如 “CPU 占用率 100% 为什么会卡?”, “malloc 是怎么分配内存的?”,“任务管理器里的进程是什么意思?”,“为什么 C 语言函数里面不能定义大数组的局部变量?”,“我的程序可以修改其他程序的内存吗?”,“多线程是怎么实现的?”。这些东西认真学完操作系统后基本都可以搞明白。

我个人推荐清华大学陈渝老师的开源的教学级操作系统 ucore。

理由一是陈渝老师录的公开课通俗易懂,我们这些普通学生也能轻松的看明白。

理由二 ucore 本身就是为了教学研发的,所以源代码写的对初学者比较友好,并且能够在 Linux 下编译并在 qemu 模拟器上运行,能切实看到自己写的操作系统跑起来所带来的反馈感是非常强烈的。

主要方法的学习方法就是:看公开课 => 跟着实验指导书完成对应 lab => 输出笔记。记笔记这点很重要,我亲身感受是看某个软件系统的源码这种事情,你不做笔记的话两三个月就忘光光了。总的来讲看内核源码的过程还是挺困难的,但同时也是有一种攻克难关的乐趣存在的。就种打《黑暗之魂》的感觉,读一小段代码就卡住了,回头翻一翻实验指导书、看一看课程视频,又突然觉得自己醍醐灌顶了。整个学习过程就是不断地“卡壳->回头思考下->豁然开朗”不断的循环。

公开课的视频,注册一下就能看了

官方的实验指导书,在线版的。在 Lab0 章节里提供了一个 VirtualBox 下的 Linux 实验镜像,可以省下安装各种依赖的力气了。

实验指导书的 github 仓库,需要离线版本的实验指导书的话就 clone 到本地,然后研究下搜索下怎么本地部署 gitbook 就能阅读了。

ucore 的源代码,master 分支下才是 C 语言的本体,clone 下来记得切一下

参考的教材,这个东西陈老师在公开课第一章里提到过,有需要可以去翻一翻。

更进阶的可以去阅读《深入理解 Linux 内核》,对哪块儿感兴趣就学习哪块儿。

组成原理

参考耗时: 100h+

前置技能:汇编语言、Linux 常用命令

我个人认为学习组成原理的主要目标是要搞明白 CPU 内部是怎么工作的,并能够针对现代 CPU 的 TLB、多级缓存、内存屏障等特性写出更优良的代码。

我个人推荐《深入理解计算机系统》(国内俗称 CSAPP)这本书。原教旨派认为的学习组成原理应该以实现简单的多级流水线 CPU 为目标,这跟我的着重点是不太相同的,我更关心说程序员可以根据哪些编程的原则来更好的利用 CPU 的特性,而 CSAPP 做到了,它真的是在认真教你怎么榨取 CPU 性能、怎么对自己的程序做优化,而且还出了好几个 LAB 专门考察你的理解够不够深刻。

读完它的前九章就差不多了,其他章节在操作系统课和计算机网络课里会更详细的学习,但如果想更轻松上手这两门课络也可以先读一下。也不需要死板的啃一遍,遇到不感兴趣的地方可以跳过,比如我觉得第二章手推浮点数的二进制表达那一堆计算题挺无聊的,所以只是写了点代码实现了字面上的小数和浮点数间的互转算法就掠过了。

书的作者开过 CMU 15213 这门课, B 站上有人给全汉化了,有需求可以去看看。

配套 lab 也是公开在网上的。注意每个实验的源码在压缩包里,参考文档在 writeup 里。

书里面的习题可以不做,但 lab 是必做的,因为它的设计确实巧妙。比如 Bomb Lab 直接扔给你一个可执行文件让你通过反汇编来 hack 掉它,比如 Performance Lab 里教你怎么运用 CPU 指令并行和 cache 来把程序的运行速度提高几十倍,这是别的课程里从未见过的奇妙体验。在攻略 CSAPP 的 lab 的过程中可以学到的知识包括 gdb 调试、分析程序反汇编码、CPU 级的性能优化方法,再加上一点点的 Linux 系统编程。

进阶:实现一个 CPU

参考耗时:10h+

前置技能:无(只是看下面这本书的话,你甚至可以不懂组成原理)

如果你想要想要更近一步实现一个 CPU 玩玩的话也有相关资料:

图一乐级别的参考书是《计算机系统要素-从零开始构建现代计算机》。它会教你用硬件编程语言 HCL 实现一个没有分支预测、没有流水线的极简化的 CPU。

配套源码

配套实验手册,分章节的

当然这个 CPU 只是图一乐的产物,甚至不及部分学校期末课设的难度。想要更进一步请自行上网搜索自制 CPU 相关资料。(我的待读列表里有本《自己动手写CPU》,不过我还没仔细看,这里只是提一嘴有兴趣的可以去看看)

网络

参考耗时:40h+

前置技能:无

学习计算机网络是为了……在座的应该没有不上网的人吧?那行了,这年头是个程序员都得懂点网络编程,所以学吧。

国内虽然有些计网的公开课讲的比较通俗,但动手的练习太少了。重点是要写点代码做做实验,只是几道填空计算题并不能算是有效的练习。我个人推荐阅读《计算机网络:自顶向下方法》这本书。

理由一网络栈分好几层,市面上很多教材都从硬件往应用的层次来讲的,一上来就搞得比较枯燥。从应用层开讲的话更容易理解。

理由二它的配套实验还是比较多的,有的会让你去模拟一下网络协议里的部分算法,有的需要抓包来切实的研究下数据在网络中传输的模样。

要更加深入理解计算机网络的话我个人去实现点小项目,比如写个 HTTP 服务器或者写个 TCP 协议原型。

进阶一:实现一个 HTTP 服务器

参考耗时:50h+

前置技能:C++、操作系统原理、计算机网络、简单的 Linux 系统编程(把 CSAPP 里的 shell lab 做完就行)

我个人推荐书籍是《Linux 高性能服务器编程》,游双老师写的。它会同时教你 Linux 编程、计算机网络、多线程编程多个方面的知识。书籍的章节分的也很不错,循序渐进的从连接的建立,到请求的解析,到线程池的维护,到使用多路复用技术维护多个连接,最后把这些模块组装起来得到一个 HTTP 服务器。

进阶二:实现一个 TCP 协议原型

参考耗时:50h+

前置技能:C++、理解 TCP 协议、简单的 Linux 系统编程

这方面主要是斯坦福的 CS 144 这门课,它以实验驱动的方式教你怎么实现一个 TCP 协议原型。

视频的话,我并没有看 CS 144 的官方录屏,也没感觉说做 Lab 有啥影响。做之前把《计算机网络:自定向下方法》里关于 TCP 的章节通读一遍,并在做 Lab4 前重点搞明白 《Linux 高性能服务器编程》里 TCP 的各个状态间是怎么转移,然后开撸就行。

数据库

SQL 基本操作

参考耗时:< 10h

前置技能:无

首先要对数据库的功能有个基本的认知,本着实践导向的原则,我推荐在 Linux 里装个 MySQL,然后跟着《MySQL 必知必会》把指令都敲一敲,增删改查交并补这些基本操作都学会了就行。

MySQL 原理

参考耗时:20h+

前置技能:SQL 基础操作

学会 SQL 的基本操作后可以去读一读 《MySQL 技术内幕 : InnoDB 存储引擎》,了解下 MySQL 里一些功能是怎么实现的。

看不下去也可以先去看后面的 CMU 15445,后者注重数据库原理学术界的一些做法,前者则是具体拿了数据库来分析,两者间互为补充。我先看的 CMU 15445,那时候虽然懂 SQL 怎么用,看是能看懂,但没什么太深感受。看完 InnoDB 后再看感觉就完全不同了,因为 CMU 15445 里讲的聚簇索引、缓冲池等等正式 InnoDB 实际用到的东西 ,再看 CMU 15445 就能理解 InnoDB 里有些地方为什么这么设计,有哪些权衡,其他的数据库权衡后又怎么设计,有了一种豁然开朗的快感。

数据库系统

参考耗时:100h+

前置技能:操作系统、MySQL 原理(推荐、非必须)

推荐学习资料为 CMU 15445 ,2021 版的没有汉化,不过经过前面几门课磨练后应该具备一定英文水平了,开着 AI 字幕看也不是特别困难。

这门课的老师 Andy 功力深厚,回答学生问题、课堂举例子都各种旁征博引。配套 project 也非常给力,会教你自底向上搭建一个简单的硬盘型数据库。

参考教材是 《数据库系统概念》,CMU 15445 的官网上有个 schedule 写了每节课对应的教材章节。

project 里会用到简单的 C++ 多线程编程知识,需要补充这部分短板可以去看网友翻译的《C++ 并发编程实战》,里面有一章会教你怎么设计并发安全的数据结构。

未完待续,有生之年有空再写

最近网上冲浪也发现了几个其他网站也有专门的 CS 自学 Roadmap,感觉质量更上一层,直接贴在这里了。

TeachYourselfCS

CSDIY WIKI