自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

huasheng722的专栏

http://blog.csdn.net/muge0913?viewmode=list

  • 博客(31)
  • 收藏
  • 关注

转载 23 Linux x86_64与i386区别之 —— 内存寻址

1 引子 毫无疑问,不管是32位,还是64位处理器,所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。 对任何一个普通进程来讲,它都会涉及到5种不同的数据段。稍有编程知识的朋友都该能想到这几

2012-08-01 15:35:33 2256

转载 22 堆的管理

一般人喜欢把堆和栈来做对比,网上资料也很多,这里我只分享一下我本人的理解。堆这个东西跟栈没有直接的关联,它只给程序员提供一个手工分配和释放的内存空间,仅此而已。对于每个Unix进程来说,都拥有一个特殊的线性区,这个线性区就是所谓的堆(heap),堆用于满足进程的动态内存请求。内存描述符的start_brk与brk字段分别限定了这个区的开始地址和结束地址。进程可以使用下面的C语言AP

2012-08-01 15:34:51 522

转载 21 创建和删除进程的地址空间

本博,我们重点关注fork()系统调用为子进程创建一个完整的新地址空间。相反,当进程结束时,内核撤消它的地址空间。我们重点来讨论Linux如何执行这两种操作。1 创建进程的地址空间回忆一下“进程的创建 —— do_fork()函数详解”博文:当创建一个新的进程时内核调用copy_mm()函数。这个函数通过建立新进程的所有页表和内存描述符来创建进程一的地址空间:static

2012-08-01 15:34:24 893

转载 20 处理非连续内存区访问

回忆一下“缺页异常处理程序”,当出现缺页异常,并且是进程处于内核态,即do_page_fault()中的那个if (unlikely(address >= TASK_SIZE))分支语句后,将通过vmalloc_fault(address)判断该发生缺页异常的地址address是否处于非连续内存区:(arch/i386/mm/Fault.c)static inline int vmalloc_

2012-08-01 15:33:44 2712

转载 19 写时复制

第一代Unix系统实现了一种傻瓜式的进程创建:当发出fork()系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。这种行为是非常耗时的,因为它需要:- 为子进程的页表分配页框- 为子进程的页分配页框- 初始化子进程的页表- 把父进程的页复制到子进程相应的页中 这种创建地址空间的方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容。

2012-08-01 15:29:57 1546

转载 18 请求调页

上一篇博文引出了“请求调页”技术,术语“请求调页”指的是一种动态内存分配技术,它把页框的分配推迟到不能再推迟为止,也就是说,一直推迟到进程要访问的页不在物理RAM中时为止,由此引起一个缺页异常。 请求调页技术背后的动机是:进程开始运行的时候并不访问其线性地址空间中的全部地址。 事实上,有一部分地址也许永远不被进程使用。此外,程序的局部性原理保证了在程序执行的每个阶段,真正引用的进程

2012-08-01 15:29:13 1028

转载 17 处理地址空间内的错误地址

如果addr地址属于进程的地址空间,则do_page_fault()转到good_area标记处的语句执行:/* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */good_area: si_code = SEGV_ACCERR; write = 0; swi

2012-08-01 15:28:15 1106

转载 16 处理地址空间以外的错误地址

前面博文提到了,如果address不属于进程的地址空间,那么do_page_fault()函数继续执行bad_area标记处的语句。如果错误发生在用户态,则发送一个SIGSEGV信号给current进程并结束函数:/* * Something tried to access memory that isn't in our memory map.. * Fix it, but ch

2012-08-01 15:27:41 1101

转载 15 缺页异常处理程序

我们在中断专题中提到,Linux的缺页(Page Fault)异常处理程序必须区分以下两种情况:由编程错误所引起的异常,及由引用属于进程地址空间但还尚未分配物理页框的页所引起的异常。 线性区描述符可以让缺页异常处理程序非常有效地完成它的工作。do_page_fault()函数是80x86上的缺页中断服务程序,它把引起缺页的线性地址和当前进程的线性区相比较,从而能够根据和下图所示的方案选择适

2012-08-01 15:27:05 1340

转载 14 释放线性地址区间

内核使用do_munmap()函数从当前进程的地址空间中删除一个线性地址区间。 1 do_munmap()函数 该参数为:进程内存描述符的地址mm,地址区间的起始地址start和它的长度len。要删除的区间并不总是对应一个线性区,它或许是一个线性区的一部分,或许跨越两个或多个线性区。 该函数经过两个主要的阶段。第一阶段(第1一6步),扫描进程所拥有的线性区链表,并把包含在进

2012-08-01 15:26:25 1395

转载 13 分配线性地址区间

前面讲了那么多线性区底层分配的细节,现在让我们讨论怎样分配一个新的线性地址区间。为了做到这点,do_mmap()函数为当前进程创建并初始化一个新的线性区。不过,分配成功之后,可以把这个新的线性区与进程已有的其他线性区进行合并。static inline unsigned long do_mmap(struct file *file, unsigned long addr, unsign

2012-08-01 15:25:43 847

转载 12 线性区的底层处理

在上一篇博文对控制内存处理所用的数据结构和状态信息有了基本理解以后,我们来看一组对线性区描述符进行操作的低层函数。这些函数应当被看作简化了do_map()和do_unmap()实现的辅助函数。这两个函数将在后面的相关博文中进行描述,它们分别扩大或者缩小进程的地址空间。这两个函数所处的层次比我们这里所考虑函数的层次要高一些,它们并不接受线性区描述符作为参数,而是使用一个线性地址区间的起始地址、长度和

2012-08-01 15:25:06 1788

转载 11 线性区的数据结构

上一篇博文我们会看到,内核使用一种新的资源成功实现了对进程动态内存的推迟分配。当用户态进程请求动态内存时,并没有获得请求的页框,而仅仅获得对一个新的线性地址区间的使用权,而这一线性地址区间就成为进程地址空间的一部分。这一区间就叫做“线性区”。本博,我们就来详细讨论这个线性区。 1 线性区数据结构 Linux通过类型为vm_area_struct的对象实现线性区,它的字段如下所示:

2012-08-01 15:24:30 2257

转载 10 内存描述符

在前面的系列博文中我们已经看到,内核中的函数以相当直截了当的方式获得动态内存:__get_free_pages()或alloc_pages()通过伙伴算法从分区页框分配器中获得页框,kmem_cache_alloc()或kmalloc()使用slab分配器为专用或通用对象分配内存,而vmalloc()或vmalloc_32()获得一块非连续的内存区。如果所请求的内存区得以满足,这些函数都返回一个页

2012-08-01 15:17:41 1011

转载 9非连续内存区

从前面的博文中我们已经知道,把一块存放slab结构的内存区映射到一组连续的物理页是最好的选择,这样会充分利用高速缓存并获得较低的平均访问时间。不过,上面的方式主要是针对那些使用非常频繁的内核数据结构——如task_struct、inode来设计的。如果对内存区的请求不是很频繁,那么,通过连续的线性地址,而不是物理地址来访问非连续的物理页框这样一种分配模式就会很有意义了。这种模式的主

2012-08-01 15:16:50 2280

转载 8slab分配器

前面详细讨论了伙伴系统算法,以及基于该算法的页框管理细节。这些内容都是采用页框作为基本内存区,这适合于对大块内存的请求。但是,内核如何处理对一些数据结构分配内存空间,大多数数据结构根本占用不到一个页框。我们如何处理对小内存区的请求呢,比如说几十或几百个字节?显然,如果为了存放很少的字节而给它分配一个整页框,这显然是一种浪费。取而代之的正确方法就是引入一种新的数据结构来描述在同一页框中如何分

2012-08-01 15:16:09 783

转载 7每CPU页框高速缓存

在“Linux页框级内存管理处理细节”一篇博文中,我们谈到内核调用alloc_pages等系列函数分配一个或一片连续的页框。这一系列函数本质上是使用伙伴算法从指定zone_t中取到一个或一片连续的空闲的页框。正如我们将在以后重点博文“slab分配器”所看到的,内核经常请求和释放单个页框。为了提升系统性能,如果请求单个或释放单个页框时,内核在使用伙伴算法之前多添了一个步骤,即每CPU页框高速

2012-08-01 15:15:27 807

转载 6高端内存映射

1 内核空间和用户空间 用户空间:在Linux中,每个用户进程都可以访问4GB的线性虚拟内存空间。其中从0到3GB的虚存地址是用户空间,通过每个进程自己的页目录、页表,用户进程可以直接访问。内核空间:从3GB到4GB的虚存地址为内核态空间,存放供内核访问的代码和数据,用户态进程不能访问,只有内核态进程才能寻址。所有进程从3GB到4GB的虚拟空间都是一样的,linux以此方式让内核态

2012-08-01 15:13:23 921

转载 5Linux页框级内存管理处理细节

弄清楚伙伴系统算法的原理以后,我们就可以开开心心地处理页框了。 我们可以通过6个稍有差别的函数和宏请求页框。一般情况下,他们都返回第一个所分配页的线性地址,或者分配失败则返回NULL。alloc_pages(gfp_mask, order):用这个函数请求2order 个连续的页框。他返回第一个所分配页框描述符的地址,或者如果失败,则返回NULL。alloc_page(gf

2012-08-01 15:12:32 596

转载 4伙伴系统算法

讲了这么多了,很多人肯定会一头雾水,前边提到的都是些数据结构或者是些概念性的东西,真正对动态页面的管理机制在哪里?换句话说,如何将每个节点,每个区中的页框分配给进程?要理清这个思路,我们首先必须学习一种算法 —— 伙伴系统算法。 内核要分配一组连续的页框,必须建立一种健壮、高效的分配策略。为此,必须解决著名的外部碎片(external fragmentation)问题。频繁地请求和释放不同

2012-08-01 15:10:08 593

转载 3Linux页框管理

在前面的博文里,我们讲解了基于80x86体系的Linux内核分段和分页机制,并详细地讨论了Linux的内存布局。有了这些基本概念以后,我们就来详细讨论内核如何动态地管理那些可用的内存空间。 对于80386这种32位的处理器结构,Linux采用4KB页框大小作为标准的内存分配单元。内核必须记录每个页框的当前状态,例如,区分哪些页框包含的是属于进程的页,而哪些页框包含的是内核代码或内核数据。内

2012-07-26 00:13:30 542

转载 2Linux内存布局

在上一篇博文里,我们已经看到Linux如何有效地利用80x86的分段和分页硬件单元把逻辑地址转换为线性地址,在由线性地址转换到物理地址。那么我们的应用程序如何使用这些逻辑地址,整个内存的地址布局又是怎样的?打一个比方,内存就像一座城市,而居住在这个城市里的市民就像是各个进程,一个市民吃喝拉撒睡,当然就得用于“房子”、“车子”、“票子”等各种各样的资源。有些资源是固定的,如“房子”,我们称之为静态数

2012-07-26 00:12:15 624

转载 1基于80x86的Linux的分段和分页机制

0386的两种工作模式:80386的工作模式包括实地址模式和虚地址模式(保护模式)。Linux主要工作在保护模式下。在保护模式下,80386虚地址空间可达16K个段,每段大小可变,最大达4GB。逻辑地址到线性地址的转换由80386分段机制管理。段寄存器CS、DS、ES、SS、FS或GS各标识一个段。这些段寄存器作为段选择器,用来选择该段的描述符。分段逻辑地址到线性地址转换图:

2012-07-26 00:11:30 676

转载 7下半部分

我们在前面的“中断处理”博文中提到,在由内核执行的几个中断任务之间有些不是紧急的:在必要情况下它们可以延迟一段时间。回忆一下,一个中断处理程序是急迫的,调用do_IRQ将几个中断服务例程串行执行,并且通常在一个中断的处理程序结束前,不应该再次出现这个中断,我们叫它“上半部分”。相反,可延迟中断可以在开中断的情况下执行。把可延迟中断从中断处理程序中抽出来有助于使内核保持较短的响应时间。 Li

2012-07-26 00:01:33 289

转载 6中断处理(续)

上一篇博文我们把中断原理和中断处理所需要的数据结构清理了一遍,现在,我们就该看看中断处理的具体动作是怎样的了。 当CPU接收一个中断时,就开始执行相应的中断处理程序代码,前面介绍过了,该代码的地址存放在IDT的相应门中。于是,与其他上下文切换一样,Linux需要保留当前寄存器的内容以便保存和恢复当前指令。 保存寄存器是中断处理程序做的第一件事情。每个IRQ的中断处理程序地址存放于i

2012-07-26 00:01:00 492

转载 5中断处理

上一篇博文里,我们谈到了异常处理,现在我们开始研究一下中断处理的情况。中断处理比异常处理复杂得多,这是因为: 第一,中断的发生对于正在运行的进程无关,被调用的中断处理函数叫做中断服务程序,它运行在内核态并处于系统上下文中(使用内核页表、其为内核代码和内核数据结构),所以中断处理程序不允许被阻塞; 第二,由于硬件资源的限制,比如APIC的IRQ引脚数量有限,一根IRQ信号线需要被几个

2012-07-26 00:00:23 415

转载 4异常处理

80x86处理器发布了大约20种不同的异常。内核必须为每种异常提供一个专门的异常处理程序。对于某些异常,CPU控制单元在开始执行异常处理程序前会产生一个硬件的错误码(hardware error code),并且压入内核态堆栈。下面列表给出了在80x86处理器中可以找到的异常的向量、名字、类型及其简单描述。更多信息可以在Intel的技术文挡中找到。0 - "Divide error

2012-07-25 23:59:47 416

转载 3中断描述符表

好了,现在,我们知道了80x86微处理器在硬件级对中断和异常做了些什么,接下来,我们继续关注的是如何初始化中断描述符表。内核启用中断以前,必须把IDT表的初始地址装到idtr寄存器,并初始化表中的每一项。这项工作是在初始化系统时完成的。int指令允许用户态进程发出一个中断信号,其值可以是0-255的任意一个向量。因此,为了防止用户通过int指令模拟非法的中断和异常,IDT的初始化必

2012-07-25 23:58:57 688

转载 2中断的硬件环境

1 IRQ讲到中断的硬件环境,我们先从著名的IRQ信号谈起。每个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线。所有现有的IRQ线都与一个名为可编程中断控制器(PIC)的硬件电路的输入引脚相连,可编程中断控制器执行下列动作:1.    监视IRQ线,检查产生的信号。如果有条或两条以上的IRQ线上产生信号,就选择引脚编号较小的IRQ线。2.    如果一个引发信号出现

2012-07-25 23:57:57 364

转载 1中断的分类

这是我们疯狂内核系列的中断处理专题开山之作,中断,广义的来说通常被定义为一个事件,该事件触发改变处理器执行指令的顺序。狭义地来说,针对80x86体系,中断被分为中断和异常,又叫同步中断和异步中断。注意广义的中断和狭义的中断千万不要混淆,以后我的博文中所有所谓的“中断”二字,就是指狭义的中断,即Linux处理80x86异步中断的细节。我们首先必须好好理清一下80x86体系中,中断和异常的区别:

2012-07-25 23:57:10 578

转载 8定时器中断

走到这里,大家肯定对Linux的中断处理有概念了,下面我们通过一个具体的实例,来了解Linux内核处理中断的全过程,那就是定时器中断。在详细分析这个过程之前,我们把Linux时间管理的概念先缕一缕。 在当前的80x86体系结构上,内核显式地与几种时钟和定时器电路打交道,其主要分为了时钟和定时器两大类:- 时钟电路同时用于跟踪当前时间和产生精确的时间度量。- 定时器电路由内核编程,所

2012-07-17 18:15:25 920

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除