zoukankan      html  css  js  c++  java
  • 通过Linux理解操作系统(一):概述

    通过Linux理解操作系统(一):概述

    通过Linux理解操作系统(一):概述

    用了那么多年电脑,操作系统从WinXPVista, 再到Win7, 然后是现在用的Ubuntu,这么长的时间里,一直没有搞明白这操作系统是个什么东西,为什么这么神奇,只要点一点,按一按,那些一块一块的硬件就可以完成我们的工作。直到学了操作系统这门课程,才开始有点朦朦胧胧的理解,最近又看了一些linux系统设计的资料,觉得有些领悟,所以写出来跟大家分享一下。

    先声明,本人不是linux技术极客,所以本文不会讲一些很酷的linux使用技术,也不会讲一些很深入的linux内核分析,这些都有相应的书籍和资料可以学习,比如鸟哥的linux私房菜和Linux内核源码剖析,我只是希望通过linux这样一个开源的操作系统实例来帮助像我一样的菜鸟们理解操作系统这个东西,更多地是从系统本身的设计,数据结构,算法,代码来讲,如何实现了I/O,进程管理,内存管理这些东西,有讲错的地方请指正或者补充。(还有请轻喷~)

    1、操作系统究竟是个神马东西

    操作系统 (英语:Operating System,简称OS )是管理计算机 硬件与软件资源的计算机程序 ,同时也是计算机系统的内核与基石。 操作系统需要处理如管理与配置内存 、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。 操作系统也提供一个让用户与系统交互的操作界面。(摘自维基百科)

    以上定义言简意赅,但是看起来还是云里雾里,好像知道了是这么个东西,其实心里疑问多多。其实说白了,抛开所有面纱,所有特殊地位,它就是一个计算机程序,它一样是通过源代码编译出来的,一样是由程序设计语言,数据结构,算法支撑起来的,它的目的就是管理计算机的硬件资源(硬盘,屏幕,键盘,鼠标,打印机,网卡等等)和软件资源(系统上运行的程序,像浏览器,播放器,文本编辑器,游戏等等),而这个目的的目的最终还是为用户服务,用户通过它,就只要点一点,按一按,高级一点的就输入一些命令,写写脚本什么的就能够完成那么多复杂的任务(你能想象自己去控制那么多硬件计算吗,想想电路实验的时候各种插线拔线吧~)。意识到这一点,我们才能抛开对它的恐惧,来好好的研究一下它。

    2Linux的历史发展

    linux的历史已经要被讲烂了,几乎所有的资料里都会花很大的篇幅来讲linux的发展,但是为了后面的理解需要,这里还是要简单提一提,详细的可以参阅:鸟哥的linux私房菜这个站点:http://linux.vbird.org/linux_basic/0110whatislinux.php

    提到Linux,就不得不提到Unix,因为linux就是由unix发展而来的。unix最早诞生与20世纪四五十年代,是贝尔实验室的Ken Thompson在一台叫PDP-7的机器上写出来的,这个东西出来以后他的同事都被这个系统的能力感到惊讶,因为在此之前操作计算机是很痛苦的,(这里涉及到计算机的历史,就不展开了)。很多人开始加入到Unix的开发中,而由于这个系统一开始是用汇编写的,这意味着要把它用在其他机器上,就必须在新的机器上重新编写整个系统。怎么办呢?学过编译原理的同学就知道应该使用一种高级语言来编写这个程序,然后在不同的机器上通过编译器将其编译成能在该机器上运行的机器代码就行了。Thompson于是自己设计了一种语言叫做B,然后用这种语言重写了Unix,但是由于这个语言设计的缺陷最终不是很成功,这时候大名鼎鼎的Dennis Ritchie出现了,根据B,他又设计出了大家都相当熟悉的C语言,而且还为C写了一个很棒的编译器,然后他们两个就用了CUnix给重写了一遍,而C语言也成为了一种影响深远的程序设计语言(这两位大哥真心牛叉~)。

    Unix出来之后,受到了很多关注,很多的大公司,科研机构都找贝尔实验室拿源码,当时贝尔实验室的老板AT&T公司由于对这个东西不是很重视 所以都给了(因为人家当时有的是钱~,推荐看看《浪潮之巅》就知道了,作者:吴军),这些机构拿到了源码之后,又进行了很多的研究和开发,于是产生了很多不同版本的Unix,其中一个就是伯克利大学,他们的版本就是很多人知道的BSD了 (Berkeley Software Distribution), 它的特殊之处在于它第一个将网络引入到操作系统中,使得网络协议(TCP/IP)的支持成为了Unix的一个标准,为之后因特网的发展中,基于Unix的服务器统治整个市场打下了基础。

    由于很多厂商,机构都发布了自己的Unix版本,这就带来了一个兼容的问题,不同的Unix版本提供不同的功能,甚至同个功能又有不同的系统调用接口,这意味着开发者必须针对每一个版本编写自己的程序,这又是一件很痛苦的事情。为了解决这个问题,IEEE标准委员会展开了一个项目叫做POSIX(前三个字母表示Potable Operating System),主要根据当时最流行的两个Unix版本,一个是BSD,一个是AT&T原生的Unix (System V), 这个标准定义了所有Unix系统必须提供的一套库函数,开发者只要通过这些库函数就可以满足他们开发利用Unix系统的需要。当然,标准制定的过程肯定有很多冲突,这个过程也是挺有趣的,有兴趣的话可以自己再去了解一下。

    讲了那么多Unix,终于要讲到Linux了。因为当时所有的Unix系统都十分庞大和复杂,很难用于学校教学目的,有位大学教授就根据Unix,又写了一个相似的简化了的系统,叫做Minix,它的内核只有1600多行C代码和800多行的汇编,这个东西出来之后同样受到了很多人的欢迎,不仅仅是学习,而且还移植了许多Unix的程序过来。随着它的发展,有人就希望原作者能够给这个系统内核增加更多的功能,但是作者为了保证这个系统的规模足够小,能够被学生在短时间理解,所有没有同意添加功能。于是一位芬兰的大学生Linus Torvalds又根据Minix重写了一个系统叫做Linux,支持了很多扩展功能如网络通信,其最为特殊的一点,也是它能取得现在这样的成功的一点是在与它的商业模式,它是一个开源的自由软件,意味着任何人都能够对它的代码进行研究,修改,由此创造了很大的活力,对这个有兴趣的可以查阅一些开源软件,开源协议方面的资料。

    。。。本来说简单提一提的,想不到说了这么多,接下来还是正式进入Linux系统的所谓概述吧。

    3Linux系统概述

    首先通过一个简单的例子来引出一些概念,思考一下,当我们在shell终端里输入:ls,这个时候系统发生了什么?

    我们知道ls是一个系统命令,可以列出一个目录里的文件列表,那么系统命令是什么,它是如何实现的?

    熟悉linux的都知道,其实它是一个编译好的程序,就放在/usr文件目录里,这些程序叫做Core Utility Program, 那么这些程序又是如何来完成其相应的功能的呢?这里就涉及到了一些概念,如库函数,系统调用,内核模式,用户模式等。了解C语言的肯定对c库函数不陌生,我们的程序里经常都会有的scanfprintffopenfwritemalloc这些就是,通过使用这些函数我们可以进行I/O操作和内存管理等,这些库函数也就是前面讲linux历史时提到的POSIX的成果,但是这些函数又是如何完成其功能的呢?

    没错,是通过系统调用,系统调用是操作系统内核的一种方式,具体是怎么实现的我也不知道,但是我们只要知道,我们的库函数内部是通过系统调用让系统的内核去控制内存,进程,还有I/O设备的管理就行,但是库函数又不完全等价于系统调用,因为有些库函数并没有进行系统调用,比如数学函数abssincos或是字符函数isnumisdigttolower这些,而前面提到的几个函数则有进行系统调用,它们之间的区别大概就在于有没有操作到内存,I/O,或是进程。而关于内核模式和用户模式,又有相关的进程的内核空间跟用户空间的概念,这些之后会再提到,我们现在只要知道,所有进行系统调用的操作都要切换到内核模式下完成,普通的操作则是在用户模式下运行即可。为什么要这样呢?简单地讲就是为了安全,这样做的话可以保证系统内核进行的所有操作都是系统本身定义的,而用户定义的操作都是在内核以外运行,这样即使用户的操作出了问题(恶意的或者无意的)那么系统的内核也不会受到很大的影响。

    为了清晰地展示一下这些概念还有Linux内核的结构,还是上个图吧~

    从上图我们可以看到那一大块的就是我们的操作系统,它位于计算机硬件的上层,支持了程序和硬件设备的交互,而整个操作系统内核又可以大致分成三个模块,分别为I/O,内存管理和进程管理。

    Linux通过一个虚拟文件系统将所有的I/O设备都抽象成文件,即所有的I/O设备的输入输出都可通过文件的read/write操作完成,而不需要考虑它实际上是在操作硬盘,屏幕,键盘还是网卡,而内存模块负责虚拟地址空间到物理地址空间的映射,内存分页管理和缓存等,进程管理模块负责进程线程的创建和终止,进程调度和进程间通信。这三个模块之间相互依赖,支撑起了操作系统的所有功能,在后续的文章里会分别进行深入的介绍。(这样讲可能笼统了一点,再举个例子吧)比如当我们的程序要读取一个磁盘上的文件时,就需要访问到文件系统,而为了减少磁盘读取延迟,提高效率,一次读取就会读取一个block的数据然后保存在内存里,而在程序等待磁盘读取的过程中,为了提高CPU的利用率,系统又会调度其他进程在这个过程中运行,这就是它们之间一个简单的依赖关系,当然它们还有其他许多的依赖。

    好了,作为第一篇博客写得实在有点长了,下次再继续吧~

     
     
     
    标签: 操作系统Linux

    电话面试被问到go的协程,曾经的军伟也问到过我协程。虽然用python时候在Eurasia和eventlet里了解过协程,但自己对协程的概念也就是轻量级线程,还有一个很通俗的红绿灯说法:线程要守规则,协程看到红灯但是没有车仍可以通行。现在总结各个资料,从个人理解上说明下 进程 线程 轻量级进程 协程 go中的goroutine 那些事儿。

    一、进程

    操作系统中最核心的概念是进程,分布式系统中最重要的问题是进程间通信。

    进程是“程序执行的一个实例” ,担当分配系统资源的实体。进程创建必须分配一个完整的独立地址空间。

    进程切换只发生在内核态,两步:1 切换页全局目录以安装一个新的地址空间 2 切换内核态堆栈和硬件上下文。  另一种说法类似:1 保存CPU环境(寄存器值、程序计数器、堆栈指针)2修改内存管理单元MMU的寄存器 3 转换后备缓冲器TLB中的地址转换缓存内容标记为无效。

    二、线程

    书中的定义:线程是进程的一个执行流,独立执行它自己的程序代码。

    维基百科:线程英语thread)是操作系统能够进行运算调度的最小单位。

    线程上下文一般只包含CPU上下文及其他的线程管理信息。线程创建的开销主要取决于为线程堆栈的建立而分配内存的开销,这些开销并不大。线程上下文切换发生在两个线程需要同步的时候,比如进入共享数据段。切换只CPU寄存器值需要存储,并随后用将要切换到的线程的原先存储的值重新加载到CPU寄存器中去。

    用户级线程主要缺点在于对引起阻塞的系统调用的调用会立即阻塞该线程所属的整个进程。内核实现线程则会导致线程上下文切换的开销跟进程一样大,所以折衷的方法是轻量级进程(Lightweight)。在linux中,一个线程组基本上就是实现了多线程应用的一组轻量级进程。我理解为 进程中存在用户线程、轻量级进程、内核线程。

    语言层面实现轻量级进程的比较少,stackless python,erlang支持,java并不支持。

    三、协程

    协程的定义?颜开、许式伟均只说协程是轻量级的线程,一个进程可轻松创建数十万计的协程。仔细研究下,个人感觉这些都是忽悠人的说法。从维基百科上看,从Knuth老爷子的基本算法卷上看“子程序其实是协程的特例”。子程序是什么?子程序英语Subroutineprocedurefunctionroutinemethodsubprogram),就是函数嘛!所以协程也没什么了不起的,就是种更一般意义的程序组件,那你内存空间够大,创建多少个函数还不是随你么?

    协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的。协程的起始处是第一个入口点,在协程里,返回点之后是接下来的入口点。子例程的生命期遵循后进先出(最后一个被调用的子例程最先返回);相反,协程的生命期完全由他们的使用的需要决定。

    线程和协程的区别:

    一旦创建完线程,你就无法决定他什么时候获得时间片,什么时候让出时间片了,你把它交给了内核。而协程编写者可以有一是可控的切换时机,二是很小的切换代价。从操作系统有没有调度权上看,协程就是因为不需要进行内核态的切换,所以会使用它,会有这么个东西。赖永浩和dccmx 这个定义我觉得相对准确  协程-用户态的轻量级的线程。(http://blog.dccmx.com/2011/04/coroutine-concept/)

    为什么要用协程:

    协程有助于实现:

    • 状态机:在一个子例程里实现状态机,这里状态由该过程当前的出口/入口点确定;这可以产生可读性更高的代码。
    • 角色模型:并行的角色模型,例如计算机游戏。每个角色有自己的过程(这又在逻辑上分离了代码),但他们自愿地向顺序执行各角色过程的中央调度器交出控制(这是合作式多任务的一种形式)。
    • 产生器:它有助于输入/输出和对数据结构的通用遍历。
    颜开总结的支持协程的常见的语言和平台,可做参考,但应深入调研下才好。
    Go语言并发之美

    四、go中的Goroutine

    go中的Goroutine, 普遍认为是协程的go语言实现。《Go语言编程》中说goroutine是轻量级线程(即协程coroutine, 原书90页). 在第九章进阶话题中, 作者又一次提到, "从根本上来说, goroutine就是一种go语言版本的协程(coroutine)" (原书204页). 但作者Rob Pike并不这么说。

    “一个Goroutine是一个与其他goroutines 并发运行在同一地址空间的Go函数或方法。一个运行的程序由一个或更多个goroutine组成。它与线程、协程、进程等不同。它是一个goroutine。”

    在栈实现上,它的编译器分支下的实现gccgo是线程pthread,6g上是多路复用的threads(6g/8g/5g分别代表64位、32位及Arm架构编译器)

    infoQ一篇文章介绍特性也说道: goroutine是Go语言运行库的功能,不是操作系统提供的功能,goroutine不是用线程实现的。具体可参见Go语言源码里的pkg/runtime/proc.c

    老赵认为goroutine就是把类库功能放进了语言里。

    goroutine的并发问题:goroutine在共享内存中运行,通信网络可能死锁,多线程问题的调试糟糕透顶等等。一个比较好的建议规则:不要通过共享内存通信,相反,通过通信共享内存。

    并行 并发区别:

    并行是指程序的运行状态,要有两个线程正在执行才能算是Parallelism;并发指程序的逻辑结构,Concurrency则只要有两个以上线程还在执行过程中即可。简单地说,Parallelism要在多核或者多处理器情况下才能做到,而Concurrency则不需要。(http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference)

    参考资料:

    《现代操作系统》《分布式系统原理与范型》《深入理解linux内核》《go程序设计语言》

    赖勇浩 协程三篇之仅一篇 http://blog.csdn.net/lanphaday/article/details/5397038

    颜开 http://qing.blog.sina.com.cn/tj/88ca09aa33002ele.html

    go程序设计语言中文 http://tonybai.com/2012/08/28/the-go-programming-language-tutorial-part3/  (中文翻译定义中漏了个 并发)

    go程序设计语言英文http://go.googlecode.com/hg-history/release-branch.r60/doc/GoCourseDay3.pdf

    go语言初体验 http://blog.dccmx.com/2011/01/go-taste/

    https://zh.wikipedia.org/wiki/Go

    https://zh.wikipedia.org/wiki/进程

    https://zh.wikipedia.org/wiki/线程

    http://stackoverflow.com/questions/1050222/concurrency-vs-parallelism-what-is-the-difference

    http://www.infoq.com/cn/articles/knowledge-behind-goroutine

    go语言编程书评:http://book.douban.com/review/5726587/

    为什么我认为goroutine和channel是把别的平台上类库的功能内置在语言里 

    http://blog.zhaojie.me/2013/04/why-channel-and-goroutine-in-golang-are-buildin-libraries-for-other-platforms.html

     
     
     
    标签: 进程线程轻量级进程协程Goroutine

    代码整洁之道 

    豆瓣评分:8.6
     

    代码整洁是基本的职业道德。

    做人要设身处地,写代码时候是否也做到了?是否不会给后来者遗留下麻烦?扪心自问,我无法回答。

    这本书是很有用的工具书,反思自己,指明写好代码的途径:好名字,清晰的函数,不写乱七八糟的函数。

     

    第二章 名字

         给代码儿子选个好名儿:变量名需要注释,就不是好名字。

         避免误导:O0l1?傻傻分不清楚。长点儿心,别给自己和别人添麻烦。

         能读出来的名字,有意义的区分:genymdhms  获取年月日时分秒?什么破名字。getAccount getAccounts getAccountInfo  ,到底该用哪一个?

         可搜索的名称:常量指代magic number的意义所在,可以迅速查出来都在哪里用到了。

         类名是名词;方法名是动词或动词短语。使用通用统一的名称,别卖萌,别自以为聪明。

    起个好名字,全家感谢你!时时照拂自己创建的东西,用心是最珍贵的资源。

     

    第三章 函数

         好的函数就是短的函数:每个函数只说一件事,然后依序把你带到下一个函数。if else while 中的代码块应该只有一行。好的函数位于不同的抽象层级,可自顶向下阅读。相关函数放在一起,前一个导引指向下一个。抽离try catch 避免扰乱代码结构。

         大师级程序员把系统当作故事讲,而不是当作程序写。

     

    第四章 注释

         好的注释:法律信息;解释意图;警示。不要写多余的注释,不要写可怕的废话。注释掉的代码就是渣,一定要删除!

     

    第五章 格式

         向报纸学习排版。

     

    第七章 错误处理

         错误处理很重要,但如果它搞乱了代码逻辑,就是错误的做法。定义异常,最重要的考虑应是它们如何被捕获。打包异常,只输出一种类型异常给外界。别返回或传递null值,返回空对象如Collections.emptyList(),保持代码整洁。

     

    第8、9章 边界和测试

         学习性测试,去了解第三方组件。保持测试整洁。测试代码和生产代码一样重要。测试的first原则:Fast、Independent、Repeatable、Self-Validating、Timely。

     

    第十章 短小的类

         单一权责;类应该足够小;

     

    第十一章     系统

         一开始就做对系统,纯属神话。反之,我们应该只去实现今天的用户故事。   互联网用户群可能暴增,也可能完全没有用户,做好扩容想法很重要。

         软件系统类似乡镇变城市,类似物理系统,都可以递增地增长。需要做好适当的切分。

     

    第十二章 并发

         为什么并发:把目的和时机做分解,改善系统吞吐量和结构。

         执行模型

              基本定义:    限定资源、互斥、线程饥饿、死锁、活锁。

              基本模型:生产者-消费者模型、读者-作者模型、宴席哲学家模型。保持同步区域微小。不要把系统错误归咎于偶然事件:宇宙射线,硬件问题等等。先使非线程代码可运行。

     

    第十四章 逐步改进

         要想写出整洁代码,必须先写肮脏代码,然后再清理它。多数新手写出能工作的程序,就转移到下一个任务上,能工作的程序最终也只是能工作,这是一种自毁行为。

     
     

    当前标签: python

     
    flask源码阅读笔记 坚毅的刀刀 2011-12-23 17:23 阅读:174 评论:0  
     
    list comprehensions 坚毅的刀刀 2011-12-23 17:22 阅读:36 评论:0  
     
    python 应用thrift---- thrift的监控fb303 - 坚毅的刀刀 2011-12-23 17:13 阅读:99 评论:0  
     
    python的数学函数(1)-python组合函数模块itertools 坚毅的刀刀 2011-12-23 17:13 阅读:131 评论:0  
     
    cherryPy学习 坚毅的刀刀 2011-12-23 15:52 阅读:72 评论:0  
     
    my python FAQ 坚毅的刀刀 2011-12-21 17:25 阅读:44 评论:0
     
    标签: 编码习惯
  • 相关阅读:
    codephp 自研PHP框架并实现composer包管理
    收藏!17 张程序员专属壁纸(使用频率很高)
    git reset hard HEAD^后显示more?的解决方案
    如果有一天我不得不离开IDE,没有其它原因,一定是ta ?
    centos7 下安装composer失败
    不知道如何技术变现?19个程序员接私活平台汇总
    MySQL常见面试题:什么是主从延时?如何降低主从延时?
    Nginx服务器,修改html 文件后页面不更新生效(已解决)
    《Microsoft SQL Server 2005: 数据库基础由入门到精通》书评
    讲座资源:Silverlight In Action
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3061929.html
Copyright © 2011-2022 走看看