程序员的自我修养-第一章 笔记

1.计算机硬件的三个部件最为重要:中央处理器CPU,内存和IO控制芯片。编程

2.主板上北桥芯片处理高速设备。南桥芯片处理低速设备,而后汇总后链接到北桥上。缓存

3.多个处理之间共享比较昂贵的缓冲部件,只保留报个核心,而且以一个处理器的外包装出售,售价比单核心的处理器只贵了一点,这就是多核处理器(Multi-core Processor)的基本想法。安全

4.操做系统内核层对于硬件层来讲是硬件接口的使用者,而硬件是接口的定义者,硬件的接口定义决定了操做系统内核,具体来说就是驱动程序如何操做硬件,如何与硬件进行通讯。这种接口每每被叫作硬件规格(HardWare Specification),硬件的生产厂商负责提供硬件规格,操做系统和驱动程序的开发者经过阅读硬件规格所规定的各类硬件编程接口标准来编写操做系统和驱动程序。多线程

5.早期的计算机中,程序是直接运行在物理内存上的,多个程序运行时也都被分配在物理内存上,这样的分配策略引起了3个重要问题:程序之间的地址空间不隔离,内存使用效率低,程序运行的地址不肯定。解决这几个问题的思路就是增长中间层,即便用一种间接的地址访问方法。整个想法是这样的,咱们把程序给出的地址看做是一种虚拟地址(Virtual Address),而后经过某些映射的方法,将这个虚拟地址转换成实际的物理地址。这样,只要咱们可以妥善地控制这个虚拟地址到物理地址的映射过程,就能够保证任意一个程序所可以访问的物理内存区域跟另一个程序相互不重叠,以达到地址空间隔离的效果,同时也能够作到每一个程序的地址是相同的。而内存使用效率低这个问题人,们很天然地想到了更小粒度的内存分割和映射的方法,使得程序的局部性原理获得充分的利用,大大提升了内存的使用率。这种方法就是分页(Paging)。并发

6.虚拟存储的实现须要依靠硬件的支持,对于不一样的CPU来讲是不一样的,可是几乎全部的硬件都采用一个叫作MMU(Memory Management Unit)的部件来进行页映射。通常MMU都集成在CPU内部了,不会以独立的部件存在。函数

7.线程(Thread),有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID、当前指令指针(PC),寄存器集合和堆栈组成。一般意义上,一个进程由一个到多个线程组成。各个线程之间共享程序的内存空间(包括代码段,数据段,堆等)及一些进程级的资源(如打开文件和信号)。优化

8.通常把频繁等待的线程称之为IO密集型线程(IO Bound Thread),把不多等待的线程成为CPU密集型线程(CPU Bound Thread)。IO密集型线程老是比CPU密集型线程容易获得优先级的提高。在线程优先级调度下,存在一种饿死(Starvation)的现象,一个线程被饿死,是说它的优先级较低,在它执行以前,老是有较高优先级的线程试图执行,所以这个低优先级线程始终没法执行。当一个CPU密集型的线程得到较高的优先级时,许多低优先级的线程极可能饿死。而一个高优先级的IO密集型线程因为大部分时间都处于等待状态,所以相对不容易形成其余线程饿死。为了不饿死现象,调度系统经常会逐步提高那些等待了过长时间的得不到执行的线程的优先级。在这样的手段下,一个线程只要等待足够长的时间,其优先级必定会提升到足够让它执行的程序。操作系统

9.fork产生新任务的速度很是快,由于fork并不复制原任务的内存空间,而是和原任务一块儿共享一个写时复制(Copy On Write,COW)的内存空间。所谓写时复制,指的是两个任务能够同时自由地读取内存,单任意一个任务试图对内存进行修改时,内存就会复制一份提供给修改方单独使用,以避免影响到其余的任务使用。线程

10.互斥量和二元信号量很相似,资源仅同时容许一个线程访问,但和信号量不一样的是,信号量在整个系统能够被任意线程获取并释放,也就是说,同一个信号量能够被系统中的一个线程获取以后由另外一个线程释放。而互斥量则要求那个线程获取了互斥量,哪一个线程就要负责释放这个锁,其余线程越俎代庖去释放互斥量是无效的。指针

11.临界区是比互斥量更加严格的同步手段。互斥量和信号量在系统的任何进程都是可见的,也就是说,一个进程建立了一个互斥量或信号量,另外一个进程试图去获取该锁是合法的。然而,临界区的做用范围仅限于本进程,其余的进程没法获取该锁,除此以外,临界区具备和互斥量相同的性质。

12.一个函数要成为可重入的,必须具备以下几个特色:
  1.不使用任何(局部)静态或全局的非const变量;
  2.不返回任何(局部)静态或全局的非const变量的指针;
  3.仅依赖于调用方提供的参数;
  4.不依赖任何单个资源的锁(mutex等);
  5.不调用任何不可重入的函数。
  可重入是并发安全的强力保障,一个可重入的函数能够在多线程环境下放心使用。

13.volatile关键字试图阻止过分优化,volatile基本能够作到两件事情:
  1.阻止编译器为了提升速度将一个变量缓存到寄存器内而不写回;
  2.阻止编译器调整操做volatile变量的指令顺序(即便volatile可以阻止编译器调整顺序,也没法阻止CPU动态调度换序);

14.CPU的乱序执行能力让咱们对多线程的安全保障的努力变得异常困难。所以要保证线程安全,阻止CPU换序是必需的。遗憾的是,如今并不存在可移植的阻止换序的方法。一般状况下是调用CPU提供的一条指令,这条指令经常被称为barrier。一条barrier指令会阻止CPU将该指令以前的指令交换到barrier以后,反之亦然。换句话说,barrier指令的做用相似于一个拦水坝,阻止换序“穿透”这个大坝。
  关于barrier()宏实际上也是优化屏障:

  #define barrier() __asm__ volatile (”lwsync”)

  CPU越过内存屏障后,将刷新本身对存储器的缓冲状态。这条语句实际上不生成任何代码,但可以使gcc在barrier()以后刷新寄存器对变量的分配。

  1.set_mb(),mb(),barrier()函数追踪到底,就是__asm__ __volatile__("":::"memory"),而这行代码就是内存屏障;   2.__asm__用于指示编译器在此插入汇编语句;   3.__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。

相关文章
相关标签/搜索