你也许会很疑惑,这里明明说基础知识,为什么要把一门编程语言单独列出来呢?因为在我看来,没有比 C 语言更适合用来理解计算机系统了。我们后面将会提到的操作系统、体系结构 这些东西非常适合用 C 语言去理解或者去实践。并且 C 语言本身的语言特性非常少,但是想学好又是不容易,很多人都觉得 C 语言难,难在哪里呢?回想了一下我大一时的感受:
简陋的标准库,几乎没有可用的数据结构和算法,什么都得自己来
指针很难理解和使用
需要了解汇编、链接、装载、内存等才能把 C 语言用好
不巧的是,这些东西正是计算机系统知识的一部分,所以用 C 语言作为学习计算机系统知识是最有效率的方式。真的很难想象用 Java 或是 Python 去给别人讲解内存,因为这些语言抽象程度都比 C 语言高,意味着离计算机系统也就越远。在 TIOBE 编程语言排行榜上,C语言几乎永远占据前三位,其地位自然毋庸置疑。而且几乎你开发中用到的很多东西都是用C语言编写的,Linux、Nginx、Redis、MySQL、Git......或许你会想要探究下原理,阅读点这些开源软件的源码,那么 C 语言也是你必备的瑞士军刀。深入学习 C 语言,能够了解计算机底层的执行原理,是理解程序运行机制的绝佳语言,无出其右。在这里,不得不引用对C语言最经典的总结:
我们编程的 IDE、写出来的程序全部都需要运行在操作系统上,说操作系统是计算机软件的基石也不为过。程序运行起来就需要创建进程,这涉及到操作系统的进程管理;写程序需要定义变量、存储数据吧,这又涉及到内存,对应内存管理;有时候我们还需要读写文件,这又离不开和文件系统打交道;你需要学习使用锁、条件变量、临界区来保证程序并发执行时不会错乱。而读写文件、分配内存这些又离不开系统调用(System call)。并且当你真正做起工程就会发现,很多问题是和操作系统紧密相关的,不理解操作系统,你连问题的原因都分析不出来。比如前段时间我们出现的在基于协程(libco)的框架下,使用多线程的锁去做同步互斥偶发死锁,后来分析才发现原因:由于协程是应用层实现的,一个线程内多个协程对于操作系统是感知不到的:那么当一个协称 A 上锁后发起网络 IO 请求,这个时候会被切换到另外一个协程B,而协程 B 又去请求这个锁。那么这个时候操作系统会认为这个锁已经被上了,因此会将协程 B 对应的线程挂起到等待队列,这样的话就导致协程 A 永远无法运行,也就无法释放锁,导致死锁。解决的方法也很简单,就是将锁设置为可重入锁,可重入意味着同一个线程多次去请求同一个锁不会导致挂起。这样当协程 B 再去请求锁的时候,操作系统就会认为协程 B 所在的线程已经持有这个锁了,直接返回,继续执行。总之,我们写程序每时每刻都在和操作系统交互,没有理由不学好。
上面说的都是软件层面,体系结构则是关于计算机是如何工作的,你会了解到典型的存储程序计算机是怎样运转的。记得南大有个老师说过 “我们不是学习使用计算机的,而是学习如何造计算机”,虽然造计算机有点夸张,但是至少我们得了解下计算机的实现原理,了解下代码是怎么被 CPU 执行的吧?不然其实你会很困惑,明明一堆英文字母,怎么在 CPU 这种电路上跑起来的,我大一学 C 语言就百思不得其解,直到后来学了组成原理和数字逻辑。我们说计算机中一切都是 0、1,0、1 又是通过高低电平来表达的,通过与、或、非等逻辑门电路来表达二进制的数值运算,再将这些简单的电路集成在一起,就形成了 ALU 等具有运算能力的处理器。你会看到一条指令是如何被CPU执行的,CPU 从内存或 Cache 中取出指令,放入指令寄存器,并对指令译码。译码就是按照指令的编码规则,将指令拆分成一系列的微操作和操作数。然后发出各种设备控制指令,执行微操作。这样就完成一条指令的执行。我们说学完编译原理,能够明白写的英文代码是如何被变成二进制指令的,学完操作系统能搞懂二进制程序是如何被链接在一起,又是如何被操作系统加载、执行的。而组成原理则会告诉你二进制指令是如何控制 CPU 跑起来的,我们的操作系统本质上也是一个二进制的程序。当你理解了计算机存储层次结构,理解了多级 Cache,你就会通过优化数据访问方式来编写出速度更快的程序。你会学到底层体系结构对 C 这些语言的栈帧和参数传递的支持,参数是如何被传递给另外一个函数的?函数的返回值又是如何拿到。这是学习组成原理对于写代码的意义。学这些到底有什么意义?你会完整的看到写的代码如何变成二进制指令,又是如何去控制各种门电路,最后变成屏幕上花花绿绿的程序的(当然这里可能还需要学习显示器的原理),这就是我们常说的“基础”和“原理”。并且计算机体系结构中的很多思想,是能够广泛运用于现代软件开发的,比如 CPU 的多级 Cache 思想,就是我们现在服务器开发中提高并发度常用的缓存技术,包括缓存的替换策略等等。当计算机对你不再是黑盒,你了解写下的代码到执行的每一步,而这也将成为你以后的核心竞争力,作为科班毕业生不应该只会使用 Java、Redis、Mysql、Spring 来写各种网站。如果读者里有半路转行或者从培训班出来的,也希望你们能够抽出空余时间去补补这些基础课,这会让你在编程这条路上走的更远和更稳。