深入理解计算机系统(第三版)-----读书报告

开个贴,记录下该书的阅读心得。

只记录较为重要的章节内容。

第7章 链接

链接可以在编译时由静态编译器来完成,也可以在加载时和运行时由动态链接器来完成。链接器处理称为目标文件的二进制文件,它有3种不同的形式:可重定位的、可执行的和可共享的。可重定位的目标文件是由静态链接器合并成一个可执行的目标文件,它可以加载到内存中并执行。共享目标文件是在运行时由动态链接器链接和加载的,或者隐含地在调用程序被加载和开始执行时,或者根据需要在程序调用dlopen库的函数时。

链接器的两个主要任务时符号解析和重定位,符号解析是将目标文件中每个全局符号都绑定到一个唯一的定义中去,而重定位就是确定每个符号的最终内存地址,并修改对那些目标的引用。

静态链接器是由像GCC这样的编译驱动程序调用。它们将多个可重定位目标文件合并成一个单独的可执行目标文件。多个目标文件可以定义相同的符号,而链接器用来悄悄地解析这些可重定义的规则可能带来的错误。

多个目标文件可以被连接到一个单独的静态库中。链接器用库来解析其他目标模块中的符号引用。许多链接器通过从左到右的顺序扫描来解析符号引用,这个产生链接错误的根源。

加载器将可执行文件的内容映射到内存中,并运行之。链接器还可能生成部分链接的可执行目标文件,这样的文件可以引用共享库中的例程和数据。在加载时,加载器将部分链接的可执行文件映射到内存中,然后调用动态链接器,它通过加载共享库和重定位程序中的引用来完成链接任务。

被编译成位置无关代码的共享库可以加载到任何地方,也可以在运行时被多个进程共享。为了加载、链接和访问共享库的函数和数据,应用程序也可以在运行时使用动态链接器。

第8章 异常控制流

异常控制流,ECF,发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制。

在硬件层,异常时由处理器中的事件触发的控制流中的突变。控制流传递给一个软件处理程序,该处理程序进行一些处理,然后返回控制给被中断的控制流。

存在4种不同类型的异常:中断、故障、终止和陷阱。当一个外部I/O设备设置了处理器芯片上的中断管脚时,中断会异步地发生。控制返回到故障指令后面的那条指令。一条指令的执行可能导致故障和终止同步发生。故障处理程序会重新启动故障指令,而终止处理程序从不将控制返回给被中断的流。最后,陷阱就像是用来实现向应用提供到操作系统代码入口点的函数调用。

在操作系统层,内核用ECF提供进程的基本概念。进程提供给应用两个重要的抽象点:

(1) 逻辑控制流,它假装独自占用处理器。

(2) 私有地址空间,它假装独自地使用主存。

在操作系统和应用程序之间的接口处,应用程序可以创建子进程,等到子进程终止后再运行新的程序和捕捉来自其他进程的信号。信号的处理过程十分精细,每个系统都不大一样。

最后,在应用层,C语言程序可以使用非本地跳转来规避正常的调用/返回栈的规则,并且可以直接从一个函数跳到另一个函数。

第9章 虚拟内存

虚拟内存是对主存的一个抽象说法。支持虚拟内存的处理器通过使用一种叫做虚拟寻址的间接形式来引用主存。处理器产生一个虚拟地址,在被发送到主存之前,这个地址呗翻译成一个物理地址。从虚拟地址空间到物理地址空间的地址翻译要求硬件和软件紧密合作。专门的硬件通过使用页表来翻译虚拟地址,而页表的内容是由操作系统提供的。

虚拟内存提供3个重要的功能。第一,它在主存中自动缓存最近使用的存放磁盘上的虚拟地址空间的内容。虚拟内存缓存中的块叫做页。对磁盘上的页。对磁盘上页的引用会触发缺页,缺页将控制转移到操作系统中的一个缺页处理程序。缺页处理程序将页面从磁盘复制到主存缓存中,如果有需要,将写回被驱逐的页。第二,虚拟内存简化了内存管理,进而又简化了链接、在进程间简化了内存保护。

地址翻译的过程必须和系统中所有的硬件缓存的操作集成在一起。大多数页表条目位于L1高速缓存中,但是一个被称为TLB(页表缓存)的页表条目的片上高速缓存,通常会消除访问在L1上的页表条目的开销。

现在系统通过虚拟内存片上和磁盘上的文件片关联起来,来做初始化虚拟内存片,该过程被称之为内存映射。内存映射为共享数据、创建新的进程以及加载程序提供了一种高级的机制。应用可以使用mmap函数(mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中作用很大)来手工地创建和删除虚拟地址空间的区域。动态内存分配器可以直接操作内存,而无需类型系统的很多帮助。分配器由2种类型,显式分配器要求应用显示地释放它们的内存块;隐式分配器(垃圾收集器)自动释放任何未使用的和不可达的块。

对于开发者来说,管理和使用虚拟内存很容易出错,常见的错误有:间接引用坏指针,读取未初始化的内存,允许栈缓冲区溢出;假设指针和他们指向的对象大小一样,引用指针没有指向它们所指向的对象,误解指针运算,引用不存在的变量,以及引起内存泄露。

第10章 系统级I/O

Linux提供了少量的基于Unix I/O模型的系统级函数,他们允许应用程序打开、关闭、读和写文件,提取文件的元数据,以及执行I/O重定向。Linux的读和写操作会出现不足值,应用程序必须能正确地预计和处理这种情况。应用程序不应直接调用Unix I/O函数,而应该适应RIO包(全称 Robust I/O,即健壮的IO包),RIO包通过反复执行读写操作,直到传送完所有的请求数据,自动处理不足值。

Linux内存使用3个相关的数据结构来表示打开的文件,描述符表中的表项指向文件表中的表项,而打开文件表中的表项又指向v-node表中的表项。每个进程都有它自己单独的描述符表,而所有的进程共享同一个打开文件表和v-node表。理解这些结构的一般组成就能使我们清楚地理解文件共享和I/O重定向。

标准I/O库使基于Unix I/O实现的,并提供了一组强大的高级的I/O例程。对于大多数应用程序而言,标准I/O更简单,是优于Unix I/O的选择。然而,因为对标准I/O和网络文件的一些相互不兼容的限制,Unix I/O比标准I/O更应该使用于网络应用程序。

第11章 网络编程

每个网路应用都是基于客户端-服务端模型的。根据这个模型,一个应用是由一个服务器和一个或多个客户端组成,服务器管理资源,以某种方式操作资源,为它的客户端提供服务。客户端-服务端模型中的基本操作是事务,它是由客户端请求和跟随其后的服务端响应组成的。

客户端和服务端通过Internet来通信,Internet具有以下几个属性:(1)每个Internet主机都有一个唯一的32位名字(IP地址)。(2)IP地址的集合被映射为一个因特网域名的集合。(3)不同的因特网主机上的进程能够通过连接相互通信。

客户端和服务端通过套接字接口建立连接。一个套接字是连接一个端点,连接以文件描述符的形式提供给应用程序。套接字接口提供了打开和关闭套接字描述符的函数。客户端和服务端通过读写这些描述符来实现彼此的通信。

web服务器使用HTTP协议和他们的客户端彼此通信。浏览器向服务器请求静态或动态的内容。对静态内容的请求是通过从服务器磁盘取得文件并把它的输出返回给客户端来服务的。对动态内容的请求是通过在服务器上一个子进程的上下文中运行一个程序并将它的输出返回给客户端来服务的。CGI(公共网关接口)标准提供了一组规则,来管理客户端如何将程序参数传递给服务器,服务器如何将这些参数以及其他信息传递给子进程,以及子进程如何将它的输出发送回客户端。

第12章 并发编程

一个并发程序是由在时间上重叠的一组逻辑流组成的。进程是由内核自动调度的,而且因为它们有各自独立的虚拟地址空间,所以要实现共享数据,必须要有显式的IPC机制(IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程)。事件驱动程序创建它们自己的并发逻辑流,这些逻辑流被模型化为状态机,用I/O多路复用来显式地调度这些流。因为程序运行在单一进程中,所以在流之间共享数据速度很快。

线程是这些方法的混合。同基于进程的流一样,线程也是由内核自动调度的。同基于I/O多路复用的流一样,线程是运行在一个单一进程的上下文的,因此可以快速共享数据。

无论哪种并发机制,同步对共享数据的并发访问都是一个困难的问题。提出对信号量的P和V操作(PV操作是一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思)就是为了帮助解决这个问题。信号量操作可以用来提供对共享数据的互斥访问,也对像生产者-消耗者程序中有限缓冲区和读者-写者系统中的共享对象这样的资源访问进行调度。一个并发预线程化的echo服务器(echo服务是一种非常有用的用于调试和检测的工具)提供了信号量使用场景的很好的例子。

并发页引入了其他一些困难的问题。被线程调用的函数必须具有一种称为线程安全的属性。我们定义了4类不安全的函数,以及一些将它们变为线程安全的建议。可重入函数时线程安全函数的一个真子集,它不访问任何共享数据。竞争和死锁时并发程序中经常出现的一个比较棘手的问题。当开发者错误地假设逻辑流调度时会发生竞争,当一个流等待一个永远不会发生的事件时,就会产生死锁。

 

------------------------------------------------------------以下是视频内容--------------------------------------------------------------------------------------

***汇编语言寄存器是如何运作的?

1.寄存器是从左到右复制的,跟C语言正好相反。

汇编指令大全:https://www.it610.com/article/5297228.htm

 

 

 

 

***栈和递归的关系?

1.push操作的目标也是一个寄存数或者立即数,不能直接将内存的数据放入栈中。

2.栈原则是递归调用可行的原因所在。

3.栈的地址从顶至底是,由大到小,压栈时不断减小,返回时不断增大。

 

 

 

***C中的复合数据类型是怎样的?

1.数组

https://blog.csdn.net/u011146511/article/details/70155939

2.结构体

https://blog.csdn.net/yanggangclcsdn/article/details/49718131

3.联合体

https://www.cnblogs.com/zhangjxblog/p/8743203.html

 

***如何优化编译器?

1.一般有用的优化

预计算;

减弱代码强度;

共享公共子表达式;

删除不必要的过程调用;

2.优化阻止程序

过程调用;

内存别名;

3.开发指令级并行

4.处理条件语句

 

***存储器层次是怎样的?

1.首先我们看来以CPU为核心的硬件结构,它包括CPU块,I/O总线和主要内存,我们常说的读写操作就是CPU中的寄存器到主要内存直接的数据流动,从“主要内存”中读出到“寄存器”,从“寄存器”写入到“主要内存”。

 

2.再来看存储器层次,从高到底是“金字塔”式的结构,这样设计的好处是,高处的存储器也能快速访问低处的存储器,而其中的实现原理就是cache(缓存机制)。

 

***缓存机制的意义是?

*缓存的主要内容如下:

缓存的结构和操作流程。

*对缓存有着性能影响的几个方面如下:

缓存山。

*重新设计“循环”来改善空间局部性。

利用阻塞来改善时间局部性;

 

*缓存对项目整体性能有着十分重要的 意义:

1.我们要重点关注内循环,因为其中有大量计算和内存访问发生。

2.可以尝试通过读取数据对象来最大化空间位置。

3.注意一旦从内存中读取数据对象,就尽可能多地使用它来最大化时间局部性。

 

***链接是什么意思?

1.链接是一种允许N个对象文件构造成1个可执行文件的技术,链接可能发生在一个程序的生命周期的不同时间:编译时间、加载时间、运行时间。

2.链接其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件合成一个可执行的文件。

拓展学习:http://c.biancheng.net/view/1736.html

 

***异常控制流是什么意思?

1.在计算机中,使控制流发生突变的源头被称之为异常控制流。

举例:AB并发 AC并发 BC不并发 。
虚线法:把每个进程的头尾用虚线连接起来,如果2个进程之间有重合部分,那么这2个线程就是并发的。

***关于进程要注意的点是什么?

1.单个CPU情况下,进程的“并发”是在上下文之间切换来实现的。

2.多个CPU下,每个CPU下的进程都有独立的堆栈、内存和寄存器和代码。

***父子进程中fork函数的原理是什么?

1.子进程执行前,pid一般是0,在子进程结束后,pid变为非0(fork()调用一次,返回子父2个结果)。

2.通常fork函数执行后,父子进程执行的流程图,以及最终可能产生的结果(代码参考上一个点)。

3.多个fork函数父子进程情况。

4.多条件fork函数执行的流程图(返回0代表正在执行子进程)

5.fork函数存在wait函数的情况,以及最终可能产生的结果。

 

***什么是System Level I/O?
1.Unix I/O

Unix下的输入输出设备都可以以文件的方式进行访问。一般访问有write、read、open、close。open就会打开一个文件的描述符,通过描述符进行读和写。close就是关闭描述符表示的文件,如果关闭一个已经关闭的文件这是灾难性的。

读文件:

返回的值在buf里面,如果读错误就会返回-1。实际读回的数目是nbytes。

写文件:

 

2.RIO Package

read和write在做读数据的时候会有不足值,那么需要比较健壮的包进行处理不足值。所以RIO包被提出。

RIO包分为有缓存和无缓存。无缓存对网络读写二进制文件特别有效,写proxy lab的时候用的就是它。

无缓存


rio_readn读取n个字节,rio_ write 写n个字节。


rio_readn 的实现如上。
有缓存

rio_readlineb: 从socket连接读取文本行的时候合适用这个有缓存的方法。因为它遇到换行、maxlen、eof都会终止。

rio_readnb: 从socket连接读取文本行的时候合适用这个有缓存的方法。因为它遇到maxlen、eof都会终止。它和rio_readlineb可交替使用。

 


3.metadata(元数据)

描述数据的数据。

获取medadata的代码:

 

4.I/O重定向:

5.standard I/O

标准I/O是定义在系统I/O上的I/O。一般来说标准IO是缓冲型IO,比系统级IO节约cycles,系统级IO涉及系统unix的调用。

标准IO的一些函数:


5.选择I/O

大部分人的职业生涯只需要用到标准输入输出,毕竟这处理终端和磁盘等文件下,标准输入输出可以看做是系统输入输出的一个带缓冲版本,也比较多好用的函数。但是涉及网络编程这类全双工流的时候,RIO包由它的优点。以及涉及信号处理的时候,系统级输入输出是异步安全的。标准输入输出由它的限制:跟在输出函数之后的输入函数,需要在中间插入fflush、fseek、fsetpos、rewind中的某个。跟在输入函数之后的输出函数,也要使用上述的函数中的某个,或者是遇到EOF。

 

***关于虚拟内存的概念是是什么?

1.虚拟内存是一种计算机系统内存管理技术。它使得应用程序认为它拥有连续可用的内存(1个完整的地址空间),而实际上它通常被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

 (某系统使用的虚拟地址,MMU是Memory Management Unit的缩写,中文名是内存管理单元)

2.内存页表工作机制。由虚拟地址定位到页表,然后由页表映射到物理内存中,如果物理内存中有对应物理地址则返回,如果没有则去磁盘(虚拟内存)中找,找到后先保存在物理内存中,再返回给页表。

3.相同虚拟地址内存可以指向不同物理地址。

4.基于页表的虚拟地址转换。

 

5.页命中和页缺失(虚拟内存应用缓存时)。页缺失时,MMU会将该异常抛给"页缺失处理器",然后处理器会去磁盘找到没有缓存的地址然后返回。

6.TLB命中和TBL未命中。TLB是转译后备缓冲器,页表命中时可以直接返回PTE(页表条目)给MMU,如果TLB未命中,则会去缓存中找到PTE然后返回给TLB,然后在返回给MMU。

7.二级页表结构。多级页表的好处是可以由若干级页表来覆盖整个虚拟内存,而用单级页表去覆盖的话,效率就低很多。

 

***动态内存分配的原理是什么?

1.动态内存分配是指程序执行过程中,动态地分配或者回收存空间的分配内存的方法。

2.介绍2个常用的动态分配内存函数:malloc()和free()。

*malloc()

1)  该函数会向堆申请一片连续可用内存空间;

2)如果申请空间成功,则返回一个指针,它是指向这片内存的;如果申请失败,则返回NULL;

3)  返回值类型为void,该函数不清楚开辟的size个字节是存储什么类型的数据,故需要我们自己决定:方法是在malloc()函数

前加强转——(int)malloc(size(int)*n);

4)如果size是0,会发生错误;

*void free(void* ptr):它在堆里申请的内存不能自动释放,需要我们手动释放,不然会造成内存泄漏;

1)如果ptr没有指向分配好的内存空间,则会报未定义错误;

2)  如果ptr是空指针,则该函数不执行操作;

3)该函数不会改变ptr本身的值,因此它依然指向相同的内存位置;

4)该函数执行后,需要把ptr初始化为空——ptr = null;不然之后的程序就会访问到无效的内存;

 

 

***网络编程有什么重要的知识点?

1.网络编程实际上是服务端和客户端的数据交换,是基于请求/响应的方式。

2.IP和域名:IP地址目前主流仍然是IPV4,由4个0-255的数字组成的(点分十进制),每个网卡只有一个IP地址;但是由于IP地址不方便记忆,所以创造了域名(IP的可读性较好地名字),由DNS服务器专门负责两者之间的转换;

3.端口:为了能在一台设备运行多个程序,而创造了端口(port,类似分机号码),每个端口唯一对应一个程序;

4.协议:Socket、tcp和udp;

Socket:简言之就是ip地址和端口的结合协议,它在网络传输中用于唯一标识两个端点(ip+port)的连接,由4部分组成(客户端地址、客户端端口、服务端地址和服务端端口)。

TCP:面向连接协议,通过3次握手建立连接,类似打电话(打过去,有人应答才能发送数据包)

UCP:面向无连接协议,类似发邮件(有没有响应,我的数据包都可以发过去)

 

 

***关于并发编程重要的几点是什么?

1.PV操作和信号量是实现并发编程的核心(PV操作是一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思)

*信号量的作用:控制共享资源的使用权(满足互斥条件);标志某事件的发生;是2个及以上的进程行为同步。打个比方,信号量就是一把钥匙,进程要运行,必须拿到这把钥匙才行;

*PV操作

P操作:使信号量-1;

可以理解为:

if ( (s = s - 1) >= 0 ) 

//继续执行本进程; 
else 
//挂起本进程/本进程等待;

V操作:使信号量+1;

可以理解为:

if ( (s = s + 1) >0 ) 
//不唤醒s的队列中的等待进程; 
elseif (s = s + 1) <= 0 
//唤醒s的队列中的等待进程; 
继续执行本进程;

下面是个具体例子:https://blog.csdn.net/wuxy720/article/details/78936912?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

2.互斥锁:每个对象都可以对应一个"互斥锁"的标记,这个标记用于保证在任意时刻,只能有一个线程去访问该对象。

3.volatile关键字:

看图,JVM运行时的内存分配,其中1个内存区域是JVM虚拟机栈(主内存)和每个线程运行时的线程栈(线程工作内存);

其中,线程工作内存保存线程运行时的变量信息,当线程访问某1个对象时,首先通过对象的引用找到堆内存变量的值,然后把该值加载到线程内存中(相当于建立1个变量副本),之后线程就不在和堆内存的变量的值有关联,只用副本。副本变量值修改完后(线程还未退出时),自动把该副本写回到该对象在堆中的值(相当于去覆盖原值),所以堆中的值发生改变。

下面是个具体例子:https://www.cnblogs.com/xd502djj/p/9873067.html

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值