进入专业的编程领域,学习系统知识是非常关键的一部分。

首先推荐的是翻译版图书《深入理解计算机系统》,原书名为《Computer Systems A Programmer’s Perspective》。不过,这本书叫做《程序员所需要了解的计算机知识》更为合适。

本书的最大优点是为程序员描述计算机系统的实现细节,帮助其在大脑中构造一个层次型的计算机系统。从最底层的数据在内存中的表示到流水线指令的构成,到虚拟存储器,到编译系统,到动态加载库,到最后的用户态应用。通过掌握程序是如何映射到系统上,以及程序是如何执行的,你能够更好地理解程序的行为为什么是这样的,以及效率低下是如何造成的。

再强调一下,这本书是程序员必读的一本书!

然后就是美国计算机科学家 理查德·史蒂文斯(Richard Stevens) 的三套巨经典无比的书。(理查德·史蒂文斯于1999年9月1日离世,终年48岁。死因不详,有人说是滑雪意外,有人说是攀岩意外,有人说是滑翔机意外。总之,家人没有透露。大师的 个人主页 今天还可以访问。)

这几书的地位我就不多说了,你可以自己看相关的书评。但是,这三本书可能都不容易读,一方面是比较厚,另一方面是知识的密度太大了,所以,读起来有点枯燥和乏味。但是,这没办法,你得忍住。

这里要重点说一下《TCP/IP详解》这本书,是一本很奇怪的书。这本书迄今至少被 近五百篇学术论文引用过 。这本写给工程师看的书居然被各种学院派的论文来引用,也是很神奇的一件事了。而且,虽然理查德·史蒂文斯不是TCP的发明人,但是这本书中把这个协议深入浅出地讲出来,还画了几百张时序图,也是令人叹为观止了。

如果你觉得上面这几本经典书比较难啃,你可以试试下面这些通俗易懂的(当然,如果读得懂上面那三本的,下面的这些也就不需要读了)。

另外,学习网络协议不单只是看书,你最好用个抓包工具看看这些网络包是什么样的。所以,这里推荐一本书《Wireshark数据包分析实战》。在这本书中,作者结合一些简单易懂的实际网络案例,图文并茂地演示使用Wireshark进行数据包分析的技术方法,可以让我们更好地了解和学习网络协议。当然,也拥有了一定的黑客的技能。

看完《Unix高级环境编程》后,你可以趁热打铁看看《Linux/Unix系统编程手册》或是罗伯特·拉姆(Robert Love)的 Linux System Programming 英文电子版 。其中文翻译版Linux系统编程 也值得一读,虽然和《Unix高级环境编程》很像,不过其主要突出的是Linux的一些关键技术和相关的系统调用。

关于TCP的东西,你还可以看看下面这一系列的文章。

对于系统知识,我认为主要有以下一些学习要点。

  • 用这些系统知识操作一下文件系统,实现一个可以拷贝目录树的小程序。
  • 用fork / wait / waitpid写一个多进程的程序,用pthread写一个多线程带同步或互斥的程序。比如,多进程购票的程序。
  • 用signal / kill / raise / alarm / pause / sigprocmask实现一个多进程间的信号量通信的程序。
  • 学会使用gcc和gdb来编程和调试程序(参看我的《用gdb调试程序)。
  • 学会使用makefile来编译程序(参看我的《跟我一起写makefile十一十二十三十四)。
  • Socket的进程间通信。用C语言写一个1对1的聊天小程序,或是一个简单的HTTP服务器。

# C10K 问题

然后,当你读完《Unix网络编程》后,千万要去读一下 “C10K Problem中文翻译版)”。提出这个问题的人叫丹·凯格尔(Dan Kegel),目前工作在美国Google公司。

他从1978年起开始接触计算机编程,是Winetricks的作者,也是Wine 1.0的管理员,同时也是Crosstool( 一个让 gcc/glibc 编译器更易用的工具套件)的作者。还是Java JSR 51规范的提交者并参与编写了Java平台的NIO和文件锁,同时参与了RFC 5128标准中有关NAT穿越(P2P打洞)技术的描述和定义。

C10K问题本质上是操作系统处理大并发请求的问题。对于Web时代的操作系统而言,对于客户端过来的大量的并发请求,需要创建相应的服务进程或线程。这些进程或线程多了,导致数据拷贝频繁(缓存I/O、内核将数据拷贝到用户进程空间、阻塞), 进程/线程上下文切换消耗大,从而导致资源被耗尽而崩溃。这就是C10K问题的本质。

了解这个问题,并了解操作系统是如何通过多路复用的技术来解决这个问题的,有助于你了解各种I/O和异步模型,这对于你未来的编程和架构能力是相当重要的。

另外,现在,整个世界都在解决C10M问题,推荐看看 The Secret To 10 Million Concurrent Connections -The Kernel Is The Problem, Not The Solution 一文。

# 实践项目

我们已经学习完了编程语言、理论学科和系统知识三部分内容,下面就来做几个实践项目,小试牛刀一下。实现语言可以用C、C++或Java。

实现一个telnet版本的聊天服务器,主要有以下需求。

  • 每个客户端可以用使用telnet ip:port的方式连接到服务器上。
  • 新连接需要用用户名和密码登录,如果没有,则需要注册一个。
  • 然后可以选择一个聊天室加入聊天。
  • 管理员有权创建或删除聊天室,普通人员只有加入、退出、查询聊天室的权力。
  • 聊天室需要有人数限制,每个人发出来的话,其它所有的人都要能看得到。

实现一个简单的HTTP服务器,主要有以下需求。

  • 解释浏览器传来的HTTP协议,只需要处理URL path。
  • 然后把所代理的目录列出来。
  • 在浏览器上可以浏览目录里的文件和下级目录。
  • 如果点击文件,则把文件打开传给浏览器(浏览器能够自动显示图片、PDF,或HTML、CSS、JavaScript以及文本文件)。
  • 如果点击子目录,则进入到子目录中,并把子目录中的文件列出来。

实现一个生产者/消费者消息队列服务,主要有以下需求。

  • 消息队列采用一个Ring-buffer的数据结构。
  • 可以有多个topic供生产者写入消息及消费者取出消息。
  • 需要支持多个生产者并发写。
  • 需要支持多个消费者消费消息(只要有一个消费者成功处理消息就可以删除消息)。
  • 消息队列要做到不丢数据(要把消息持久化下来)。
  • 能做到性能很高。

# 小结

到今天,我们已经学习完了专业编程方面最为重要的三部分内容:编程语言、理论学科和系统知识,我们针对这些内容做个小结。如果想看完我推荐的那些书和知识,并能理解和掌握,我估计怎么也得需要4-5年的时间。嗯,是的,就是一个计算机科学系科班出身的程序员需要学习的一些东西。这其中,最重要的是下面这几点。

编程语言。以工业级的C、C++、Java这三门语言为主,这三门语言才是真正算得上工业级的编程语言,因为有工业级的标准化组织在控制着这几门语言,而且也有工业级的企业应用。尤其是Java,还衍生出了大量的企业级架构上的开源生态。你至少需要掌握C语言和Java语言,这对你以后面对各式各样的编程语言是非常重要的。

此外,还推荐学习Go语言,它已成为云计算领域事实上的标准语言,尤其是在Docker、Kubernetes等项目中。而且,Go语言在国内外一些知名公司中有了一定的应用和实践,并且其生态圈也越来越好。

算法和数据结构。这个太重要了,尤其是最基础的算法和数据结构,这是任何一个称职的程序员都需要学习和掌握的。你必需要掌握。

计算机的相关系统。你至少要掌握三个系统的基础知识,一个是操作系统,一个是网络系统,还有一个是数据库系统。它们分别代表着计算机基础构架的三大件——计算、存储、网络。

如果你能够走到这里,把前面的那些知识都了解了(不用精通,因为精通是需要时间和实践来慢慢锤炼出来的,所以,你也不用着急),那么你已经是一个非常非常合格的程序员了,而且你的潜力和可能性是非常非常高的。

如果经历过这些比较枯燥的理论知识,而且你还能有热情和成就感,那么我要恭喜你了。因为你已经超过了绝大多数人,而且还是排在上游的比较抢手的程序员了。我相信你至少可以找到年薪50万以上的工作了。

但是,你还需要很多的经验或是一些实践,以及一些大系统大项目的实际动手的经验。没关系,我们后面会有教你怎么实操的方法和攻略。

但是,往后面走,你需要开始需要术业有专攻了。下面给一些建议的方向。

  • 底层方向:操作系统、文件系统、数据库、网络……
  • 架构方向:分布式系统架构、微服务、DevOps、Cloud Native……
  • 数据方向:大数据、机器学习、人工智能……
  • 前端方向:你对用户体验或是交互更感兴趣,那么你走前端的路吧。
  • 其它方向:比如,安全开发、运维开发、嵌入式开发……

这些方向你要仔细选择,因为一旦选好,就要勇往直前地走下去,当然,你要回头转别的方向也没什么问题,因为你有前面的这些基础知识在身,所以,不用害怕。只是不同的方向上会有不同的经验积累,经验积累是看书看不来的,这个是转方向的成本。

下篇文章,我们将进入《软件设计篇》。敬请期待。

下面是《程序员练级攻略(2018)》系列文章的目录。