【第 1 周】深入理解计算机系统共读心得体会

所在小组:第五组 (所有成员回帖)
组内昵称: 李锦锐、张学广、陈松彬、王传义、肖思成、郑伟钊、孙恒


By: 张学广

深入理解计算机第一章
这一章整体介绍了下计算机操作系统的概念,结构,并从hello程序串通了程序执行的流程。
首先,理解操作系统是作为硬件与应用程序之间的媒介,通过对底层硬件的封装和抽象,提供简单的接口给上层应用程序使用,包括文件是对io设备的抽象、虚拟内存是对内存的抽象,进程是对一个运行中程序的抽象(包括了它占用的资源)
之后是对程序是如何编译执行的介绍,简单来说就是将我们能理解的高级语言编译链接得到计算机能理解的二进制文件,这个文件也被称为可执行文件。这个文件首先被加载到内存,cpu经历取指、译码、执指的过程完成对程序的运算执行,其中pc寄存器是用来告诉我们下一条执行的指令地址的,通过这样的流程执行完整个程序。
最后是对并发的介绍,我们计算机上的程序并不是一个个执行的,而是在我们看来多个一起执行的,cpu只有一个(暂不说多核),同时只能有一个程序在运行,所以并发可以理解为cpu在多个程序切换,因为切换太快,所以在我们看来是同时执行的,这个是由时钟中断实现的


Chapter1

Author: @陈松彬

Date: 2020-9-20

第一章作者从一个很高的视角,为我们剖析了操作系统的构成(冯诺伊曼体系)与运行机制(cpu取指令,执行指令),每一个点细挖下去都会是一个大章节。我想先把一些疑问抛出来,读完之后的章节之后再重新回来回顾第一章。

思考

  • 用分层和抽象的视角去思考问题

    分层:对于计算机系统这么复杂的东西,可以将其分为三大层,层与层之间规定了交互的接口:

    • 硬件层
    • 内核层
    • 应用层

    抽象:对于不同的IO设备,在内核看来,都是一个文件,内核读写的时候都是用的同一个接口write/read,具体的读写实现由各个硬件厂商的驱动完成

    这让我联想到了我们做微服务设计的时候,要进行模块的拆分,模块与模块之间需要约定好交互的接口,每个模块各司其职其职

    • 对于系统设计的时候,如果比较复杂,不如先对系统进行模块划分,之后再考虑具体实现
    • 设计模块对外的接口时候,一定要尽量保证接口定义,不要轻易改变,因此设计接口的时候,不妨多些考虑
  • 分层的存储

    在操作系统中,充分利用了时间局部性和空间局部性的原理,使用分层的存储结构,在保证了成本的同时,大大提高了CPU执行效率。

    (1)在应用设计的时候,考虑存储,也可以借鉴这种存储设计,比如设计一个TSDB(Time Series Database):

    假设:对于t-1 day的相关查询占据了总查询90%,而对于更早之前的查询只占据了10%

    反过来,t-1day的所有数据只占了总存储的1%,其他时间的数据占总存储的99%

    我们可以利用这个原理:

    • t-1 day的数据存储在内存
    • 其他数据存储在SSD、或者更廉价的机械硬盘

    这样在能保证大部分查询的速度的同时,而且大大节约了存储成本

    (2)在编写代码的时候,我们也可以利用这个分层的存储,比如同一个功能可以用数组和链表实现,并且需要遍历其中的元素,那么使用数组实现这个功能的性能是不是就会更好?

    sum := 0
    for element := range array or linklist
    	sum += element
    

疑问

  1. 操作系统与内核的关系?
  2. 系统调用的具体流程?
  3. 怎么理解系统调用中”陷入内核“这个概念?
  4. CPU中,多级缓存L1、L2、L3与内存的一致性如何保证?

By: 王传义

第一周打卡: 
关于 一个程序是如何执行的,具体来说在单核下 执行2个字如何 理解的。

(1) shell 执行fork +exec命令运行一个程序。因此可见程序运行需要一个地址空间,包括只读段,堆栈这些虚拟空间. 这个时候经常进程创建成功。这只是静态部分,如果单任务情况下已经满足了。

(2) 进程执行具体是通过线程调用(默认一个线程)完成的。
场景:在执行过程需要IO操作 鼠标键盘,,网络 信号等任务时候。需要操作系统中断机制 完成协同工作。
例如。通过信号让一个阻塞在慢系统调用的线程从睡眠状态到运行状态。

(3)即使在单核情况下,除去中断外,系统调用也会引起cpu上下文切换。
vmstat 查看 in数量小于cs的印证了,上下文切换触发原因多种(进程,线程 切换 和中断切换)
(4) 有什么办法减少上下文切换?
a :无锁编程 ,一般锁会触发中断,内核用的说和普通高级说还不一样
b: 协程:上下文切换在内核完成,在用户态完成。
c:原子操作 
d:减少线程数量。

因此可见,一个程序运行,这里抽象为进程这个概念。这个概念隐含很多东西,不仅是独占那么简单。需要更加复杂机制。

By: 肖思成


By: 李锦锐

Chapter 1 -Notes

Chapter 1 is the roadmap of this book started by a simple example that every programmer should have known - The “Hello world” program.

Unlike other book’s, The author break this program down into serval part and introduces the idea in computer systems by tracing the life cycle of the program, which give me a more clear picture of what this program doing in machine level.

There is a few key concepts I would like to bear in mind in the future .

Bits + Context = Information .

If you search wiki for the definition of information, there is a lots[bad expression] of sayings. But the author says, in the computer science context, information is equal to bits with context. information can be defined to bits with context.

  • All information in a system is represented as a bunch of bits . For example, the hello.c program is stored in a file as a sequence of bytes.

  • the same bits with different context , can be represent different things. it could be an integer, floating-point number, character strings, or machine instruction.

what is it context the above talking about ? I thought it mean the state of cpu register or the state of the process, for which it can take the same input bits, but interpret it into different objects.

A memory hierarchy

Why we need memory hierarch ?
Because, On top of the memory hierarchy memory has faster access time, less capacity and higher cost per bit stored. At the bottom there is larger storage capacity, slower access time and lower cost per bit stored.

As the book says

As we move from the top of hierarchy to the bottom, the devices become slower, larger, and less costly per byte. 

More Detail:
Memory Hierarchy Introduction

Operating system manages the hardware

There two primary purposes of operating system:

  • to protect the hardware from misuse by application
  • to provide applications with simple and uniform mechanisms for manipulating low-level devices .

So it can be discussed into three parts: process、virtual memory、files.

Level of concurrency

  • Concurrency, refer to the general concept of a system with multiple, simultaneous activities
  • Parallelism, refer to the use of concurrency to make a system run faster.

There is a three ways of concurrency:

  • The thread level
  • Instruction level
  • Single-Instruction, Multiple-Data (SIMD) Parallelism.

What is the different of these three levels ? how to distinguish them from each other ?

REFERENCE

Aside

  • You are 【poised 】for an exciting journey

  • You will learn the promises and 【pitfalls】 of concurrency

  • C also lacks 【explicit】 support for useful abstractions .

  • We do not know 【the inner workings】 of the compiler .

  • The 32-bit code that has become 【ubiquitous】 on machines running Linux, Windows.

  • The shell prints a prompt and waits for the next input command line.

  • The executable program 【resides】 on the disk.

  • A system can get the effect of both a very large memory and a very fast one by 【exploiting】 locality, 【the tendency for 】programs to access data and code in localized regions.

  • We can think of the operation system as a layer of software 【interposed 】between the application program and the hardware.

  • This concludes our initial 【whirlwind】 tour of systems.

这些有趣的表达,在中译本会被翻译成什么样呢?

By : 郑伟钊

By: 孙恒

  1. 虽在小组:二组
  2. 组内昵称:可可
  3. 心得体会

整理心得体会的时候,又按照目录把第一章过了一下。先抛开具体的知识点,第一章就是把计算机的整体给我们过了一下。从最开始的code或者data是如何表示(bits + context),再到code是如何被计算机所理解(code-> machine-language)。接着就解释计算机是如何做到可以理解这些机器码的,以及一些额外的工作(eg. optimizing, avoiding security holes)。再从解释内存中的数据如何来,顺便引出内存IO速度和硬盘IO速度的差距,也就是后面经常提到的cache,并提出the main idea of memory hierarchy is thar storage at one level serves as a cache for storage at the next lower level。觉得这句话是对计算机存储特别好的抽象,也就是说我们设计各级别存储的时候可以参考已有的方案去做,比如内存的l1 cache的缺页策略可以节前给page fault(当前具体实现上可能不完全相同,但是个人特别喜欢这种抽象的解释和设计)。

再往后引入了硬件,说到OS,必不可少的硬件。硬件上的OS对上提供了统一的接口,使得我们大多数人不必关心底层的东西,我们可以插入一个 U盘,一个ssd,一个机械硬盘等,然后对用户都是文件夹,对程序员都是file。这种抽象很赞。同时在硬件上也提到了虚拟内存也是一个很好的抽象(这边更多引起的是思考,后面会简单写下,等待以后的回答)。硬件管理上出文件和内存外也提到了进程和线程(估计是因为进程包含着所有运行的上下文吧,疑问脸),那线程呢,在进程不足的情况下引入线程,为了并发。

一些思考

联系到GMP调度的时候协程的内存分配,go实现的内存逃逸以及栈扩容等都是基于内存。

感觉突破了传统对栈的认知,

go的栈在上图堆上,自己实现栈的调度切换。所以也就没有了栈溢出的问题。

一些疑惑

  1. go的栈切换在扩容后的表现

    重新在heap上找连续地址进行分配?

    ​ 可以观察分配前后 栈内数据地址验证

  2. go函数中的字段内存分配在哪里

  3. 对于go来说,传统的stack是用来做什么

  4. Linux的线程是固定大小的,是因为分配到 user stack的缘故吗?

process → thread → goroutine

最后,我可能永远也不会去写一个操作系统,但其中学习到的思想可以影响很多。无论是阅读大佬的代码,还是自己搬砖时候的架构,即使学得一二,也不算妄读此书。

  1. 所在小组:静默组
  2. 组内昵称:Mr_李冲
  3. 本周大致看了CSAPP的第一二章内容,主要是加深了对一些基础概念的印象,从hello world程序出发,对操作系统的运行流程有一个大致的概念。第二章是信息的表示和处理,这部分在上学的时候有些概念不是特别清晰,现在再学一遍有一些收获的。

Amdahl定律:它描述系统某部分加速,对整个系统影响取决于该部分的重要性和加速程度。
计算机系统抽象的重要性:抽象是构筑庞大计算机世界的基石,通过抽象不同模块之间无需知道对方内部的具体实现细节即可很好地进行协同工作。
信息数据表示:信息最基本单元是位,再上层由位构筑整数、小数(浮点数)以及字符串三种最基础的数据类型,并通过位的逻辑处理及移位来实现最基本的计算操作。其中最需要注意的是浮点数的精度引发的问题。

1.	所在小组:静默
2.	组内昵称:helloLinux
3.	你的心得体会

◦	程序处理流程:预处理ccp,编译器ccl,汇编器as,链接器ld
◦	计算机文件流程:寄存器,L1cache,L2cache,L3cache,主存DRAM,本地磁盘,远程存储分布式文件或者web服务器

不管是任何语言,都需要了解计算机本质内容,硬件和软件的关联。而对于编译器、链接器,汇编器这些流程原理就是所谓各门语言自己的逻辑和硬件的处理

所在小组:2组

组内昵称:文弱书生

心得体会:

本周大概看了第一章和第二章2.2.3之前的章节,主要学习计算机系统的基本概念和描述,以及信息的表示、进制的处理等内容。

1.信息的本质是具有上下文的位

2.学习了简单的程序生命周期

预处理(*.i)、编译(*.s)、汇编(*.s)、链接(二进制文件)

3.简单的系统硬件组成。

总线、IO设备、主存、硬盘、处理器(核心是PC)

4.程序运行运行时,系统硬件的操作过程

shell将字符逐一读入寄存器,再放到内存中,从磁盘加载可执行的hello文件到主存,包括最终要输出的字符也会一并加载。处理器开始执行hello,从main函数开始,将字符串从主存复制到寄存器,再复制到显示设备。

5.存储器的层次结构

寄存器、L1高速缓存、L2高速缓存、L3高速缓存、主存、本地二级存储、远程二级存储

上一层存储器作为低一层存储器的高速缓存。

6.操作系统对于硬件各部位的抽象表达。

进程:是操作系统对于一个运行程序的抽象,进程切换会有上下文的切换和管理

线程:每个线程都运行在进程的上下文中,进程中的代码和全局数据每个线程共享

虚拟内存:对内存的抽象,每个进程都好像独占一块内存,每个进程看到的内存地址都是一致的,称为虚拟地址空间

文件:字节序列,在Unix体系中,一切都是文件,包括IO设备、网络等等

7.由Amdahl定律Tnew = (1 - α)Told + (αTold)/k = Told[(1 - α)+α/k]计算系统加速比等参数,从公式可知,系统的某个部分运行时间占比整个系统越大,优化的效果越好

但是这个加速正比加速,某个部分加速到一定程度,在加速的效果就会下降很多

8.不同进制的表示和相互转换

| 十六进制数字 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |

| :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: | :—: |

| 十进制值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

| 二进制值 | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |

PS:笔记 https://github.com/reading-notes-rock/CSAPP

  1. 所在小组: 静默组
  2. 组内昵称:frances
  3. 心得体会

第一章主要是以helloworld程序为切入点对计算机系统整体进行阐述,硬件和软件如何通过协同操作来运行应用程序。对于编译链接的流程和基本概念比较熟悉所以本章主要记录以下两点笔记:

  • Amdahl定律:当对系统的某个部分进行加速时,整体性能的影响取决于该部分的重要性和加速程序。该定律主要用于并行计算领域预测多个处理器时的理论最大加速比,实际场景中程序往往并不能有效的利用多核,因为系统中不可避免的会存在一些需要串行访问的资源。因此在多核处理器中还要考虑如何降低串行计算部分的比例以及降低交互开销。
  • Concurrency和parallelism:
    • 线程级并发:单处理器系统中并发是通过进程执行切换来模拟出并发的效果,允许多用户同时与系统交互。随着多核处理器的出现减少了执行多个任务时模拟并发的需要,但是要求程序以多线程方式进行编写来高效地并行执行。另外,超线程允许一个CPU执行多个控制流的技术,常规的处理器大约需要2w个时钟周期做线程切换,超线程处理器可以在单个周期的基础上决定执行哪个线程(FMT,通过拉长每个线程的平均执行时间来实现随时切换,英伟达和AMD的GPU中用的比较多)。现实中绝大多数程序并不会占用CPU的所有资源,超线程的引入主要就是为了更好得利用空闲资源。
    • 指令级并行:每条指令从开始到结束大约需要20+个时钟周期,但是处理器通过流水线技术,将指令划分为不同的阶段通过并行操作能够达到每个时钟周期2-4个指令的执行速率。
    • 单指令多标量并行SIMD:通过特殊的硬件来支持一条指令产生多个可以并行的操作。
      这些应该都是本科学习过的知识点,重新温习了一遍。
  1. 所在小组:静默组
  2. 组内昵称:xuan
  3. 心得体会
  • 第一章计算机系统漫游 , 信息量巨大的基础概览(x86-64, posix 计算机组成原理 编译原理等等)。熟悉的大象放到冰箱里分几步(编译过程)。搬砖这么久只是在感受它,并没很好的理解它。

    正如前言讲的学习系统的方法是 do it,1.5谈高速缓存性能差异巨大,如果不看213视频的copy demo(cache line)理解起来不是那么清晰。

    老android用户 对安迪-比尔定律比amdahl定律对认识更深刻,先把资源占了再聊性能。

    分时系统下 并行/并发永远是热点,c10K演化到c10M问题,经常能看到标题党百万连接、千万并发,还有window资源管理器128个cpu在跳舞。

    系统和硬件之间如何结合-抽象,如果不行再抽象,自底向上不停抽象 :rofl:

小破站 2015 CMU 15-213

论可计算数

从左往右越来越强调以程序员角度

  1. 所在小组: 第六组
  2. 组内昵称: 利健锋
  3. 心得体会
  • 我们的编码怎么在计算机系统中执行
  • IO 才是计算机系统的主角
  • 了解整个计算机系统,因为能让你在写代码时拥有更广阔的思维把控整个大局
  • 位运算和计算机各种进制之间起到的作用

其中第一章主要能够体会到本书对计算机拆分的一些章节和模块划分,贯穿了真本书所涉及的一些知识点或者说概念。

读完第一章之后能够有一个大体的计算机结构/架构的认识,每个知识点都可以选择性的放大、深入,例如 编译 可以深入探讨 《编译原理》。

我在某一个地方看到过这么一个问题 “编译器是怎么编译中文变量名的?”,目前我还回答不出来,觉得蛮有意思

总结:让自己逐渐不再黑盒写 BUG :crazy_face:

  1. 所在小组:静默组
  2. 组内昵称:黄小黄
  3. 心得体会
  • 编译过程
    编译系统(compilation system):预处理器(pre-processor)、编译器(compiler)、汇编器(assembler)、链接器(linker)

    • 预处理阶段:处理字符#开头的命令,即:1)将头文件的内容插入程序文本中。2)宏定义替换。3)条件编译(#if #ifdef),不被编译的部分变为空行。4)删除注释
    • 编译阶段:通过编译器将源程序翻译成汇编程序(assembly-language program)
    • 汇编阶段:将汇编程序翻译成机器语言指令,并将其打包成可重定位目标程序(relocatable object program)
    • 链接阶段:链接器将各个.o文件合并成可执行文件。链接器使得分离编译成为可能。在编写大型程序时,可将模块分小,由此达到独立修改和编译不同模块的目的:未被修改的模块不用重新编译,而只需将修改后的模块编译,重新链接即可。
  • 系统的硬件组成

    • 总线(buses):通常总线被设计成传送定长的字节块,也就是字。字中的字节数*(字长word size)*,是一个基本的系统参数,大多数机器为4个字节(32位)或8个字节(64位)
    • I/O设备(I/O devices):每个I/O设备都通过一个*控制器(controller)或者适配器(adapter)*与I/O总线相连接。控制器和适配器的区别主要在于其封装方式:控制器是置于I/O设备本身的或主板上的芯片组,而适配器则是一块插在主板插槽上的卡
    • 主存(main memory:主存即内存,是一个CPU能直接寻址的临时存储设备,在处理器执行程序时用来存放程序和程序处理的数据。物理上,主存由一组DRAM组成。逻辑上,存储器是一个线性的字节数组,每一个字节都有其唯一的地址(数组索引),这些地址均从零开始。
    • 处理器(processor):CPU的核心是一个字长的寄存器,称为程序计数器(program counter PC)。在任何时刻,PC都指向内存中的某条机器语言指令。处理器看起来只是它的指令集结构的简单实现,但实际上现代处理器使用了非常复杂的机制*(微体系结构 microarchitecture)*来加速程序的执行
  • 进程
    是操纵系统对一个正在运行的程序的一种抽象。并发运行是指一个进程的指令和另一个进程的指令是交错执行的。一个cpu看上去都像是在并发地执行多个进程,这是通过处理器子进程间切换来实现的。操作系统实现这种交错执行的机制称为上下文切换。所谓的上下文是指操作系统保持跟踪进程运行所需的所有状态信息,包括PC和寄存器文件(由一些打个字长的寄存器组成,每个寄存器都有唯一的名字)的当前值以及主存的内容。当操作系统决定要把控制权从当前进程转移到另一个进程时,就会进行上下文切换,即保存当前进程的上下文、恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始。

  • 线程
    一个进程可以由多个称为线程的执行单元组成,每个线程运行在进程的上下文中,并共享同样的代码和全局数据,线程比进程更易共享数据,而且更高效。

所在小组:第 3 组
组内昵称:Hector
心得体会:

第 1 章 计算机系统漫游

第 1 章以 hello world 程序为例介绍了计算机硬件组、软件运行过程、存储器层次结构、操作系统管理硬件等内容。
  • 程序运行:预处理 -> 编译 -> 汇编 -> 链接

  • 系统的硬件组成:总线 + I/O设备 + 主存 + 处理器

  • 缓存主要是利用局部性原理来提高程序性能。存储器层次结构的主要思想是上一层的存储器作为低一层存储器的高速缓存。

  • 操作系统的主要作用是防止防止硬件资源被应用程序滥用以及向应用程序提供简单一致的机制来访问硬件资源。操作系统主要是通过几个抽象(进程、虚拟内存、文件)概念来实现这些功能。

  • 虚拟内存让每个进程都以为自己在独占地使用主存。每个进程看到的是虚拟地址空间,从低到高主要分为程序代码和数据、堆、共享库、栈、内核虚拟内存。

  • Amdahl 定律:当我们对系统对某个部分进行加速时,其对系统整体虚拟对影响取决于该部分对重要性和加速程度。

  • 并发和并行的三个层次:(1)线程级并发(2)指令级并行(3)单指令、多数据并行

第 2 章 信息的表示和处理

第 2 章主要讲述了如何在计算机上表示信息、处理信息等。
  • 计算机可能没有产生预期等结果,但是至少它是一致的。
  • 二进制、十进制、十六进制的表示及转化。
  • 位运算(~、&、|、^) 与逻辑运算( ||、&&、!)
  • 移位运算:左移、右移(逻辑右移、算术右移)
  1. 所在小组:第六组
  2. 组内昵称:之昂
  3. 心得体会:
  • 为在系统上运行.c程序,每条语句必须转化成能被机器识别的低级机器语言指令,即可执行目标文件。从源文件到目标文件的转化由编译器驱动程序完成,将源程序.c文件翻译成一个可执行的文件,整个翻译部分分为4阶段:

  • 系统硬件组成:总线、I/O设备、主存、处理器(CPU);

  • 高速缓存:hello程序的机器指令最初存放在磁盘上,程序加载时,能够被复制到主存;运行时又从主存复制到处理器。直观的发现这些互相复制的过程其实就是程序变慢的过程。针对处理器和主存之间的差异,采用更小更快的存储设备,即高速缓存存储器(cache),作为暂时性的集结区,存放近期可能会需要的信息。使得当前的大部分内存操作都能够在快速的高速缓存中完成。

  • 进程:对正在运行程序的抽象描述,一个系统可同时运行多个进程,每个进程看似在独占使用硬件。其中并发运行表示的是一个进程的指令和另一个进程的指令交错执行。先进的多核处理器同时能够执行多个程序,靠处理器在进程间切换(上下文切换)完成。

  • 线程:一个进程可包含多个线程,每个线程都运行在进程的上下文中。

  • 文件:字节数列。磁盘、键盘、显示器凡通过I/O操作的均可看作为文件。

    第一次读这本书,前面内容都是对计算机的基础认知,找工作的时候背了很多面试题,和现在已经读过的这部分内容来对比,觉得当时的认识确实不够深刻,可以书面的表达名词意思,但是核心底层还是联系不起来、或者说没有自己的见解。之后可能还要回来再看几遍,前后知识串通,希望能真正理解,挖透。

CSAPP共读心得体会 - 第一周

  1. 所在小组:第三组

  2. 组内名称:hhhhhhe

  3. 心得体会:

  • 操作系统如何编译一个hello.c文件?
  1. shell接收到用户输入gcc -o hello hello.c

  2. shell首先检查开头的第一个英文单词是否为shell内置命令,如果不是则尝试将其作为可执行文件加载到内存中。shell如何尝试找到gcc这条命令的呢?答案是通过环境变量PATH,通常Linux下可执行文件会存放在/usr/local/sbin,/usr/local/bin,/usr/sbin,/usr/bin等目录中,shell会查找这些目录下是否有gcc命令。如果有,shell找到该命令文件对应的inode号,获得文件的inode信息,inode信息包括文件在磁盘的数据块位置。找到文件所在的磁盘位置之后,shell开始从磁盘的指定块中读取文件内容(shell会如何读取文件内容?),加载到内存中开始执行(文件如何从磁盘加载到内存?)。

  3. gcc编译器开始执行后,分为四个步骤。1、预处理阶段:将#开头的头文件内容和原文件内容一起写入到一个新的以.i结尾的文件中;2、编译阶段:将高级语言指令翻译成汇编,为什么不直接翻译成机器语言?;3、汇编阶段:将汇编翻译成机器指令,将这些指令打包到hello.o文件;4、链接阶段:链接器ld将编译好的hello程序与所用到的库函数链接起来。

  4. 至此hello.c文件备编译成了一个可执行程序hello

  • 操作系统如何执行hello文件?
  1. shell接收输入./hello

  2. 尝试运行加载hello文件,将文件从磁盘复制到内存中

  3. CPU开始执行内存中的hello程序

  4. 执行完成的结果发送到显示器上最终显示出来

  • CPU缓存为什么重要?
  1. 首先要明白缓存中存放的是什么?答案是CPU最近使用到的数据(曾经执行过的命令)或将会使用到的数据(预读文件内容)

  2. 然后需要知道CPU的运算速度和内存数据的读取速度之间差距非常大

  3. CPU需要一个地方存放曾经使用过的数据或将来会用到的数据,内存显然不行,速度差距太大。这时CPU缓存就上场了。通常CPU有L1、L2、L3级缓存,其中L1级缓存的读取速度与CPU运算速度最相近。

  4. 有了CPU缓存,CPU的运算效率会降低被低速设备影响的概率(通常CPU90%的数据都可以从缓存中获取),能更高效的执行。

  • 抽象
  1. 文件是对I/O设备的抽象?把所有的I/O设备抽象成文件进行处理

  2. 虚拟内存是对内存和磁盘I/O设备的抽象?把进程所需的内存操作和I/O操作抽象成对虚拟内存操作

  3. 进程是对处理器、内存和I/O设备的抽象?把程序运行所需的CPU时间片、内存空间、I/O设备操作等资源抽象成进程

  • Amdahl定律
  1. 结论就是:想要显著加速整个系统,必须提升全系统中相当大部分的速度。说人话就是别想着只优化一个地方就能获得好性能
  • 信息流处理
  1. 这一章内容细致,没细看。
  1. 所在小组:第7组
  2. 组内名称:李佳
  3. 心得体会:
  • 编译系统
    • 预处理:GCC编译器为例,将头文件内容复制到程序文本中
    • 编译:将高级语言翻译成汇编语言
    • 汇编:将汇编语言翻译成机器语言
    • 链接:将系统函数合并到程序中,得到可执行的二进制文件
  • 进程:进程是正在运行的程序的状态信息.不同进程交错执行,1保存当前上下文,2恢复新进程上下文,3将控制权传递到新进程,为上下文切换.进程运行所需的所有状态信息为上下文.一个进程可以包含多个线程.线程共享进程拥有的所有资源
  • 位运算使用
    • 乘除运算可以提高效率
    • 例题2.5亿整数中找出不重复的整数:采用2-bitmap,00不存在,01出现一次,10重复出现,共需内存2^32*2 bit = 1GB 内存.扫描所有整数,在对应位改变存储数据,扫描完后,查看bitmap把对应为是01的整数输出.
    • 40亿个不重复的unsigned int整数,判断给定整数是否在这些数中.512m内存,一个bit代表一个unsigned int值,扫描40亿整数,设置响应的bit位,读入查询的数,查看相应bit位是否为1.
  1. 所在小组:第七组
  2. 组内昵称:高华
  3. 心得体会

第一章

  • 我们看到的信息对于计算机来说都是 0 和 1 组成的序列,不同的 context 赋予了序列不同的含义。这句话对于理解计算机对信息的表示有很重要的意义。

  • 计算机作为整体来看非常的复杂,通过层层的抽象来简化之。抽象能力对于编程能力至关重要。

    • 文件:是对 I/O 设备的抽象

    • 虚拟内存:对主存和 文件的抽象

    • 进程:对处理器和虚拟内存的抽象

  • Amdahl 定律

第二章

  • 虚拟地址空间大小:总线位数决定计算机的字长(即指针数据的标称大小),字长决定了虚拟地址空间大小。

  • 大端法和小端法:不用太纠结为什么会有两种表示方法,就像磕鸡蛋应该先磕大的那头还是小的那头一样。在大多数情况下对于我们来说是透明的,但在某些特定情况,需要了解两种表示方法的差异。

    • 大端法:最高有效字节存在前面
    • 小端法:最低有效字节存在前面
  1. 所在小组:第七组
  2. 组内昵称:Shawn
  3. 心得体会: 从"Hello World"到计算机抽象

运行一个Hello World的程序,首先需要将.c源文件编译成可执行的二进制文件。这个过程可以被划分为4个步骤。

  • 预处理:处理以字符#开头的命令,将源文件"处理"为.i结尾的文件;
  • 编译:将.i文件"处理"为.s结尾的文件,它包含的是汇编语言程序;
  • 汇编:将.s结尾的文件翻译成机器语言指令,其包含在一个.o结尾的文件中;
  • 链接:只要有调用其它库(包括标准库)的函数,肯定就设计将多个.o文件进行合并,这个过程就是链接。执行链接操作之后,将生成一个可执行文件;

计算机在执行程序时,首先会将存储在磁盘的的可执行文件加载到主存中,随着指令的执行,"Hello World"字符串先经过总线被加载到邻近CPU的"寄存器文件"上,再经过总线被复制到输出设备上。

这个过程涉及到大量的时间来搬运数据,因此为了解决这个问题,计算机就引入了高速缓存。

一般而言,容量大的存储设备要比容量小的存储设备运行得慢,同时其价格也就越便宜。根据这个特兴,现代计算机普遍引入了多级缓存,形成了存储器层次结构,高一级的存储设备作为低一级存储设备的缓存而使用。最高级的缓存就是"寄存器文件",被称作是L0,本地磁盘可能作为L5存在,其可以视为可能存在的远程存储设备(L6)缓存。

为了防止硬件设备被程序滥用,同时也是为了屏蔽复杂的底层设备调用差异,程序一般是通过操作系统接口来完成底层设备的使用的。操作系统主要提供了3个抽象来为上次应用程序提供调用接口,这3个抽象分别为:

  • 进程:是对正在运行的程序的抽象;
  • 虚拟内存:是对计算机整个存储结构的抽象;
  • 文件:对计算机I/O设备的抽象;

最后聊聊Amdahl定律。其表示当我们提升了系统中某一部分的性能时,其对于整个系统的提升效果取决于该部分对于整个系统的重要性和该次提升的加速程度。也就意味着,盲目的执行优化,并不一定有明显的效果。

总结:第一章通过一个Hello World的程序引出了编译、高速缓存、操作系统、抽象等计算中非常重要对的概念,对整个计算机系统有了一定初步的认识。

  1. 所在小组:第二组
  2. 组内昵称:叶王
  3. 心得体会
    • 了解计算机系统是如何运作的,是一个软件工程师的基本素养。第一章从二进制位,一直讲到进程,再到多机之间的网络通信,还提到了并发和并行,在全书起到概论综述的作用。
    • 计算机硬件通过数字电路中电源电压高电平表示1,低电平表示0,从而携带了一个位(bit)的信息。而根据上下文(不同值,不同长度的位组合),计算机可以表示不同的信息。
    • 计算机的数据信息,可以简单分为数据和程序(当然,程序也是一种数据)。一般我们写的程序,比如C语言,C++,Java,Go等等,都是高级语言,计算机需要通过编译器或解释器「翻译」成二进制,处理器(CPU)才可以执行。
    • 计算机通过一些抽象,比如文件,虚拟存储,进程,虚机等,对计算机程序的运行过程做了很好的封装,比如,进程是资源分配的最小单位,静态的程序是代码,运行起来往往就是抽象出来的进程,进程的设计(比如虚机内存等)使它认为自己独占整个计算机。而线程是cpu调度的最小单位,一个进程可以有一个或多个线程,线程的设计是为了使cpu利用率提高,比如一个线程处于io,可以切换另一个线程进行计算。
    • 第一章后每一章对概论进行展开,第二章讲信息存储,就是从单个位(bit),扩展到多个位如何表示,以及如何运算。这些都是非常基础,却是真正去理解程序运行过程所必须的知识。下周完整看完第二章再细说。
  1. 所在小组:第七组
  2. 组内昵称:hayden
  3. 心得体会
  • 抽象在计算机系统中的重要性

    编码过程中高质量的抽象能提高代码的可扩展性和灵活性,在计算机系统中,操作系统通过提供不同层级的抽象来简化了上层应用程序编码的难度。通过抽象来隐藏了系统实现的复杂性,对外提供了一直的接口。就像我们开车一样,不管什么品牌的汽车,驾驶打开差别不大,汽车内部不管怎么改造,对外提供的操作不会改变。

    • 文件是I/O设备的抽象。
    • 虚拟内存是对主存和磁盘的抽象。
    • 进程是处理器、主存和I/O总体的抽象。

    通过抽象我们能理解操作系统的作用是什么?操作系统是将硬件与软件的协作抽象出一个个接口,通过抽象开发者能更好的在操作系统上开发出应用程序,无需关系操作系统内部硬件与软件如何协作,深入了解操作系统需要了解底层的实现,了解操作系统是如何来进行抽象的,如何让硬件软件来进行协调工作。

  1. 所在小组:静默组
  2. 组内继承:清风环佩
  3. 心得体会:

计算机硬件是一切的根本。

操作系统作为平台,用以抽象硬件、衍生应用。再强大的应用,总是要调用操作系统提供的接口。如果linux没有namespace和cgoups机制,大概也不会有docker。再往下一层,硬件层面做了限制,操作系统的诸多神奇功能也将施展不开。

之前看到一篇智能语音方面的文章,内容记不清了,大概意思是说,某款cpu型号添加了一个新的指令集,能够大幅度提高浮点运算能力,直接影响到了智能语音的识别速度和准确性。如此看来,那些工业级的cpu应该是有做过特定优化的,不会日常所用的泛用途cpu相同。

就我个人而言,第一章中提到的一级二级缓存,在平时工作的基本不会用到,在硬件层面提供的诸多功能,比如锁和原子操作,更加令我好奇的。虽然也有查阅过相关资料,但一直没有从头到尾的搞明白。与这些相比,第二章的内存表示和处理,繁琐但是重要,读起来的趣味性可能会少一些,但依然很期待下周的读书历程。

  1. 所在小组:第二组
  2. 组内昵称:李显良
  3. 心得体会

第一章 计算机系统漫游

  1. 计算机的数据组成 算机系统中所有的数据都是由一串比特表示,包括源程序文本文件、编译好的可执行文件

理解程序编译

程序的编译过程

可执行程序就是计算机可以识别并运行的机器语言指令。编译过程就是通过编译器将源程序翻译成机器语言指令的过程就是编译程序。(编译器如gcc也是一个可执行程序)

编译执行阶段

  1. 预处理阶段 将include头文件、宏定义原始代码插入到源程序文件中
  2. 编译阶段 cc1将程序编译为汇编代码,汇编代码以标准的文本格式描述了一条低级机器指令
  3. 汇编阶段 as汇编器将汇编代码翻译成机器语言指令,此时文件不再是文本文件,也是一个二进制文件,也称为可重定位目标程序
  4. 链接阶段 ld将程序需要依赖的系统库打包并入形成执行文件;如果链接的库为静态库则打包进入程序;如果是动态库
  5. 静态库和动态库的编译过程 静态库是通过ar将多个可重定位目标程序打包成一个静态文件,用于生成可执行文件阶段 动态库: 通过gcc shared将多个可重定位目标程序生成一个动态库,动态库在生成可执行文件只会进行引用不会链接到程序中,在程序启动运行才会载入,而且多个程序可以可以公用

总线

贯穿整个系统是一组电子管道称作总线,它携带信息字节并负责在各个部件间传递,通常总线被设计成定长的字节块也就是字;目前的32位系统、64位系统就和操作系统字的字节长度相关,总线每次数据交换只进行一个字的交互

高速缓存

存储设备的存储结构就像一个金字塔,越是从底往上,下层为上层的缓存,下层每字节成本更底但读写速度更慢,而上层存储更贵读写速度更快

第二章 信息表示和处理

信息存储

字节由8个位组成,可作为最小的可寻址的存储器单位,机器级的程序将存储器作为一个非常大的字节数组,称为虚拟存储器
  1. 指针 指针包含两个方面,值和值的类型;它的值表示某个对象的位置,类型表示那个位置上所存储对象的类型(方便后续根据不同类型做不同的计算)

寻址和字节顺序

多字节的对象(比如int32)被存储在多个连续字节序列,那对象的地址为所使用字节序列中最小地址
  1. 大端法 将数据按字节的单位将高位存储在底地址中
  2. 小端法 将数据按字节的单位将底位存储在底地址中
  3. 字符串 由于字符只需要一个字节表示,字符数组存储方式与字节顺序、字节大小无关

1、所在小组:第二组
2、组内昵称:梁广超
3、心得体会
第一章主要是概述性的介绍整本书之后的内容,主要有三个方面印象比较深,早期开发过一款处理字符串的程序,第一次接触字符串编码的时候,十分头疼,后来开了阮一峰的博客,才对此有清晰的了解,对于unicode字符集,有多种编码方方式,不像ascii字符集这么直接。常用的unicode编码方式有utf-8。其次是程序编译的预处理阶段,之前开发过一个程序根据配置生成对应erlang程序源代码文件,当时有几个宏文件很大,都是上千行,导致预处理之后,对于的源文件很大,导致最后编译速度随着项目的发展变得异常慢,之后的解决方案是根据功能对宏文件进行拆分,使得大部分文件都是一百行以内,但是编译速度快了很多。还有就是并发不是线程/进程越多越好,之前使用erlang,采用生产者消费者模型处理相关的业务,因为早期这是一个很小很冷门的功能,所以平时关注点很少,所以在并发上面没有做相关进程池,导致后期由于需求的变动,导致这个功能被频繁调用,但是处理完进程没有及时关闭,程序运行一段时间之后,残留了大量空转的进程,内存资源被极大的浪费;
第二章,主要是信息的存储,我平时使用的动态语言比较多,这个对于我来说,平时是比较少直接接触到的,体会比较深的有两个,一个是大小端问题,一个位运算。大小端的问题主要是当时开发游戏网络框架时,接触到,当时为了减小包体,是和前端商量一套协议,当时就涉及到大小端的问题。位运算主要是用于状态机的状态存储,当时游戏状态比较多,多大几十种,并且很多状态是可以叠加的,导致如果只是简单的采用列表的形式存储,会导致与前端同步时,数据量会比较大,当时就采用位运算形式记录、移除相关状态信息。