不忘初心------最近读专业书有感
终于开始专注读专业书了,终于从读人文社科方面的书转到专业书了,所以心情是喜悦而充实的。从7月上旬开始,如下图所示,这次从左到右。所以读书的顺序依次是《深入理解Linux网络》/《程序员的自我修养》/《go语言设计与实现》/《go程序设计语言》/《C++程序设计语言(原书第4版)》两卷,这里简写了;详细书名/作者和出版社如图。从图可以看出,有些书翻得的有些烂了,而事实上,右边的C++书至少是第三次翻阅了;而这次买的新书只有第一本,而且前面两本书是通读,其他书都是选读。接下来通读的两本书分开做总结,而其他书都是语言书,作为一部分做总结。
1. 网络与Linux内核
图中最左边的书的购买可以说是机缘巧合。之前买过Robert Love的两本Linux系统和内核的书,也因此写过不少的文章,但这两本书去年这个时候送人了。而最近看完一系列的人文科学方面的书后,是时候看专业书了,一想到的还是这两本书,所以打算网上回购,但无意中发现推荐这本书,看标题还不错,所以就买下来了。收到后,一看就没停下来了。
这本书太适合我不过了。之前看过不少Linux内核和Linux系统的书,但对网络这块特别是它的底层和背后的原理似乎有点说不清道不明的味道,但又不知道差在哪里,原来就是差这么一本书!Linux系统底层可以理解Linux内核,也就是该作者说的内功。我认为内功如下图可分成这么些块:
图二 Liunx内核与系统
该图是刚才花了点时间画出来的。就本人而言,对线程同步/内存和存储掌握的还可以,踩过很多坑当过救火队长,存储方面的应用也重构过。其实文件(系统)是Linux最核心的概念,所以有了这些经验,看linux内核的书还算比较顺利。但Linux内核对于网络是比较少涉猎,虽然之前研发过不少的协议,比如ARP/SSL/POP3/SMTP等等,但是对于其中的交互,特别是OS/内存/网卡之间交互的具体原理和细节,确实如该作者所言,市面上相关的书很难让人解渴。而作者自己动手,让我们解渴了,非常感谢作者的辛苦付出。
作者的很多观点我都非常认可,比如上面说到的内功,作者的副标题就是修练内功。面对当下越来越焦虑和越来越卷的行情,我觉得修练内功就是解药,我们需要理智的声音,而不是天天喊35岁危机制造焦虑。那为什么是解药呢,作者说内功可以让技术生命周期延长;这点我这段时间深有感触,有时间就好好修练内功!
然后作者对于技术的态度很欣赏。作者可不是一般的打破砂锅问到底,直接扒内核源码!此时好奇的想知道,作者是不是一个倔强的人呢?作者无疑是大牛,我觉得大牛的性格就应该有些倔强的。上次遇到一个很务实的老板,他说他是处女座,对他的产品追求完美。我想大佬对自己的要求都不会低,所以同样看一个技术人员牛不牛就看他对技术、对问题的态度。
再然后作者写作的思路很喜欢。整本书和每个章节都是先提问,先引导你思考;而且每个问题都有实际的场景对应,大部门来自日常的工作中,所以非常的接地气,然后寻根究底,不停的深入提问。这样的书谁不爱。
最后,我们总结一下这本书中的主要内容,该书有一个主线,就是网络上收发包的流程到底是什么,Linux内核做了什么;以此铺开内核和进程之间如何协作,CPU/内存/缓存/协议栈/网卡之间到底怎么流转数据,每个系统调用的背后到底有些什么动作,这些数据在内存到底占有多大,空闲和有负载时又是怎么样的,那么对高并发有什么样的影响,能支撑多少并发量,一条连接的客户端和服务端到底有什么区别,跨机通讯和本地通讯又有什么不同。这样把一些关键的细节描述的非常清楚,比如,软中断和硬中断,epoll与阻塞,Redis单线程的高效,Kafka的零拷贝,127.0.0.1与本机IP,三次握手,半连接和全连接队列等等。再比如使用epoll经典三步系统调用的每一步都有源码讲解和分析,三次握手的背后建立连接对应的每一步同样是源码解析且有每一步失败对应的处理。总的来说,作者对整体流程和关键细节把握的非常好,除了源码分析,还配有很多流程图。非常的接地气,通俗易懂,让人茅塞顿开,省了去趴源码的时间,当然看一遍是不够的,写着写着我又不得不再次翻阅。但我觉得还不够,希望后续有时间再常读,更希望后面有机会在具体实践中运用起来再写总结。
2. 程序员自我修养之编译原理
看完第一本书,意犹未尽,自然就关注了作者的其他文章,自然看看大牛推荐的书单,补充了些书,但是发现这本书(图一第二本《程序员的自我修养-链接、装载与库》)我有,而且买了十来年了,竟然没怎么看。这一看,又放不下了。从7月23日到8月1日看完。跟上一本书一样,全文通读,做笔记;笔记都是在书上画,所以看纸质书有这个好处。本书的内容如副标题,主要讲程序的编译、链接、装载与库。这是一本非常基础的书,所以十年前我反而看不太懂,只能稍微翻了一下就放弃了,看来我确实有些后知后觉。但也如作者的主标题想表达的,基础的东西,反而是一个程序员需要持续精进的,持续去修养的。所以今天能仔细阅读,何尝不是一件幸事。前面这两本书,都是国人写的,一方面读起来比较合口味,另一方面说明我们技术沉淀和发展是不错的。像这本书,两位主要的作者当年还是在校读硕,我觉得非常了不起;按他们的水平,我们开发一种语言,开发对应的编译器等相应的系统工具也不是不可能。
这本书就是讲程序背后的故事,我们平时主要写代码,但是代码的运行需要环境,代码的打包需要有环境;前者是运行环境,后者是编译环境,这些都离不开编译器这个工具。编译器和语言、操作系统、系统架构都是分不开的。所以讲编译器的原理,就涉及到底层很多东西,但同时很系统。内存布局包括只读区的代码区和全局区静态区,堆栈,共享区等,这就涉及到操作系统很多知识,每种操作系统都不一样,寄存器不一样,压栈也不一样,调用惯例不一样,优化不一样;每种语言还不一样。这只是一个方面,还要介绍静态链接和动态链接,中间文件、库文件和可执行文件的种种,编译和链接的不一样,参数的不一样,文件存储位置、文件格式、符号表、重定向、系统调用等等不同操作系统下又不一样。作者主要介绍Linux系统和Windows两个系统,语言主要是C和C++。最后作者还自己撸了一个迷你运行库,把前面的知识点都连贯起来,非常的好。这本书还有一个非常重要的点,也是迷你运行库的重点,讲述分析程序是如何启动的,main函数之前做了非常多的重要的工作,main函数之后同样做了很多收尾的工作。让人印象特别深刻,也就理解了全局对象和静态对象的创建和回收的时机,为什么要尽量少定义。
所以这本书非常好,对于把握 程序的前世今生 即 程序的整体性非常有用,很自然一旦程序出了问题知道怎么查看dump文件,怎么定位和解决问题。这正是我们程序员的能力和目的之一,代码可维护性,同样给程序的设计也带来了理论上的支撑,比如前面说到的全局对象和静态对象的尽量少用。还有非常多非常好的知识点,在此不再累赘,更多具体的细节请看原书。
3. go语言与C/C++语言
看上一本书的时候,买了一本《go语言设计与实现》(后面简称第三本书);2021年有个项目用的开源库是go语言开发的,当时初略的了解过,也买了几本书,其中之一见图一中的第四本;也写过一篇文章讲怎么搭建环境。巧合的是这个书开门见山的讲了编译原理,后面介绍go语言特性的时候都是围绕着编译时和运行时讲解。但该书有点厚,400页多点,而我对go语言的基础语法都不是很了解,所以我觉得先看刚提到的《go程序设计语言》(后面简称第四本书)。书名很相似,后面这本书很薄,不到300页;虽然薄一点,但每句话都不冗余甚至很经典,都值得好好思考,所以速度也不快。
第三本书的作者也是本土的,而且非常的年轻,本科毕业没几年,但是经验却很丰富,从初中阶段就开始接触C语言(看来可以让我孩子早点接触)。当然还是第四本书作者老辣,来自google,而go语言由google的三名员工发明的,所以他们应该是熟悉的;而且这个系列的书都很经典,图一最后两本C++的书,就是发明者自己著作的。当然相关的C语言书我也买了,只是这次没怎么看。
所以看Go的这两本书时,我就思考读它们的目的和目标是什么,我想主要是了解这个新语言的特性和语言的演变过程。虽然个人也很看好go语言,但我使用最多的是C++语言,所以后面基本上是同步看C++的书,如图一最后两本,这两本书反复翻过,但是C++的特性实在太多,我一般是选读或重点读。
go语言第一次发布是2009年,所以非常年轻,所以吸取了各种语言的精华。传承了C和Java的优点,剔除了C++和Java语言的繁杂。没有C++的类,继承,模板等等,但是有接口和实现;没有lambda表达式,但是有函数对象。go最耀眼的莫过于支持高并发的goroutine和通道了,goroutine中文叫做协程,和线程完全不同,更灵活可以更轻量级但也可以更重。我们一般OS的线程的栈空间为2MB,虽然可以改,但是编译器改的相对有限,当然我也看过之前公司自己开发类似协程,将线程分割成更多细粒度的,而有些开源库比如流媒体srs也实现了协程。而goroutine是原生的机制,它的栈空间默认2KB,而且可以增大或缩小,最大可达2GB。OS线程由OS内核来调度,而Go运行时包含一个自己的调度器,这个调度器使用一个称为m:n调度的技术(因为它可以复用/调度m个goroutine到n个OS线程)。这样OS调度器是由硬件时钟定期触发的,而Go调度器由特定的Go语言结构来触发的,所以之前的OS线程切换都需要上下文切换即需要消耗不少的资源,而goroutine的切换的成本低许多!而通道则是两个goroutine之间的连接通讯机制,使用起来相当方便。
所以每次看到go语言的这些特性,我都感慨语言的进化速度是非常快的,也代表了技术的进化速度之快;但同时不以人的意志为转移;我们需要的是多实践和多思考。其实有时我也在想,既然这么看好go语言,那为什么不干脆将来从事go语言好了;如果真这样,但就有点认知浅薄了。虽然C++似乎越来越重,但是其实它自身确实也在进化,现在它也有支持高并发的标准库,很简单。而且C++最强大的面向对象的思想,即类和多继承,在做复杂高效的程序时,是一种非常合适的武器;因为我曾经用过它的虚拟继承开发过复杂的业务,且高效。所以语言说到底只有合不合适,只是技术的发展,会涌现出新的语言,这是非常自然的事,就是自然的进化。所以一方面我们需要了解新技术为什么会出现,但是传统和经典的技术也需要研究和继承。事实上有了如上面说的Linux内核的基础,有C/C++语言的基础,掌握一门新的语言是非常快的,难度在于创新。比如说C++的发明人,也就是图中C++书的作者,他发明之前已经掌握当时常见的各种编程语言和脚本语言,而且是非常熟练的那种,才发明了C++;而goroutine语言的发明者何尝不是如此。所以我们可能学习一种语言比较容易,但是创新却很难,存在一定差距,差距也许就在于真正传承好了吗?基础研究做好了吗?而C++发明者1979年就加入了贝尔实验室,而这两本书都是关于C++11的,那么写这本书至少有五十多岁了,所以美国人对技术的精神值得我们学习。
读书也好,实践也罢都要回过头看看自己走过的路,别忘了当时的初心。也希望通过这段时间的读书,真正养成习惯,跟跑步一样,加油吧!