ps:博客格式太难调整了,所以我将其上传到我的百度文库,我想那里的预览功能会更好。文章写得很零碎,在文库顺带看了之前自己写的博客,瞬间感觉自己的认真。工作的认真程度真不如上学的时候!!
在看这本书的时候主要参见了现代操作系统纲要
以下是读书笔记部分节选,详细可以参见百度文库。
《现代操作系统》看了两个多月才看了前面200页,很多都似懂非懂,权且将自己认为重要的概念抄下来,以备后续查看。
0. 概述
(1)操作系统的概念
对操作系统的定义,有两种说法,一种声称操作系统是计算机的扩展器,一种声称操作系统是计算机资源集的抽象。
所谓操作系统是计算机的扩展,是将操作系统当做计算机对外的接口。对外包括对应用程序,对程序员,对用户。操作系统对计算机进行“化妆”,将计算机“丑陋晦涩”的硬件对外隐藏,而向外呈现界面友好清晰,更易理解的操作系统。如下图所示:
所谓操作系统是计算机资源集的抽象,是指操作系统将计算机资源(处理器,存储器以及I/O设备等)进行抽象以及管理。将CPU处理抽象为进程,将内存抽象为地址空间,磁盘抽象成文件。而这一切抽象都是为了实现多道程序设计,即可以在一个计算机上同时运行多个互不干扰程序。
(2)操作系统的作用
操作系统的主要任务是在相互竞争的程序之间有序地控制对处理器、存储器以及其他I/O接口设备的分配。其主要任务包括管理资源分配,评估使用代价和调节资源分配的冲突,记录哪个程序在用什么资源,用多少,用多久。资源管理包括用以下两种不同方式实现多路复用:在时间上复用(进程调度: CPU 时间片轮转)和在空间上复用(内存管理:虚拟内存,页面置换;磁盘管理:文件系统)。在时间上分配CPU资源需要考虑该进程在上面运行多久,下一次切换到哪一个进程。在空间上分配存储空间需要考虑给每个进程分配多少内存,如果内存不足的时候,将哪个页面置换到磁盘以腾出空间。
操作系统的主要功能:为用户程序提供抽象和管理计算机资源。用户程序和操作系统之间的交互处理是前者。用户程序和操作系统之间的交互主要是处理抽象。对于管理计算机资源系统(进程调度,内存置换等)一般自动完成。所以主要是用户程序与操作系统的交互。用户程序通过操作系统提供的接口来访问底层的系统。操作系统提供一种特殊的过程调用——系统调用,该种过程调用可以由用户态陷入内核态对底层进行操作。比如可以创建和退出进程,打开和读写文件等。目前主要由以下几种系统调用函数——进程管理,文件管理,目录和文件系统管理以及其他。常用的系统调用可以参见下表:
(3)为什么要了解操作系统
之前跟同事说我没有修过操作系统,想补补操作系统,同事说操作系统工作中很少用,我当时想学习操作系统也跟我学习数据结构后一个感觉——很难很有趣但是工作中用不着。而老大却一再跟我说,让我打好基础,好好补补操作系统,计算机网络等基础课程。而我自己看了操作系统之后,突然恍然大悟,oh,原来学习数据结构就是为了学习操作系统呀呀呀~~(*^__^*) 嘻嘻……
瞎扯完,言归正传。我当时学习操作系统源于工作中遇到一个父子进程共享文件描述符的坑。这个坑坑得我好惨啊,因为其具有偶发性,而且完全没有日志迹象查询,只知道在入口处数据就莫名其妙丢了。之前同事也遇到过这种情况,只是定位怀疑有一段代码存在问题,总是觉得那段fork子进程的代码有问题,后来因为这个bug复现少,所以也不了了之。后来因为系统用得越来越多,这个问题复现概率大大提高,于是开始打日志,每一个操作都打日志,最后在老大的英明神武下,发现了原来是在fork子进程之前没有关闭父进程使用的队列连接。如果没有老大,估计打死我也不知道如何解决这个问题。因为我完全不知道这个基础知识。虽然后来老大跟我说了父子进程共享文件描述符的知识,也说了数据可能被子进程的队列连接socket的handle读走了。但是我还是似懂非懂,于是乎,我决定好好补补操作系统。除了因为这个坑,还因为每次老大跟我说内存,cpu,进程,线程我都没概念,所以我决定脑补下,^_^。
吐槽完总结下,了解操作系统就是为了理解计算机是如何工作的,以及程序如何运行的,从而优化程序更好地实现需求,达到预期目的。
(a)程序在运行的时候需要哪些资源,会受到哪些资源(如磁盘,内存,cpu等)限制。
比如日常程序每天导库的时候需要例行将历史记录清除,以防磁盘空间不足。
比如redis中的缓存过期数据也要定时清除,以防止redis内存不足而导致其他redis操作失败。在设置redis的max_memory的时候,也要考虑随着业务增长,对redis内存空间要求会越来越大。
比如写程序的时候也要考虑程序可能要用到的最大内存,如果使用内存超过系统默认分配内存,将导致程序出错退出。
比如使用sort命令或awk联合数组对大文件排序的时候可能导致内存使用率100%。
比如使用fork子进程的,要考虑cpu的限制,不能fork很多子进程,最好使用taskset设置空出一两个核以使机器负载不至于过高,还有fork子进程要记得exit,否则资源不释放,会导致cpu飙到100%。
比如访问同一个资源(db或者redis)的时候要考虑qps,否则会使得资源cpu使用率过高,从而使得有些资源操作失败。影响业务qps主要是用户的访问频率,除此之外在异步处理(用队列存储数据,用daemon处理数据)中,daemon数,队列数都会影响qps。
(b)分析系统的资源评价,从而使得系统优化。
考虑程序运行慢是受哪一部分的限制:CPU,内存,磁盘等。耗费这些资源过多的是磁盘IO还是网络IO。比如考虑优化网络IO,使用pipeline将数据push到队列,比一个个push要快很多,使用hashMSet比hashSet要快,比如redis的qps过高,会引起cpu占用太高,从而引起程序太慢。
(c)为防止各个程序同时访问一个资源而引起冲突,会引进互斥的概念。
比如多个任务并行处理,如何分配唯一任务id。或许有人说采用时间戳,可是当系统并行处理多个任务的时候,他们处理时差远少于1秒,1秒的间隔已经无法区分他们的时序。有人可能说采用一个共享变量,每次自增来分配task_id。但是如何保证并行处理的时候这个共享task_id唯一,不会出现冲突,这就要理解互斥。在工作之前没有任何什么锁住表,锁住redis,互斥的概念,这些对我来说都是天书。所以当工作遇到redis为了实现互斥,采用单线程。并且redis在执行一个操作的时候,会锁住redis。于是问题来了,当redis删除大数据(如6kw+长度的hash列表或者大的set,长的list)的时候,redis会被锁住很久(高达十几秒),此时对该redis的其他操作就无法执行。在工作中也遇到这个坑,很多泪就不飙了…除了这个坑,当多个程序同时访问同一个redis的时候,发生了冲突,即竞争条件,从而使得redis操作失败,并且使得下面的程序无法执行。这个时候可以考虑模拟假脱机打印机处理打印机资源冲突的方式。将对redis操作请求入队列,单独使用一个daemon来消耗队列中redis操作请求一一处理之。采用队列的方式,可以使得各redis的操作请求具有一定的时序。
(d)采用多进程,以及父子进程共享文件描述符,多线程共享地址空间都是为了充分利用系统资源,并行处理以更快速度解决问题。并行就必须考虑各个进程之间是否保有独立性,各个同时处理的数据是否具有依赖性,若有依赖性就牵涉时序问题,资源共享也往往引起冲突。
可能你的程序看起来很符合逻辑,但是在处理大文件和大量数据的时候,还符合逻辑吗?在多个相关任务并行处理的时候,还符合逻辑吗?在机器出现资源不足的时候,还符合逻辑吗?在多个程序竞争资源的时候,还符合逻辑吗?在需求变更的时候,还符合逻辑吗?
要解答这些问题,就是你要学习操作系统的原因。
要理解操作系统,就必须要理解每一个资源抽象的概念和实现,以及如何使用这些抽象来解决问题。所以下面会从cpu的抽象——进程(进程调度),内存的抽象——地址空间(内存管理),磁盘的抽象——文件(文件系统),互斥和锁这几个方面来记录学习操作系统的一些笔记和感受。
记得附上《现代操作系统》读书笔记网上版本http://wenku.baidu.com/view/593a591731b765ce0508149c.html。这样方便查看。