计时不是很准确。它的计时基于一个简单的间隔计数机制,编译过的程序为每个函数维护一个计数器,记录花费在执行该函数上的时间。对于运行时间较长的程序,相对准确。
调用信息相当可靠。
默认情况下,不显示库函数的调用。相反地,库函数的时间会被计算到调用它们的函数的时间中。
静态链接和动态链接一个很重要的区别是:动态链接时没有任何动态链接库的代码和数据节真正的被拷贝到可执行文件中,反之,链接器只需拷贝一些重定位和符号表信息,即可使得运行时可以解析对动态链接库中代码和数据的引用。
存储器映射
指的是将磁盘上的空间映射为虚拟存储器区域。Unix进程可以使用mmap函数来创建新的虚拟存储器区域,并将对象映射到这些区域中,这属于低级的分配方式。
一般C程序会使用malloc和free来动态分配存储器区域,这是利用堆的方式。
造成堆利用率很低的主要原因是碎片,当虽然有未使用的存储器但不能用来满足分配请求时,就会发生这种现象。
有两种形式的碎片:内部碎片和外部碎片。两者的区别如下:
内部碎片是在一个已分配的块比有效载荷大时发生的。例如,有些分配器为了满足对其约束添加额外的1字的存储空间,这个1字的空间就是内部碎片。它就是已分配块大小和它们的有效载荷大小之差的和。
外部碎片是当空闲存储器合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以来处理这个请求时发生的。
现代OS提供了三种方法实现并发编程:
进程。用这种方法,每个逻辑控制流都是一个进程,由内核来调度和维护。因为进程有独立的虚拟地址空间,想要和其他流通信,控制流必须使用进程间通信(IPC)。
I/O多路复用。这种形式的并发,应用程序在一个进程的上下文中显示地调度它们自己的逻辑流。逻辑流被模拟为“状态机”,数据到达文件描述符后,主程序显示地从一个状态转换到另一个状态。因为程序是一个单独的进程,所以所有的流都共享一个地址空间。
线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。线程可以看做是进程和I/O多路复用的合体,像进程一样由内核调度,像I/O多路复用一样共享一个虚拟地址空间。
(1)基于进程的并发服务器
构造并发最简单的就是使用进程,像fork函数。例如,一个并发服务器,在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。为了了解这是如何工作的,假设我们有两个客户端和一个服务器,服务器正在监听一个监听描述符(比如描述符3)上的连接请求。下面显示了服务器是如何接受这两个客户端的请求的。
关于进程的优劣,对于在父、子进程间共享状态信息,进程有一个非常清晰的模型:共享文件表,但是不共享用户地址空间。进程有独立的地址控件爱你既是优点又是缺点。由于独立的地址空间,所以进程不会覆盖另一个进程的虚拟存储器。但是另一方面进程间通信就比较麻烦,至少开销很高。
(2)基于I/O多路复用的并发编程
比如一个服务器,它有两个I/O事件:1)网络客户端发起连接请求,2)用户在键盘上键入命令行。我们先等待那个事件呢?没有那个选择是理想的。如果accept中等待连接,那么无法响应输入命令。如果在read中等待一个输入命令,我们就不能响应任何连接请求(这个前提是一个进程)。
针对这种困境的一个解决办法就是I/O多路复用技术。基本思想是:使用select函数,要求内核挂起进程,只有在一个或者多个I/O事件发生后,才将控制返给应用程序。
I/O多路复用的优劣:由于I/O多路复用是在单一进程的上下文中的,因此每个逻辑流程都能访问该进程的全部地址空间,所以开销比多进程低得多;缺点是编程复杂度高。