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


    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创建。但其缺点为:内核线程的数量有限制;内核线程调度时,上下文切换的开销较大,导致用户线程执行效率低。

    多对一模型

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

    多对多模型

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

  • 相关阅读:
    Analysis Services features supported by SQL Server editions
    Azure DevOps to Azure AppServices
    Power BI For Competition
    Win10开机“提示语音”以及”随机播放音乐”
    Azure DevOps
    Allow Only Ajax Requests For An Action In ASP.NET Core
    Mobile CI/CD 101
    Configure SSL for SharePoint 2013
    AWS Step Function Serverless Applications
    Cordova Upload Images using File Transfer Plugin and .Net core WebAPI
  • 原文地址:https://www.cnblogs.com/alonecat06/p/2868482.html
Copyright © 2020-2023  润新知