4.2《深入理解计算机系统》笔记(五)并发、多进程和多线程【Final】

该书中第11章是写web服务器的搭建,无奈对web还比较陌生。还没有搞明白。

这些所谓的并发,其实都是操作系统做的事情,比如,多进程是操作系统fork函数实现的、I/O多路复用需要内核挂起进程、多线程需要内核创建和挂起线程。我么只是使用以下操作系统的这项并发技术。但是我们必须处理一些存在问题。

进程。用这种方法,每个逻辑控制流都是一个进程,由内核来调度和维护。因为进程有独立的虚拟地址空间,想要和其他流通信,控制流必须使用进程间通信(IPC)。

I/O多路复用。这种形式的并发,应用程序在一个进程的上下文中显示地调度它们自己的逻辑流。逻辑流被模拟为“状态机”,数据到达文件描述符后,主程序显示地从一个状态转换到另一个状态。因为程序是一个单独的进程,所以所有的流都共享一个地址空间。

线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。线程可以看做是进程和I/O多路复用的合体,像进程一样由内核调度,像I/O多路复用一样共享一个虚拟地址空间。

12.1 基于进程的并发编程

构造并发最简单的就是使用进程,像fork函数。例如,一个并发服务器,在父进程中接受客户端连接请求,然后创建一个新的字进程来为每个新客户端提供服务。

关于进程的优劣,对于在父、子进程间共享状态信息,进程有一个非常清晰的模型:共享文件表,但是不共享用户地址空间。进程有独立的地址控件爱你既是优点又是缺点。由于独立的地址空间,所以进程不会覆盖另一个进程的虚拟存储器。但是另一方面进程间通信就比较麻烦,至少开销很高。

12.2基于I/O多路复用的并发编程

比如一个服务器,它有两个I/O事件:1)网络客户端发起连接请求,2)用户在键盘上键入命令行。我们先等待那个事件呢?没有那个选择是理想的。如果accept中等待连接,那么无法相应输入命令。如果在read中等待一个输入命令,我们就不能响应任何连接请求(这个前提是一个进程)。

针对这种困境的一个解决办法就是I/O多路复用技术。基本思想是:使用select函数,要求内核挂起进程,只有在一个或者多个I/O事件发生后,才将控制返给应用程序。如图所示:横向的方格可以看作是一个n位的描述符向量。现在,我们定义第0位描述是“标准输入”,第3位描述符是“监听描述符”。

I/O多路复用的优劣:由于I/O多路复用是在单一进程的上下文中的,因此每个逻辑流程都能访问该进程的全部地址空间,所以开销比多进程低得多;缺点是编程复杂度高。

12.3基于线程的并发编程

每个线程都有自己的线程上下文,包括一个线程ID、栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的线程共享该进程的整个虚拟地址空间。由于线程运行在单一进程中,因此共享这个进程虚拟地址空间的整个内容,包括它的代码、数据、堆、共享库和打开的文件。所以我认为不存在线程间通信,线程间只有锁的概念。

线程执行的模型。线程和进程的执行模型有些相似。每个进程的声明周期都是一个线程,我们称之为主线程。但是大家要有意识:线程是对等的,主线程跟其他线程的区别就是它先执行。

一般来说,线程的代码和本地数据被封装在一个线程例程中(就是一个函数)。该函数通常只有一个指针参数和一个指针返回值。

在Unix中线程可以是joinable(可结合)或者detached(分离)的。joinable可以被其他线程杀死,detached线程不能被杀死,它的存储器资源有系统自动释放。

线程存储器模型,每个线程都有它自己的独立的线程上下文,包括线程ID、栈、栈指针、程序计数器、条件码和通用目的寄存器。每个线程和其他线程共享剩下的部分,包括整个用户虚拟地址空间,它是由代码段、数据段、堆以及所有的共享库代码和数据区域组成。不同线程的栈是对其他线程不设防的,也就是说:如果一个线程以某种方式得到一个指向其他线程的指针,那么它可以读取这个线程栈的任何部分。

12.4多线程中共享变量

全局变量和static 变量是存储在数据段,所以,多线程共享之!

●由于线程的栈是独立的,所有线程中的自动变量是独立的。即使多个线程运行同一段代码总的自动变量,那么他们的值也是根据线程的不同而不同。

●比如C++中,类属性不是在用户栈中的。所以线程共享之!

12.5用信号量同步线程。

信号量通常称之为PV操作(发明PV操作的是荷兰一个哥们),虽然它的思想是将临界代码保护起来,达到互斥效果。这里面操作系统使用到了线程挂起!PV操作是就不再做过多的解析。下面展示一个多线程对共享变量修改的进度图的解释:

现在有两个线程thread1和thread2.那么操作系统并行thread1 和thread2。汇编的执行顺序如下(可能的顺序,一共是10步):

H1、L1、U1、S1、T1、H2、L2、U2、S2、T2//可以得到cnt =2

H1、L1、U1、H2、L2、S1、T1、U2、S2、T2//得到错误的cnt =1

那么,这种排列组合会有很多种情况。这十步会给我们带来奇妙的答案。下面我们用进度图(是一种二维笛卡尔坐标系)来表达:

黄色的区域是临界区,绿色的轨迹是不安全的,而蓝色的轨迹是安全的。线程锁就是当线程即将进入临界区的时候,挂起自己等待其他线程走完临界区,自己再执行。当然,二维笛卡尔已经不能描述线程锁的原理了。

剩余章节还介绍了“生产者——消费者”问题,“读者——写者”问题,略过。

12.6 使用线程提高并行性

12.7线程安全

我们编程过程中,尽可能编写线程安全函数,即一个函数当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。如果做不到这个条件我们称之为线程不安全函数。下面介绍四类线程不安全函数:

●不保护共享变量的函数。解决办法是PV操作。

●保持跨越多个调用的状态函数。比如使用静态变量的函数。解决方法是不要使用静态变量或者使用可读静态变量。

●返回指向静态变量的指针的函数。解决方法是lock-and-copy(枷锁-拷贝)

●调用线程不安全函数的函数

死锁。由于PV操作不当,可能造成死锁现象。这在程序中也会出现。是很头疼的事情。

总结

无乱那种并发机制,同步对共享数据的并发都是一个困难的问题。该书没有更详细说明并行程序的过程。但是我相信:只要操作系统有挂起的功能,那么并发的形式应该是多种多样的!

《深入理解计算机系统》昨天已经看完了,今天把最后一章存档为博客,方便以后自己查阅并加深理解,这篇是终结篇!

纵览该书,其重要程度不言而喻。囊括了二进制表示、汇编、指令、高速缓存、CPU体系结构、存储器、虚拟存储器、栈、堆、异常、进程、编译、链接、静态库和动态库、运行、网络、线程和并发。可以作为从事计算机行业着知识的基石,只恨自己大学时候没有读这本书。

原文:http://blog.csdn.net/hherima/article/details/8987813

时间: 2024-10-09 17:29:01

4.2《深入理解计算机系统》笔记(五)并发、多进程和多线程【Final】的相关文章

深入理解计算机系统笔记

我的博客上的比这个排版显示的更好一些,特别是图片 http://notelzg.github.io/2016/06/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F%E6%80%BB%E7%BB%93/ 1. hello wordl 我们还是从hello world程序说起吧: #include <stdio.h> int main() { printf("hell

C语言杂记 -- 简陋的&lt;深入理解计算机系统&gt;笔记

程序的表示 l 32位64位操作系统是由CPU寄存器的位数决定,即虚拟寻址的范围为2^32.2^64. l 字节的大端小端法是以字节为基本单位的:比如十进制的7在十六位机器上表示 · 地址 100 101 大端法 00000000 00000111 小端法 00000111 00000000 l 大部分编译器默认进行算数右移和用补码表示复数 l 对于移位k来说,实际移位量为k mod 2^w(w为所移动数据类型的位数) l 对于整型常量来说c编译器都是先将其看成正数,如果前面有 - 运算符则对其

深入理解计算机系统9个重点笔记

引言 深入理解计算机系统,对我来说是部大块头.说实话,我没有从头到尾完完整整的全部看完,而是选择性的看了一些我自认为重要的或感兴趣的章节,也从中获益良多,看清楚了计算机系统的一些本质东西或原理性的内容,这对每个想要深入学习编程的程序员来说都是至关重要的.只有很好的理解了系统到底是如何运行我们代码的,我们才能针对系统的特点写出高质量.高效率的代码来.这本书我以后还需要多研究几遍,今天就先总结下书中我已学到的几点知识. 重点笔记 编写高效的程序需要下面几类活动: 选择一组合适的算法和数据结构.这是很

《深入理解计算机系统》 Chapter 7 读书笔记

<深入理解计算机系统>Chapter 7 读书笔记 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(货被拷贝)到存储器并执行. 链接的时机 编译时,也就是在源代码被翻译成机器代码时 加载时,也就是在程序被加载器加载到存储器并执行时 运行时,由应用程序执行 链接器使分离编译称为可能. 一.编译器驱动程序 大部分编译系统提供编译驱动程序:代表用户在需要时调用语言预处理器.编译器.汇编器和链接器. 1.将示例程序从ASCⅡ码源文件翻译成可执行目标文件的步骤 (1)运

深入理解计算机系统读书笔记一 ---&gt; 计算机基础漫游

一.程序编译的不同阶段. 通常我们是以高级程序开发易于阅读的代码,我们通过语法规则推断代码的具体含义.但是计算机执行代码的时候就需要把代码解析成既定的可执行问题,计算机是如何处理的呢?这里以C语言hello.c文件为例来说明中间过程. #include <stdio.h> int main() { printf("hello world!\n"); } 先上张图. C语言源程序----预处理解析头文件和函数  --- 编译器解析成汇编语言 ---   翻译机器语言指令,打包

第七章读书笔记《深入理解计算机系统》

第七章 读书笔记<深入理解计算机系统> 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或拷贝)到存储器并执行. 链接可以执行于编译时,也就是在源代码被翻译成机器代码时:也可以执行于加载时,也就是在程序被加载器加载到存储器并执行时:甚至执行于运行时,由应用程序来执行. 在早期的计算机系统中,链接是手动执行的.在现代系统中,链接是由叫链接器的自动执行的. 7.1 编译器驱动程序 1.大部分编译系统提供编译驱动程序:代表用户在需要时调用语言预处理器.编译器.汇编器

深入理解计算机系统读书笔记之第一章:漫游

我是从豆瓣上看到好多人都在推荐这本书,于是就去借来读一读,昨天晚上用了好长时间来读这本书的第一章节,感觉这本书比较符合我(有些基础还不太明白,这本书详细的进行了讲解,很好). 下面写一下我的理解(顺便回顾一下知识) 第一节主要讲的是: A Tour of Computer Systems 以hello.c为例进行讲解,介绍这个程序如何从一个源程序变成可执行程序,再到执行,显示屏上出现“hello,world” ···········································

读书笔记《深入理解计算机系统》(第三版) 概述

<深入理解计算机系统>第三版刚出来不到一周,便买下了这本书:之所以阅读本书,一方面源于网友推荐以及豆瓣不错的评分.评价:另一方面是针对本人非科班出身,计算机系统相关的知识相对比较薄弱,很多情况下此类知识需要工作之外的时间自学.补涨此类知识,而该书从程序员的角度进行阐述.展示了计算机系统的各个层面的知识以及其影响程序的正确性.性能.实用性,更容易引起某些上层和下层的知识接轨理解,以期望可以融汇各方面的知识. 明白程序的运作过程,编写更好的软件程序,对底层过程了解以完善自己知识体系和提高信心.接下

《深入理解计算机系统》第六周学习笔记

第四章 处理器体系结构 (一)知识点总结 一.Y86指令集体系结构 1.Y86处理器状态类似于IA32,有8个程序寄存器: %eax.%ecx.%edx.%ebx.%esi.%edi.%esp.%ebp.处理器的每个程序寄存器存储一个字.%esp被入栈.出栈.调用和返回指令作为栈指针. 2.3个一位的条件吗:ZF.SF.OF,它们保存最近的算术或逻辑指令所造成影响的有关信息.程序计数器PC存放当前正在执行指令的地址. 3.程序状态的最后一个部分是状态码stat,它表明程序执行的总体状态 4.Y8