文章目录

  • @[toc]
  • 第一章 温故而知新
    • 1. 计算机硬件结构
    • 2. SMP与多核
    • 3. 中间层
    • 4. 操作系统
    • 5. 虚拟地址
    • 6. 线程
      • 线程基础
      • 线程安全

第一章 温故而知新

1. 计算机硬件结构

  1. 早期计算机硬件结构:所有I/O设备(显示设备,键盘,硬盘等),CPU和内存都直接连接在同一条总线Bus)。I/O设备速度慢,需要连接一个I/O控制器才能连到总线上,这样才能与速度快的CPU进行通信。

  2. 现代计算机硬件结构:用北桥芯片连接高速设备,使CPU、内存和高速的图形设备等能够高速地交换数据。南桥芯片连接所有的低速设备然后将它们汇总到系统总线。内存速度跟不上CPU,所以需要北桥芯片来协调。PCI/ISA及南北桥设计的硬件结构如图。
    从图中可以看出,南北桥芯片都连接到PCI总线,CPU和内存直接连接到北桥芯片。

2. SMP与多核

  1. SMP(Symmetrical Multi-Processing):对称多处理器。一个计算机有多个CPU。每个CPU在系统中所处的地位和发挥的功能是一样的,是相互对称的。多处理器可以最大效能地处理大量的并发请求。应用于商用的服务器和需要大量计算的环境。成本高。

  2. 多核处理器(Muti-core Processor):简化版SMP,一个处理器上面有多个计算核心,这些计算核心共享比较昂贵的缓存部件等。

3. 中间层

  1. 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。(Any problem in computer science can be solved by another layer of indirection.)

  2. 每个层次之间需要互相通信,通信就需要有一个协议,即接口(Interface)。例如操作系统内核层是硬件接口的使用者,硬件是硬件接口的定义者,硬件定义了驱动程序如何操作硬件,如何与硬件通信。

4. 操作系统

  1. 多任务(Multi-tasking)系统:操作系统接管了所有的硬件资源。所有的进程运行在比操作系统权限更低的级别。每个进程都有独立的地址空间,进程间的地址空间互相隔离。

  2. 抢占式(Preemptive)CPU分配方式:CPU由操作系统进行分配,每个进程根据进程都有机会的到CPU,运行时间超出一定的时间,操作系统就会暂停该进程,将CPU资源分配给其他等待运行的进程。操作系统分配给每个进程的时间都很短,即CPU在多个进程间快速地切换,从而造成很多进程都在同时运行的假象。

  3. 设备驱动(Device Driver):直接操作硬件的程序。操作体系会给硬件设备生产商提供一系列接口和框架,按照这个接口和框架开发的驱动程序就可以在该操作系统上运行。

  4. 文件系统:文件系统是操作系统最重要的组成部分之一。文件系统管理着硬盘中文件的存储方式。硬盘分成多个扇区。文件系统保存了文件存储的数据结构,有效地利用硬盘的扇区。

  5. 例如在linux系统中使用read函数来读取文件:
    a. 文件系统收到read请求之后,计算出要读取数据所在硬盘的扇区。
    b. 文件系统向硬盘驱动程序发出要读取哪些扇区的请求。
    c. 硬盘驱动程序向硬盘发出I/O命令,即读写I/O端口的寄存器。
    d. 硬盘收到命令后,执行相应的操作,并且将数据读取到设置好的内存地址中。

文件系统
硬盘驱动程序
硬盘
内存

5. 虚拟地址

  1. 进程隔离: 程序使用的地址是虚拟地址(Virtual Address),虚拟地址会映射到实际的物理地址。每个进程都有自己独立的虚拟空间,且只能访问自己的地址空间,从而实现了进程的隔离。 如果程序直接访问物理内存,那么恶意程序很容易改写其他程序的内存。由于程序使用的是虚拟地址,程序是不知道其他进程的物理地址,也没权限访问。

  2. 程序的运行地址确定:程序在编译时,访问数据的地址以及指令跳转的地址都是固定的。当程序载入运行时,操作系统会分配一块内存。如果直接使用物理地址,这块内存的物理地址是不确定的,那么程序编译时的地址则是不正确的。而虚拟地址是固定的,例如从地址0x00000000到0x00A00000,程序按照这个地址空间进行存放变量和指令,在运行载入的时候就不需要重定位,直接访问这个虚拟地址。

  3. 虚拟存储需要硬件支持,即MMU(Memory Management Unit),一般集成在CPU中。

  4. 分页(Paging):使用分页技术来实现进程隔离以及有效地利用内存。分页的基本方法是把地址空间等分成固定大小的页。一般操作系统都是用4KB大小的页。把进程的虚拟空间地址按页分割,将常用的数据和代码装载到内存中,不常用的数据和代码保存到硬盘里,当进程需要的时候,再从硬盘里读出并载入内存。虚拟空间的页叫虚拟页(Virtual Page),物理内存的页叫物理页(Physical Page),磁盘的页叫磁盘页(Disk Page)

6. 线程

线程基础
  1. 线程(Thread):程序执行流的最小单元。由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。一个进程可由多个线程组成,各个线程之间共享程序的内存空间(代码段,数据段,堆等)以及进程级的资源(如打开文件等)。进程是不共享内存空间的,所以进程间的共享数据比线程间贡献数据更加复杂,低效。

  2. 线程调度(Thread Schedule):操作系统会让多个线程轮流执行,模拟出并发的状态。不断地在处理器上切换不同的线程的行为即线程调度。

  3. 时间片(Time Slice):操作系统赋予处于运行中线程的可以执行的一段时间。

  4. 线程至少有三种状态:

    • 运行(Running):正在执行。
    • 就绪(Ready):此线程可以立即运行,但CPU已经被占用。
    • 等待(Waiting):正在等待某一时间发生,无法执行。
  5. 线程状态切换:线程执行一段时间,时间片用光后,进入就绪状态。如果在时间片用尽之前就开始等待某事件,那么该线程进入等待状态。每当一个线程离开运行状态时,调度系统就会选择其他就绪的线程继续执行。当一个处于等待状态的线程所等待的线程发生之后,该线程就进入就绪状态。

  6. 线程调度方式:优先级调度(Priority Schedule)和轮转法(Round Robin)。轮转法就是让各个线程轮流执行一小段时间,决定了线程之间交错的特点。优先级调度决定了线程按照什么顺序轮流执行。优先级高的线程会更早的执行,优先级的线程常常要等到系统中已经没有更高优先级的可执行的线程存在时才能够执行。

  7. 线程优先级改变方式:

    • 用户指定优先级。
    • 操作系统会根据线程进入等待状态的频率提升或降低优先级。
    • 长时间得不到执行而被提升优先级。操作系统为了避免线程**饿死(Starvation)**现象。调度系统会逐步提升等待过长时间的得不到执行的线程的优先级。
  8. 线程分类:

    • IO密集型线程(IO Bound Thread):处理I/O的线程,频繁进入等待状态的线程,很少把时间片用光。优先级更容易被操作系统提升。
    • CPU密集型线程(CPU Bound Thread):进行大量计算,总是把时间片用光,很少进入等待状态。
线程安全
  1. 多个线程同时访问同一个数据的时候,就会产生安全问题。

  2. 线程同步(Synchronization):一个线程访问数据未结束的时候,其他线程不得对同一个数据进行访问。

  3. 同步方式常使用锁(Lock):

    • 信号量(Semaphore),一个初始值为N的信号量允许N个线程并发访问。
    • 互斥锁(Mutex)。
    • 临界区(Critical Section)。
    • 读写锁(Read-Write Lock):同时多个读,但同时只能一个写。
    • 条件变量(Condition Variable)。多个线程通过等待和唤醒条件变量,来达到互相通信,同步的目的。

4.过度优化: 即使使用了锁,也未必保证线程安全。编译器在进行优化的时候可能为了提高效率交换毫不相干的两条相邻指令的执行顺序。CPU在执行程序的时候也可能为了提高效率而交换指令的执行顺序。

  1. volatile关键字组织编译器过度优化:

    • 阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回内存。例如一个线程更改了变量的值,但是更改后的值被缓存到寄存器而没有写回内存,那么另一个线程在访问这个变量的时候,还是修改之前的值。
    • 组织编译器调整volatile变量的指令顺序。
  2. 内存栅栏(Barrier)组织CPU将该指令之前的指令交换到Barrier之后。许多CPU都提供了barrrier指令。

  3. CPU动态调度换序的例子,double-check单例。

// double-check lock pattern
T* GetInstance() {if (pInst == NULL) {lock();if (pInst == NULL) {pInst = new T;}unlock();}return pInst;
}

加锁lock()解决了线程竞争问题,双重if判断降低lock调用带来的开销。

C++的new包含了两个步骤:分配内存和调用构造函数。所以pInst = new T包含了三个步骤:

  • 分配内存。
  • 在内存位置上调用构造函数。
  • 将内存的地址赋值给pInst。

CPU可能回颠倒第二步和第三步的执行顺序。如果一个线程在执行new的时候先将内存地址赋值给pInst,但是这块内存还没有被构造。此时另一个线程在调用GetInstance()时,就是直接返回这个还没有构造的对象的内存地址。

使用barrier来解决该问题:

// POWERPC 提供lwsync这个Barrier指令。
#define barrier() __asm__ volatile("lwsync")T* GetInstance() {if (pInst == NULL) {lock();if (pInst == NULL) {T* temp = new T;barrier();pInst = temp;}unlock();}return pInst;
}

barrier的存在,对象的构造一定在barrier执行之前完成,因此当pInst被赋值时,对象总是完好的。

《程序员的自我修养---链接、装载与库》读书笔记(一)计算机组成原理与操作系统相关推荐

  1. 【《程序员的自我修养---链接装载于库》读书笔记】可执行文件的装载与进程

    系列文章目录 [<程序员的自我修养-链接装载于库>读书笔记]初探ELF [<程序员的自我修养-链接装载于库>读书笔记]windows PE/COFF [<程序员的自我修养 ...

  2. 《程序员的自我修养-链接-装载与库》第三章 目标文件里有什么(1)

    目录 0.引言 1.目标文件的格式 1.1 目标文件的格式及ELF文件格式的文件的分类 1.2 目标文件与可执行文件格式的小历史 2.目标文件是什么样的 2.1 程序与目标文件简介 2.2 BSS历史 ...

  3. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(三)】函数调用与栈(this指针、返回值传递临时对象构建栈、运行库与多线程、_main函数、系统调用与中断向量表、Win32、可变参数、大小端

    文章目录 前言 介绍 内存 内存布局 栈与调用惯例 堆与内存管理 运行库 入口函数和程序初始化 C/C++运行库 运行库与多线程 C++全局构造与析构 fread 实现 系统调用与API 系统调用介绍 ...

  4. 程序员的自我修养—链接、装载与库 笔记

    程序员的自我修养-链接.装载与库 笔记 内存管理 直接使用物理内存地址 虚拟内存-分段 虚拟内存-分页 分页和分段的主要区别 段页式 代码生成过程 预处理 编译 词法分析 语法分析 语义分析 源代码优 ...

  5. 《程序员的自我修养—链接、装载与库》pdf书签,目录分享

    在网上下载到<程序员的自我修养-链接.装载与库>pdf版本,拜读之后受益匪浅,但是因为下载的pdf没有书签,所以想要查找某一章的内容不是很方便,于是自己制作了一下书签文件,将书签文件导入p ...

  6. 【读书笔记】【程序员的自我修养 -- 链接、装载与库(二)】进程虚拟地址空间、装载与动态链接、GOT、全局符号表、共享库的组织、DLL、C++与动态链接

    文章目录 前言 介绍 可执行文件的装载与进程 进程虚拟地址空间 装载方式 操作系统对可执行文件的装载 进程虚存空间分布 ELF文件的链接视图和执行视图 堆和栈 Linux 内核装载ELF & ...

  7. 《程序员的自我修养--链接、装载与库》笔记

    写在前面:本文是我在阅读<程序员的自我修养–链接.装载与库>一书时做的笔记,所谓好记性不如烂笔头嘛,其中主要摘抄记录了本人着重阅读的章节:除此之外还有小部分本人对书中内容的个人理解.以及文 ...

  8. 《程序员的自我修养--链接、装载与库》学习笔记(一)

    本系列文章是<程序员的自我修养–链接.装载与库>(电子工业出版社)一书的学习摘录笔记,本文是书中1.1至1.4部分. 文章目录 基础概念 硬件 软件 基础概念 #include <s ...

  9. 程序员的自我修养——链接、装载与库 笔记(一)

    程序员的自我修养   悄咪咪的说一句,这篇文章可能需要对计算机有过系统的学习,不然看着可能一脸懵.如果有疑问的话,当然,很可能是我太菜了,写的不好,欢迎大家评论区留言指教!此笔记只是刚刚开始,后续我会 ...

  10. 程序员的自我修养--链接、装载与库笔记:总结

    <程序员的自我修养----链接.装载与库>这本书是2009年出版的,书中有些内容的介绍可能已经过时,已不再适用于现在的C/C++开发,而且书中展示的结果均是在32位机上进行的操作,这里全部 ...

最新文章

  1. oracle 杀死过程,ORACLE-Kill 杀死正在执行的Oracle存储过程和死锁语句
  2. 写给初学者的深度学习教程之 MNIST 数字识别
  3. Python 实现9*9乘法表
  4. weblogic从入门到起飞!(域模块、扩展模块)(三)
  5. 阿波罗登月51周年,程序员用DAIN技术还原阿波罗登月高清影像,效果震撼
  6. 信息学奥赛一本通(1122:计算鞍点)
  7. 计算机组成原理课程论文结语,计算机组成原理课程论文
  8. C# MD5加密与解密
  9. 始于CSDN,归于CSDN【一个七年老码农的心声】
  10. 我的第一个Python程序:Luogu1001 A+B Problem
  11. 第七篇:A133 Android 10 触摸提示音过大
  12. 魔兽世界插件编写-第一个插件-空插件 EmptyAddOns
  13. Typora桌面快捷方式创建
  14. oppoa11android版本是什么,oppoa11x处理器是什么?oppoA11x配置介绍
  15. IoT黑板报0209:工信部明确增加物联网网号
  16. 360浏览器登录新浪微博图标显示为字母及占用CPU过高问题的解决
  17. Java实现微信红包随机金额算法
  18. #JAVA# JAVA简易版计算器GUI编程练习
  19. JavaScript中的onload详解
  20. 程序员的自我修养——链接、装载与库

热门文章

  1. 老罗android开发视频教程学习完了
  2. 在国外当程序员是一种什么样的体验
  3. 8种排序算法(Java实现)
  4. C-Free 3.5.2 注册码
  5. 安卓隐藏摄像_隐藏摄像头探测器
  6. android qq三方登录授权失败,qq第三方登陆授权失败110401错误码解决办法介绍
  7. 服务器群晖系统安装,使用电脑为群晖NAS安装DSM操作系统
  8. 高等代数——大学高等代数课程创新教材(丘维声)——1.3笔记+习题
  9. Bean的装配方式之xml装配--(超详细,适合小白入门)
  10. java控制浏览器,java控制夜神访问浏览器