• 《程序员的自我修养》学习笔记(一):简介


    1. 从Hello World说起

    2. 万变不离其宗

    3. 站得高,望得远

    4. 操作系统做什么

    不让CPU打盹

    监控程序监控CPU使用——多道程序;

    每个程序运行一段时间后主动让出CPU——分时系统;

    多任务系统:应用程序以进程方式运行;抢占式CPU分配。

    设备驱动

    操作系统是对硬件抽象;

    操作系统中的硬件驱动程序完成硬件细节的实现;

    操作系统为硬件厂商提供一系列接口和框架。

    5. 内存不够怎么办

    简单分配物理内存的问题:地址空间不隔离;内存使用效率低;程序运行的地址不确定。

    关于隔离:虚拟地址空间;物理地址空间。

    分段(Segmentation)

    把一段与程序所需的内存空间大小的虚拟空间映射到某个地址空间

    解决了问题1、3

    分页(Paging)

    把地址空间人为地分成固定大小的页,于是产生了:虚拟页;物理页;磁盘页。

    页映射

    内存共享;

    内存保护;

    采用Memory Management Unit部件进行页映射;

    CPU发出虚拟地址-->MMU转换成物理地址-->物理内存。

    6. 众人拾柴火焰高

    线程基础

    线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈组成。

    多个线程组成进程,线程间共享

    内存空间:代码段;数据段;堆。

    进程级资源:打开文件;信号。

    多线程的优点:

    有效利用等待时间;交互与计算;程序逻辑要求;多CPU或多核计算机;与多进程相比更高效的数据共享。

    线程的访问权限

    可以访问进程内存所有数据:全局变量;堆;函数内的静态变量;程序代码;打开文件。

    线程私有空间:栈上数据——函数参数;线程局部存储空间(Thread Local Storage)上的数据;寄存器上数据——局部变量。

    线程调度与优先级

    不断在处理器上切换不同的线程——线程调度。

    线程状态:运行;就绪;等待。

    调度方法有轮转法和优先级调度法。

    优先级设置策略

    a.用户设置

    b.系统设置,其考虑因素有:

    进入等待的频繁程度:频繁等待——IO密集型线程;很少等待,用尽时间片——CPU密集型线程。IO密集型更容易提升优先级;

    等待时间长短,等待时间过长的线程将饿死。

    可抢占式线程和不可抢占式线程。抢占式为当线程用尽时间片后会被强制释放CPU,而进入就绪状态。早期系统线程不可抢占,只能线程自己发命令放弃执行,主动进入就绪状态。不可抢占线程主动放弃执行的情况:1)线程试图等待某事件;2)线程主动放弃时间片。

    Linux的多线程

    Linux内核不存在真正意义的线程概念。Linux将所有执行实体都称为任务(Task)——具有内存空间、执行实体、文件资源。任务间可以共享内存空间。系统调用创建新的任务:

    fork:复制当前进程,与原任务一起共享写时复制的内存空间;

    exec:使用新的可执行映像覆盖当前可执行映像,fork与exec配合产生新任务;

    clone:创建子进程并从指定位置开始执行,用于产生新线程。

    线程安全

    维护并发数据的一致性对多线程程序很重要

    竞争与原子操作

    以自增为例子说明一句高级程序代码可能被转化为多句汇编指令,从而导致错误,而单指令的操作成为原子操作,其执行不会被打乱。CPU和操作系统提供了相应的原子操作接口。

    同步与锁

    同步(Synchronization),即一个线程访问数据未有结束时,其他线程不得对同一个数据进行访问,它实现了数据访问的原子化。

    锁(Lock)——实现同步最常见的方法,其概念为:线程访问资源或数据前首先试图获取(Acquire)锁;访问结束时释放(Release)锁;当试图获取锁时,锁已被其它进程占用,线程将等待。

    二元信号量(Binary Semaphore),最简单的锁,只有占用/非占用两种状态;

    信号量(Semaphore),允许多个线程并发访问资源,初始值N的信号量允许N个线程并发访问。其操作:

    线程访问资源:信号量减1;如果信号量小于0,线程进入等待状态,否则继续执行;

    线程释放资源:信号量加1;如果信号量小于1,唤醒一个等待中的线程。

    互斥量(Mutex),资源仅同时允许一个线程访问,与二元信号量相类似。

    与二元信号量不同的是,信号量在整个系统可以被任意线程获取并释放;互斥量则要求获取和释放互斥量的是同一个线程。

    临界区(Critical Section),进入临界区——锁的获取;离开临界区——锁的释放。

    与信号量、互斥量的区别是,信号量、互斥量在系统的任何进程里都是可见的;临界区的作用范围仅限于本进程,其他进程无法获取该锁

    读写锁(Read-Write Lock),对于同一个锁,有两种获取方式:共享的(Shared),独占的(Exclusive)。当锁处于自由时,两种获取锁的方式都成功;当锁处于共享状态时,其他线程以共享的方式获取锁仍然成功;当锁处于独占状态时,其他线程必须等待其释放。

    条件变量(Condition Variable),可被多个线程等待(线程等待某个事件),条件变量被唤醒(事件发生),所有线程可以一起恢复执行。

    可重入与线程安全

    函数被重入,表示函数没有执行完成,由于外部因素或内部调用,又一次进入该函数执行。发生重入的情况有:多个线程同时执行这个函数;函数自身(可能经过多层调用后)调用自身。

    函数可重入的条件:

    不使用任何(局部)静态或全局的非const变量;

    不返回任何(局部)静态或全局的非const变量的指针;

    仅依赖与调用方提供的参数;

    不依赖任何单个资源的锁;

    不调用任何不可重入的函数。

    过度优化

    编译器和CPU优化带来两个问题:编译器为提高变量访问速度,将其缓存在寄存器,即使发生写操作也不写回主存(互斥问题);CPU动态调度,交换两条相邻指令的执行顺序(同步问题)。

    使用volatile关键字试图阻止过度优化,该关键字能够。

    1)阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回;

    2)阻止编译器调整操作volatile变量的指令顺序。

    但它不能阻止CPU动态调用换序,文中以Singleton为例说明,指出new指令包含三步操作,多线程和CPU动态调用情况下可能导致错误。需要调用CPU提供的指令barrier阻止其进行指令交换。

    多线程内部情况

    内核线程由多处理器或调度实现并发。而用户实际使用的是用户态线程,并不一定在操作系统内核里对于同等数量的线程,于是产生了用户线程与内核线程的三种模型

    一对一模型

    线程间的并发是真正的并发,一个线程受阻塞并不影响其他线程,在多处理器上有很好表现。Linux中使用clone;Windows中使用CreateThread创建。但其缺点为:内核线程的数量有限制;内核线程调度时,上下文切换的开销较大,导致用户线程执行效率低。

    多对一模型

    多个用户线程映射到一个内核线程上,线程切换由用户态代码实现,速度快。但如果一个用户线程受阻塞,其他线程均无法执行,而且未能利用多处理器。

    多对多模型

    多个用户线程映射到多于一个但少量的内核线程上。一个用户线程阻塞并不会使所有用户线程阻塞,对用户线程数量没有限制,在多处理器系统上的运行效率也有一定提升。

  • 相关阅读:
    docker运行springboot应用
    docer运行node
    Git添加仓库
    安装docker-compose
    centos安装docker
    Centos设置软件源
    ubuntu安装docker
    ubuntu安装阿里云镜像地址
    docker加速器配置
    配置Spring发送邮件
  • 原文地址:https://www.cnblogs.com/alonecat06/p/2868482.html
Copyright © 2020-2023  润新知