【有书共读10】《码农翻身》读书笔记第一章
线程:
线程一出生就别编了一个唯一的编号,其作用是处理系统分配给它的任务。
线程有三种状态,等待状态,就绪状态,运行状态。当线程的执行时间过长时会被cup中断从而进入等待状态,处于等待状态的线程在进入运行状态之前必须先进入就绪状态。
一个线程如果占用了大量的cpu资源将会被kill掉然后当做垃圾回收。
处于线程池中的线程在处理完任务后不会被注销,
系统重启后线程池全部销毁。
Mem***d线程可以帮助网站缓存很多用户数据而且还是分布式的。
在使用线程的时候一定要注意加锁,不然可能会出现如下问题:
(1)没有加锁的情况
(2)加锁的情况
此外还应特别注意死锁的问题,当两个线程相互等待对方占有的资源时会发生死锁,操作系统解决死锁的方法非常的简单粗暴——直接杀死其中的一个线程。当然为了避免死锁操作系统会预先规定调度算法。
TCP/IP
TCP连接是虚拟的,连接的状态信息并不会在路上保存:相反,连接的状态信息是在两端维持的。
为什么需要三次握手呢?三次握手主要是为了验证通信双方的发信和收信能力没有问题。第一次握手:客户端发信,服务器端收到了,这时服务器就明白客户端发信能力和自己的收信能力都是没有问题的。第二次握手:服务器发信客户端收到了,此时客户端明白自己的收发信能力和服务器的收发信能力都没有问题,但此时服务器端还不知道自己的发信能力和客户端的收信能力,所以需要第三次握手。第三次握手:客户端发信,服务器端收信,这样就消除了服务器端对于客户端的收信能力的担忧。两端发送的信息并不是直接到达的,中间将会经过多个路由器的转发。在传输的过程中可能会发生数据包丢失的情况,所以tcp传输采取了超时重传的机制,当经过了一定的时间还没有收到确认包的时候,发送端会重新发送数据。
CPU
我的脑容量很小,所以醒来以后只想起我的创造者告诉我的几件事情:
(1)你的工作就是运行指令
(2)你不能保存指令,你的指令全在内存里。
(3)你的第一条指令放在地址0xFFFFFFF0处。
那还有什么可说的,感觉去取第一条指令吧。
运行时我只关心两件事:
(1)我工作必备的寄存器就放在我面前的工作台上。
(2)程序计数器,我用它记住我要执行的下一条指令地址。
缓存:根据局部性原理,将经常访问的东西存放在缓存中,这样就不用每次都访问内存了,可以加快速度,因为访问内存是很慢的。
进程:
进程的诞生
我所在的计算机是个批处理系统, 每次上机时, 我和其他程序都排好队, 一个接一个的进入内存运行。又到了月末发薪水的时候, 我刚一进入内存,便看到这么一个公告:
公告
为了创建和谐社会,促进效率和公平, 充分发挥每一个人的能力,经系统***慎重研究决定:本系统自即日起,正式从“批处理系统”转为“多道程序系统”, 希望各部门通力配合,一起完成切换工作。 系统***
xxxx年xx月xx日
线程
有了进程就万事大吉了吗? 人类的欲望是无止境的,很快就出现了新情况, 举个例子来说吧,我有一个兄弟,是个文字处理软件, 他和我不一样, 他有界面, 人类在用的时候能看到, 这实在是很幸福, 不像我总是在背后默默工作,几乎无人知晓。
这哥们有个智能的小功能,就是在人类编辑文档的时候能自动保存, 防止辛辛苦苦敲的文字由于断电什么的丢掉。
可是这个功能导致了人类的抱怨, 原因很简单,自动保存文字是和IO打交道,那硬盘有多慢你也知道, 这个时候整个进程就被挂起了, 给人类的感觉就是: 程序死了,键盘和鼠标不响应了! 无法继续输入文字, 但是过一会儿就好了。
并且这种假死一会儿就会出现一次(每当自动保存的时候), 让人不胜其烦。 当然可以用两个进程来解决问题, 一个进程负责和用户交互, 另外一个进程负责自动保存, 但是,这两个进程之间完全是独立的,每个人都有自己的一亩三分地(地址空间), 完全互不知晓, 进程之间通信的开销实在是太大, 他们没有办法高效的操作那同一份文档数据。后来还是劳模阿甘想出了一招 : 可以采用多进程的伟大思想啊!
把一个进程当成一个资源的容器, 让里边运行几个轻量级的进程, 就叫线程吧, 这些线程共享进程的所有资源, 例如地址空间,全局变量,文件资源等等。
这哥们有个智能的小功能,就是在人类编辑文档的时候能自动保存, 防止辛辛苦苦敲的文字由于断电什么的丢掉。
可是这个功能导致了人类的抱怨, 原因很简单,自动保存文字是和IO打交道,那硬盘有多慢你也知道, 这个时候整个进程就被挂起了, 给人类的感觉就是: 程序死了,键盘和鼠标不响应了! 无法继续输入文字, 但是过一会儿就好了。
并且这种假死一会儿就会出现一次(每当自动保存的时候), 让人不胜其烦。 当然可以用两个进程来解决问题, 一个进程负责和用户交互, 另外一个进程负责自动保存, 但是,这两个进程之间完全是独立的,每个人都有自己的一亩三分地(地址空间), 完全互不知晓, 进程之间通信的开销实在是太大, 他们没有办法高效的操作那同一份文档数据。后来还是劳模阿甘想出了一招 : 可以采用多进程的伟大思想啊!
把一个进程当成一个资源的容器, 让里边运行几个轻量级的进程, 就叫线程吧, 这些线程共享进程的所有资源, 例如地址空间,全局变量,文件资源等等。
硬盘:
内部结构
看到没有,我有很多个盘片像串糖葫芦一样被串在一个主轴上, 主轴带着他们疯狂的旋转。
每个盘片都有很多一圈一圈的磁道, 每个磁道又分为一个一个的扇区。
多个盘片上的同一位置的磁道组成了一个柱面 (需要发挥一下你的想象力)
最后每个盘片上都有可以读写数据的磁头。所以,如果你想访问我的数据,可以说: 把0柱面, 0磁头, 1扇区的数据给我拿来。
我就把磁头挪到您指定的柱面,对每个磁盘来讲其实就是指定的磁道, 所以这叫“寻道时间”
然后再旋转磁盘,让磁头指向您指定的扇区,这才能开始读取数据, 这叫“旋转时间”,转速快的硬盘能更快的旋转到特定扇区, 所以性能会更好些。
每个盘片都有很多一圈一圈的磁道, 每个磁道又分为一个一个的扇区。
多个盘片上的同一位置的磁道组成了一个柱面 (需要发挥一下你的想象力)
最后每个盘片上都有可以读写数据的磁头。所以,如果你想访问我的数据,可以说: 把0柱面, 0磁头, 1扇区的数据给我拿来。
我就把磁头挪到您指定的柱面,对每个磁盘来讲其实就是指定的磁道, 所以这叫“寻道时间”
然后再旋转磁盘,让磁头指向您指定的扇区,这才能开始读取数据, 这叫“旋转时间”,转速快的硬盘能更快的旋转到特定扇区, 所以性能会更好些。
文件
对于绝大部分人来说,都不想去了解什么柱面,磁头,扇区这些非人的术语,所以我为懒人们专门提供了一个叫做逻辑块的方式,你看到磁盘就是有一个个“块”组成的,编号为1, 2, 3, ..n 。文件对人类来说是最小存储单位, 你想存任何东西,无论多么小,非得建个文件不可。此外为了让这个世界整洁有序, 多个文件可以放到一个目录(其实也是个特殊的文件)里, 目录之上还可以有目录,形成一个树的结构。
目录
每个文件都需要有个inode来描述, 每个目录是不是也需要一个? ”这是自然,和文件一样, 每个目录也是一个inode, 其中有目录的属性,还有存放这个目录内容的磁盘块号,在磁盘块中才真正的存放着目录下的内容“
“
举个例子来说吧: 有人要读取 /tmp/test.log 这个文件, 查找次序是这样的:根目录inode->根目录磁盘块->tmp目录inode->tmp目录磁盘块->test.log的 inode->读取磁盘块”
内存说: “我赛,这也太绕了吧,比CPU访问我的数据麻烦多了, 硬盘,你要小心点, 这要是操作不当的很容易出乱子的。”
管理空闲块
目录和文件的存储问题解决了, 接下来我需要一个大管家,把那些没有使用的、空白的、数量上亿的磁盘块给管理起来,只有这样, 新的文件来的时候,才能分配空间存储。
操作系统老大给我推荐了两位, 第一位主张是链式大法好, 无非就是把空闲磁盘块组成一个链表(又是链表!) ,但是我心里盘算了一下: 如果磁盘块号是32位的,每个块都得花费我32位的空间,如果我有5亿个空闲块, 那仅仅为了记录他们就要占用接近2G的磁盘空间! 这浪费可是有点大啊。
还有一位主张位图法, 这个方法更简单, 对每个磁盘块,如果已经被使用,那就标记为1, 没被使用就是0。 这样整个磁盘块就形成了一个由0和1组成的一个大位图。
操作系统老大给我推荐了两位, 第一位主张是链式大法好, 无非就是把空闲磁盘块组成一个链表(又是链表!) ,但是我心里盘算了一下: 如果磁盘块号是32位的,每个块都得花费我32位的空间,如果我有5亿个空闲块, 那仅仅为了记录他们就要占用接近2G的磁盘空间! 这浪费可是有点大啊。
还有一位主张位图法, 这个方法更简单, 对每个磁盘块,如果已经被使用,那就标记为1, 没被使用就是0。 这样整个磁盘块就形成了一个由0和1组成的一个大位图。
文件系统
扯了这么多,是时候看一看全局了, 在你们程序员的眼中, 其实我是长这个样子的(拿你们崇拜的Linux ext2为例):
键盘:
CPU是怎么和我们联系的?
一种办法就是CPU和每个I/O设备之间都扯一根线, 有多少个设备就扯多少根,组成了一个以CPU为中心的星型布局, 很明显这样太麻烦了, 尤其是来了新设备怎么办?
后来我们采用了“总线”这个概念, 大家都挂到这一条“总线”上, CPU想找谁了,就在上面吼一声。

一种办法就是CPU和每个I/O设备之间都扯一根线, 有多少个设备就扯多少根,组成了一个以CPU为中心的星型布局, 很明显这样太麻烦了, 尤其是来了新设备怎么办?
后来我们采用了“总线”这个概念, 大家都挂到这一条“总线”上, CPU想找谁了,就在上面吼一声。
当然这种方式也有缺点, 当一个人在总线上吼叫的时候会霸占总线, 其他人都得等待。
还有这么多设备, CPU怎么知道谁是谁?
首先肯定得给每个设备编号,比方说硬盘(更准确一点是,硬盘控制器)的编号是320 , 图形控制器的编号是 3D0 , 这个编号就被称为 IO端口。
有时候CPU会更懒, 他和内存商量好, 把我们这些IO端口映射到内存中去, 这样CPU访问我们的时候,就像访问内存地址一样了。

还有这么多设备, CPU怎么知道谁是谁?
首先肯定得给每个设备编号,比方说硬盘(更准确一点是,硬盘控制器)的编号是320 , 图形控制器的编号是 3D0 , 这个编号就被称为 IO端口。
有时候CPU会更懒, 他和内存商量好, 把我们这些IO端口映射到内存中去, 这样CPU访问我们的时候,就像访问内存地址一样了。
DMA
CPU和内存才是系统的核心, CPU运算时候只认内存这个好基友,所以所有的数据不管是谁产生的, 不管是1.5等公民硬盘,还是2等公民键盘,鼠标等, 数据统统都得搬到内存去。
这就给我们带来了一个挑战: 数据的搬运。
中断的方式对于小数据量传输是有效的, 像我是一个键盘,每次你按下一个键以后, 我就会发出一个中断告诉CPU,CPU就能发出指令,把这个一个键对应的字符搬到内存。
但是对于大数据量传输尤其是像硬盘这样的, CPU还得花费大量的时间和精力不断的发出指令,让磁盘控制器把数据从硬盘搬到内存去, 这相当于又陷入了程序式I/O的陷阱。
对于类似这样的情况,我们也有办法处理, 就是用一个DMA 控制器, 使用这个专用的处理器进行I/O设备和内存之间直接的数据传输, 脏活累活都被这个DMA给处理了。
这就给我们带来了一个挑战: 数据的搬运。
中断的方式对于小数据量传输是有效的, 像我是一个键盘,每次你按下一个键以后, 我就会发出一个中断告诉CPU,CPU就能发出指令,把这个一个键对应的字符搬到内存。
但是对于大数据量传输尤其是像硬盘这样的, CPU还得花费大量的时间和精力不断的发出指令,让磁盘控制器把数据从硬盘搬到内存去, 这相当于又陷入了程序式I/O的陷阱。
对于类似这样的情况,我们也有办法处理, 就是用一个DMA 控制器, 使用这个专用的处理器进行I/O设备和内存之间直接的数据传输, 脏活累活都被这个DMA给处理了。
未完待续.............................................................................................................................................................