本节内容

1、引子

2、进程的诞生

3、线程

4、争吵

一、引子

我听说我的祖先们生活在专用计算机里, 一生只帮助人类做一件事情,比说微积分运算 了、人口统计了 、生成密码、甚至通过织布机印花 !  
如果你想在这些专用“计算机”上干点别的事儿,例如安装个游戏玩玩, 那是绝对不可能的, 除非你把它拆掉, 然后建一个全新的机器。  而我这些祖先们勉强可以称为“程序”。
后来有个叫冯诺依曼的人, 非常了不起, 他提出了“存储程序”的思想, 并且把计算机分为五大部件: 运算器、控制器、存储器、输入设备、输出设备。 
各种各样不同功能的程序写好以后,和程序使用的数据一起存放在计算机的存储器中,即“存储程序”;然后,计算机按照存储的程序逐条取出指令加以分析,并执行指令所规定的操作。 
这样一来,原来的专用计算机变成了通用的计算机,不管你是计算导弹弹道的,模拟核爆炸的,还是计算个人所得税的, 统统都可以在一台机器上运行, 我就是其中的一员:  专门计算员工的薪水。

二、进程的诞生

进程的诞生我所在的计算机是个批处理系统, 每次上机时, 我和其他程序都排好队, 一个接一个的进入内存运行。 
每个月末是发薪日, 我都要运行一次,这样我每月都能见一次CPU阿甘, 这个沉默寡言,但是跑的非常快的家伙。
我知道内存看阿甘不顺眼,还告了它一状,说他一遇到IO操作的时候,就歇着喝茶 ,从来不管不问内存和硬盘的忙的要死的惨境。 
(码农翻身注:参见《CPU阿甘》和《CPU阿甘之烦恼》)

其实我倒是觉得挺好, 这时候正好和阿甘海阔天空的聊天,他阅程序无数, 知道很多内部消息,每一个字节都清清楚楚, 和他聊天实在是爽。

又到了月末发薪水的时候, 我刚一进入内存,便看到这么一个公告:

                                                    公告
为了创建和谐社会,促进效率和公平, 充分发挥每一个人的能力,经系统党委慎重研究决定:本系统自即日起,正式从“批处理系统”转为“多道程序系统”, 希望各部门通力配合,一起完成切换工作。
系统党委
xxxx年xx月xx日
 
我正想着啥是多道程序系统, 阿甘便打电话给内存要我的指令开始运行了。 
和之前一样,运行到了第13869123行,这是个IO指令, 我欢天喜地的准备和阿甘开聊了。 
阿甘说: 哥们, 准备保存现场吧, 我要切换到另外一个程序来运行啦!
“啊 ? 我这正运行着呢! 咱们不喝茶了?
“喝啥茶啊,马上另外一个程序就来了!”
 " 那我什么时候回来再见你?” 我问道。
“等这个IO指令完成,然后操作系统老大会再给你机会运行的。”
“那谁来记住我当前正在运行第13869123行?  还有刚把两个数据从内存装载到了你的寄存器,就是那个EAX, EBX , 你一切换岂不都丢了?  ” 我有点着急。
阿甘说:“所以要暂时保存起来啊, 不仅仅是这些,还有你的那些函数在调用过程中形成的栈帧和栈顶, 我这里用寄存器EBP和ESP维护着, 都得保存起来。”(码农翻身注:参见《CPU阿甘之函数调用的秘密》)

“还有” 阿甘接着说,“你打开的文件句柄,你的程序段和数据段的地址, 你已经使用CPU的时间,等待CPU的时间。。。。。。 以及其他好多好多的东西,统统都要保存下来。”

我瞪大了眼睛: “这也太麻烦了吧, 原来我只需要关心我的指令和数据, 现在还得整这么多稀奇古怪的东西”
“没办法,这就叫做上下文切换, 把你的工作现场保存好,这样下一次运行的时候才能恢复啊。 对了,老大给你们统一起了一个新的名称: 进程 !   刚才那些需要保存的东西叫做叫做进程控制块
(Processing Control Block, PCB),”
我想了想,这个名字还挺贴切的, 一个真正进行的程序!  只是这个正在进行的程序随时可以被打断啊。 
我只好保存好上下文,撤出CPU, 回到内存里歇着去了,与此同时另外一个程序开始占据CPU运行。 
其实我这个程序,奥,不对,我这个进程被放到一个阻塞队列里,等到IO的数据来了以后,又被赶到了就绪队列中, 最后才有机会再次运行,再次见到CPU阿甘。(码农翻身: 进程的就绪, 阻塞,运行这三个状态的转换和《我是一个线程》中描述的非常类似)
阿甘从我的PCB中取出各种保存的信息,恢复了运行时现场, 可是忙活了好一阵, 没办法,这就是程序切换必须要付出的代价。 
我有点同情阿甘了, 从此以后,他很难再悠闲和和我们海阔天空,每时每刻都处于高速的奔跑中。 
得益于阿甘的高速度, 虽然在同一时刻只有一个程序在运行, 但是有很多程序在短时间内不断的切换, 在外界看来,  似乎多个程序在同时执行。
尤其是那些速度超慢的人类, 他们开着电脑一边听歌,一边上网,一边QQ,  很是自在, 理所当然的认为这些程序就是同时在运行。 岂不知阿甘是让音乐播放器上运行几十毫秒, 然后打断,让浏览器进程运行几十毫秒,再打断,让QQ也运行几十毫秒,如此循环往复。 
唉,阿甘真是能者多劳啊, 这个计算机系统也算是达到了我们党委的目标:兼顾了效率和公平。

三、线程

有了进程就万事大吉了吗? 人类的欲望是无止境的,很快就出现了新情况, 举个例子来说吧,我有一个兄弟,是个文字处理软件, 他和我不一样, 他有界面, 人类在用的时候能看到, 这实在是很幸福, 不像我总是在背后默默工作,几乎无人知晓。
 
这哥们有个智能的小功能,就是在人类编辑文档的时候能自动保存, 防止辛辛苦苦敲的文字由于断电什么的丢掉。 
可是这个功能导致了人类的抱怨, 原因很简单,自动保存文字是和IO打交道,那硬盘有多慢你也知道, 这个时候整个进程就被挂起了, 给人类的感觉就是: 程序死了,键盘和鼠标不响应了! 无法继续输入文字, 但是过一会儿就好了。
 
并且这种假死一会儿就会出现一次(每当自动保存的时候), 让人不胜其烦。 
系统党委研究了很久, 他们当然可以用两个进程来解决问题, 一个进程负责和用户交互, 另外一个进程负责自动保存, 但是,这两个进程之间完全是独立的,每个人都有自己的一亩三分地(地址空间), 完全互不知晓, 进程之间通信的开销实在是太大, 他们没有办法高效的操作那同一份文档数据。
 
后来还是劳模阿甘想出了一招 :  可以采用多进程的伟大思想啊!  
 
把一个进程当成一个资源的容器, 让里边运行几个轻量级的进程, 就叫线程吧, 这些线程共享进程的所有资源, 例如地址空间,全局变量,文件资源等等。
但是每个线程也有自己独特的部分, 那就是要记住自己运行到哪一行指令了, 有自己的函数调用堆栈,自己的状态等等, 总而言之,就是为了能像切换进程那样切换线程。 

拿我那个哥们的情况来说, 一个进程保存着文档的数据,  进程中有两个线程, 一个负责和用户交互, 另外一个专门负责定时的自动保存,  IO导致的阻塞就不会影响另外一个了。

注意,这两个线程都能访问进程的所有东西, 他们两个要小心,不要发起冲突才好  -- 这是人类程序员要做的事情了, 不归我们管。

四、争吵

阿甘的建议被采纳了, 其实这几乎是唯一的解决问题方式了,  但是由谁来管理引起了激烈争吵。
系统党委有一波人坚持要在用户空间实现线程, 换通俗的话说就是让那些进程在自个儿内部去管理线程, 他们的理由也很充分:
 
你们自己实现了线程,可以自己定制自己的调度算法,多灵活啊;  
所有的线程切换都在进程内完成, 不用请求我们操作系统内核来处理,效率多高啊;
况且你们可以在那些内核不支持线程的操作系统中运行, 移植性多好啊。
 

我们清楚的知道这是内核想做甩手掌柜,  因为他们选择性的忽略了一个致命的问题: 如果由我们实现线程,则操作系统内核还是认为我们只是一个进程而已,对里边的线程一无所知,对进程的调度还是以进程为最小单位。

一旦出现阻塞的系统调用,不仅仅阻塞那个线程,还会阻塞整个进程!
例如文字处理器那个进程, 如果负责定时保存的线程发起了IO调用, 内核会认为,这是由进程发起的,于是就把整个进程给挂起了, 虽然和用户交互的进程还是可以运行,也被强制的随着进程挂起来,不响应了, 这多么悲催啊, 又回到了老问题上去了。 
所以我们坚决不能答应, 我们则一致的要求:在内核中实现线程 ! 内核需要知道进程中线程的存在,内核需要维护线程表,并且负责调度!

党委的人傲慢的说: 你们不嫌累吗, 每次创建一个线程都得通过我们内核, 多慢啊。

我们说:只有这样,一个线程的IO系统调用才不会阻塞我们整个进程啊, 你们完全可以选择同一个进程的另外一个线程去执行。
双发僵持不下,最后只好妥协, 那就是:混合着实现吧。
用户空间的进程可以创建线程(用户线程), 内核也会创建线程(内核线程), 用户线程映射到内核线程上。

问题基本解决了,但也带来了新的问题,我们的系统也变的越来越复杂, 尤其是进程之间的通信和线程之间的同步, 会那些程序员们带来无穷无尽的烦恼, 这是后话了, 有机会下次再说吧。

注: 本文转自微信公众号码农转身

网络编程基础【day10】:我是一个进程(三)的更多相关文章

  1. 【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)

    在网络课程中,有讲到Socket编程,对于tcp讲解的环节,为了加深理解,自己写了Linux下进程Socket通信,在学习的过程中,又接触到了其它的几种方式.记录一下. 管道通信(匿名,有名) 管道通 ...

  2. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

  3. Java网络编程基础(Netty预备知识)

    今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...

  4. linux 网络编程 基础

    网络编程基础 套接字编程需要指定套接字地址作为参数,不同的协议族有不同的地址结构,比如以太网其结构为sockaddr_in. 通用套接字: struct sockaddr { sa_family_t ...

  5. 服务器编程入门(4)Linux网络编程基础API

      问题聚焦:     这节介绍的不仅是网络编程的几个API     更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系.     这节主要介绍三个方面的内容:套接字( ...

  6. Linux 高性能服务器编程——Linux网络编程基础API

    问题聚焦:     这节介绍的不仅是网络编程的几个API     更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系.     这节主要介绍三个方面的内容:套接字(so ...

  7. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  8. python全栈开发从入门到放弃之socket网络编程基础

    网络编程基础 一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务 ...

  9. 大数据学习笔记——Java篇之网络编程基础

    Java网络编程学习笔记 1. 网络编程基础知识 1.1 网络分层图 网络分层分为两种模型:OSI模型以及TCP/IP网络模型,前者模型分为7层,是一个理论的,参考的模型:后者为实际应用的模型,具体对 ...

  10. Linux网络编程基础API

    第5章 Linux网络编程基础API 探讨Linux网络编程基础API与内核中TCP/IP协议族之间的关系,并未后续章节提供编程基础.从3个方面讨论Linux网络API. socket地址API.so ...

随机推荐

  1. Go语言类型(布尔、整型、数组、切片、map等)

    1.基本类型 布尔类型:bool 注意:布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换. 整型:int8.byte(uint8).int16.int.uint.uintptr int.ui ...

  2. Marriage Match III HDU - 3277(二分权值 + 拆点 建边)

    题意: 只不过是hdu3081多加了k种选择 想一下,最多能玩x轮,是不是就是每个女生能最多选x个男生 现在题中的每个女生比3081多了k中选择   那就把女生拆点  i  i‘ i --> i ...

  3. centos6.8下安装matlab2009(图片转帖)

    前言 如何优雅的在centos6.8上安装matlab2009. 流程 不过我个人安装过程完后启动matlab的时候又出现了新问题: error while loading shared librar ...

  4. git 本地推送远程仓库报错: error: failed to push some refs to 'https://github.com/yangtuothink/mxonline.git'

    报错现象 添加远程仓库后 推送代码的时候报错 报错分析 远程代码和本地代码不匹配问题 远程初始仓库的创建有些默认 的 README什么的本地是没有的 需要先同步后再上传 报错解决 git push - ...

  5. python 识别验证码

    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/instal ...

  6. zabbix 常用监控模板

    以下为常用的服务监控,可直接通过zabbix的导入功能导入,做基本修改就可以使用nginx监控模板 <?xml version="1.0" encoding="UT ...

  7. docker命令篇

    基础命令: 镜像: 获取镜像 $ docker pull centos:7 下拉自己仓库镜像,在后面仓库部分会讲到. 列出镜像: $ docker image ls 删除镜像: $ docker im ...

  8. 2019西北工业大学程序设计创新实践基地春季选拔赛(重现赛) Chino with Equation(组合公式)

    链接:https://ac.nowcoder.com/acm/contest/553/D来源:牛客网 题目描述 Chino的数学很差,因此Cocoa非常担心.今天,Cocoa要教Chino解不定方程. ...

  9. 牛客练习赛43 Tachibana Kanade Loves Review C(最小生成树Kruskal)

    链接:https://ac.nowcoder.com/acm/contest/548/C来源:牛客网 题目描述 立华奏是一个刚刚开始学习 OI 的萌新. 最近,实力强大的 QingyuQingyu 当 ...

  10. Python3 与 C# 基础语法对比(List、Tuple、Dict、Set专栏)

      Code:https://github.com/lotapp/BaseCode 多图旧版:https://www.cnblogs.com/dunitian/p/9156097.html 在线预览: ...