内存管理与IO访问

为了在用户自己编写的内核程序中完成分配,释放页等操作(直接对内存的操作基本是对页的操作,区只是为了管理不同的用途的页而存在的)。

NUMANon Uniform Memory Access Architecture)技术可以使众多服务器像单一系统那样运转,同时保留小系统便于编程和管理的优点。基于电子商务应用对内存访问提出的更高的要求,NUMA也向复杂的结构设计提出了挑战。NUMA通过提供分离的存储器给各个处理器,避免当多个处理器访问同一个存储器产生的性能损失来试图解决这个问题。对于涉及到分散的数据的应用(在服务器和类似于服务器的应用中很常见),NUMA可以通过一个共享的存储器提高性能至n,n大约是处理器(或者分离的存储器)的个数。

Alloc_page分配页

Page_address把页转成虚拟地址 返回值就是page结构体对应的虚拟地址。

__get_free_pages用于直接获取分配的连续物理衣的虚拟地址(首地址)

Free_pages

Hot page就是已经缓存到CPU CACHE中的页,cold page就是并未缓存到cpu cache中的页。

__GPF_WAIT(分配器可以睡眠)__GFP_IO(分配器可以启动磁盘I/O)__GFP_FS(分配器可以启动文件系统I/O)

上面的分配函数是分配以页为单位的内存空间,因为有些API是需要以页为单位分配内存空间的。但大多数时候需要以自己来分配内在空间,这就要使用KMALLOC VMALLOC;

Kmalloc函数与用户空间的MALLOC函数非常类似,只不过KAMLLOC函数多了一个FALAGS参数,KMALLOC函数可以获得以字节为单位的一块连续的内核内存。Kmalloc函数返回一个指向内在块的指针,其内存块至少要有SIZE大小,所分配的内存区在物理上是连续的,如果分配失败,返回NULL.分配失败的唯一原因是没有足够的内存,如果空闲内存充足,KMALLOC函数一定可以成功分配SIZE大小的内存空间。使用kmalloc函数分配的内存块可能要比实际请求的内存块大,这是由于系统分配内存的基本方法都是基于PAGE的,并不能以字节为单位分配内存空间,系统在分配内存空间时,会分配整个PAGE的空间,如果KMALLOC函数指定的SIZE不是PAGE所占字节的整数倍,那么实际分配的空间就会比SIZE

__get_free_pageskmalloc都会用一个gfp_mask标志。Gfp_mask标志分为如下3类:

行为修饰符ACTION MODIFIERS 区修饰符ZONE MODIFIERS 类型标志TYPE FLAGS;

行为修饰符表示内核应当如何分配内存,在某些特定的情况下,只能使用某些特定的方法分配内存,例如,中断处理程序就要求内核在分配内存的过程中不能休眠(国为中断处理程序不能被重新调度)。区修饰符表示从哪个分配内存。内核把物理内存分为多个区,每个区用于不同的目的。区修饰符指定了要从哪一区分配内存。类型标志组合了行为修饰符和区修符。

区修饰符表示内存区应从何处分配,通常,内存分配可以从任何区开始,不过,内核优先从ZONE_NORMAL区开始。这样可以确保其它区在需要时有足够的空闲页可以供使用。LINUX只有3个区修饰符。_GFP_DMA 只从ZONE_DMA分配

_GFP_DMA32只从ZONE_DMA32分配。

_GFP_HIGHMEM ZONE_HIEGHMEMZONE_NORMAL分配

一般在LINUX内核中以两个画线__开头的函数,宏尽量不要直接使用,因为这些函数和宏基本都是在系统内部使用的。而一般都会有相应的不以两个画线__开头的函数和宏供第三方开发人员使用。

分配不连续的内存空间VMALLOCVMALLOC函数的工作方式与KMALLOC函数类似,只不过前者分配的内存虚拟地址是连续的,而物理地址不一定连续。用户空间的内存分配方式与VMLLOC函数类似。也就是说,用户空间的MALLOC函数在分配内存时,虚拟是连续的,但并不保证物理地址也连续。KMALLOC函数可以确保物理地址和虚拟址都是连续的。那为什么VMALLOC函数中分配内存时物理地址可能会不连续呢(虚拟地址一定是连续的)?这是怎么做到的呢?实际上,VMALLOC函数中分配内存时首先分配一段连续的虚拟地址,然后再分配相应的物理内存块(可以是连续的,也可以是不连续的),最后再根据这些物理内存块的地址修正页表,把内存映射到逻辑地址空间的连续区域中。这样就会产生虚拟地址连续,物理地址可能不连续的效果 。大多数内存代码都选择使用KMALLOC函数来分配内存,而不是VMALLOC函数。这主要是出于性能的考虑。VMALLOC函数分配内存虽然灵活。但也为此付出代价。VMALLOC函数为了把物理上不连续的页转换为虚拟地址空间上连续的页。必须专门建立页表项。更糟糕的是,通过VMALLOC函数获得的页必须一个一个地进行映射(因为它们物理上是不连续的),这就会导致比直接内存映射大得多的TLB(TRANSLATION LOOKASIDE BUFFER,一种硬件缓冲区)抖动。因此这些原因,VMALLOC函数公在不得已时才使用。

Kmem_cache_create函数返回一个所创建高速缓存的针钱 kmem_cache_destory销毁

在创建高速缓存后,可以通过KMEM_CACHE_ALLOC函数从中获取对像,也就是对像的分配。

SLAB表示高速缓存。LINUX结体中用struct kmem_cache结构体来描述。SLAB全局缓存。

LINUX内核中,除了SLAB以外,还包含了对内存池的支持,内在池技术也是一种用于分配对像缓存技术。内在池相关的函数:

Mempool_create

Mempool_destroy

Mempool_alloc

Mempool_free

虚拟地址转换为物理地址的函数是VIRT_TO_PHYS

物理地址到虚拟地址的转换函数是PHYS_TO_VIRT

#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)

#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)

页面目录:PGD

中间目录:PMD

页面表:PT

PT中的表项则称为PTEPAGE TALBE ENTRY

对于CPU发出的线性地址,虚拟的LINUX内存管理单元如何转换成物理地址。如下图:

 

上图描述过程语言描述如下步骤:

A.用线性地址中最高的那一个位段作为下标在PGD中找到相应的表项,该表项指向相应的中间目录PMD

B.用线性地址中的第二个位段作为下标在此PMD中找到相应的表项,访表项指向相应的页面表

C.用线性地址中的第三个位段作为下标在页面表中找到相应的表项PTE.该表项中存放的就是指向物理页面的指针

D.线必地址中的最后位段为物理页面内的相对位移量,将此位移量与目标物理页面的超始地址相加便得到相应的物理地址。

 

32位地址意味着4G字节的虚存空间,LINUX内核将这4G字节的空间公成两部分。将最高的1G字(虚拟地址0XC0000000-0XFFFFFFFF,用于内核本身,称为系统空间,而将较低的3G字节(从虚拟地址0X00XBFFFFFFF,用作各个进程的用户空间。虽然系统空音占据了每个虚拟空间中最高的1G字节,在物理的内存中却总是从最低的地址0开始,所以,对于内核来说,其地址的映射是很简单的线性映身。0XC00000000就是两者之间的位移量。

 

页式映射的过程,每个进程都有其自身的页面目录PGD,指向这个目录的指针保挂在每个进程的MM_STRUCT数据结构中,每当调度一个进程进入运行的时候,内核都要为即将运行的进程设置好控制寄存器CR3,MMU的硬件则总是从CR3中取得指向当前页面目录的指针。在内核中,不管什么进程,一量进入内核就进了系统空间,都有相同的页映射。每个进程的PGD物理地址装入寄存器CR3中,在一个进程中的虚拟地址,通过高12位在CR3指定的页外目录中找到页面表,在通过中间10在前面找到页表中去找到相应的PTR面表项。然通过低10位合在PTR组成具体的物理地址。内核空间具须相同的CR3所以内核中的虚拟地址与物理地址是相必对应的,但在用户空间就不线性的。因为每个线程具体不同的用户空间,既不同的CR3。同一个线程在内核空间地虚拟址是线性的,但在用户空间就不是对应的了,不同的进程用户空间有不同的CR3

LINUX内核只要能为硬件准备好页面目录PGD,页面表PT以及全局段描述表GDT和局部段描述述表LDT,并正确的设置有关的寄存器,就完成了内在管理机制中地址映射部分的准备工作。页面目录PGD,中间目录PMD和页面表PT分别是由表项PGD_T,PMD_T以及PTE_T构成的数组,而这些表项又都是数据结构

内核中有个全局量MEM_MAP,是一个指针,指向一个PAGE数据结构的数组,每个PAGE数据结构代表着一个物理页面,整个数组就代表着系统中的全部物理页面。

32LINUX内核页的大小是4K,因此32位中占据高20位,低12位为页内指针,高20位为页序号

系统中的每个物理页面都有一个PAGE结构(或MEM_MAP_T[struct page *mem_map;].系统在初始化时根据物理内在的大小建立起一个PAGE结构数组MEM_MAP。作为物理页面的仓库。里面的每个PAGE数据结构都代表着系统中的一个物理页面。每个物理页面的PAGE结构在这个数组的下票就是该物理页面的序号。仓库里的物理页面划分成ZONE_DMAZONE_NORMAL两个管理区(有的还分一个ZONE_HIGHMEM

在传统的计臬机结构中,整个物理空间都是均匀一致的,CPU访问这个空间中的任何一个地址所需的时间都是相同的。所以称为均质存储构UNIFORM MEMORY ARCHITECTURE,简称UMA.但在多CPU结构的系统中,物理存储空间在这方面的一致怀却成了问题。例如下面一种结构:系统的中心是一条总线,例如PCI总线。有多个CPU模块连接在系统总线上,每个CPU模块都有本地的物理内存,但也可以通过总线访问其CPU模块上的内存。但可以通过系统总线访问其它CPU模块上的内存。系统总线上还连接着一个公用的存储模块,所有的CPU模块都可以通过系统总线来访问它。所有这些物理内存的地址互相连续而形成一个连续的物理地址空间。就这种CPU架构而言,访问其本地的存储器是速度最快的,而穿过系统总线访问公用存储模块或其它CPU模块上的存储器就比较慢,而且还面临因可的竟争而引起的不确定性,也就是说,在这样的系统中,其物理存储空间虽然地址连续,质地却不一致,所以称为非均质存储结构NON UNIFORM MEMORY ARCHITECTURE 简称NUMA。在NUMA结构的系统中,分配连续的若干物理页面时一般都要求分配在质地相同的区间,称为NODE节点。前面几个数据结构都是用于物理空间管理的,现在来看看虚拟空间的管理,也就是虚存页面的管理,虚存空间的管理不像物理空间的管理那样有一个总的物理页面仓库。而是以进程为基础的,每个进程都有各知的虚存(用户)空间,不过,如彰所述,每个进程的系统空间是统为所有进程所有共享的,以后我们对进程的虚存空间和用户空间这两个词常常不加区分。物理空间的管理是由几个结构来完成的从上到下:pglist_data_t->mem_map/zone_t->page[NUMA管理各个单独的存储区,MEM_MAP管理页  PAGE代表物理页 ZONTE_TMEM_MAP分成几个区域]

LINUX系统中对虚拟存储空间的管理是vm_area_struct数据结构。

#define PAGE_SHIFT 12

#define PAGE_SIZE 1<PAGE_SHIT   //4K

CONFIG_PAGE_OFFSET=0xC0000000

#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)

#define PLAT_PHYS_OFFSET UL(0x40000000)

#define PHYS_OFFSET PLAT_PHYS_OFFSET

#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)

#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)

struct page *mem_map;内核中有一个全局量,它是一个指针,指向一个PAGE数据结构的数组。每个PAGE数据结构代表着一个物理页面,整个数组就代表着系统中的全部物理页面。因此,页面表项的高20位对于软件和MMU硬件有关不同的意义,对于软件,这是一个物理页面的序号,将这个序号用作下标就可以从mem_map找到代表这个物理页面的page数据结构,对于硬件,则(在低位补上120)就是物理页面的超始地址。

Pglist_data->zone_struct->mem_map->page

Mm_structvm_area_struct说明了对页面的需求,PAGEZONE_STRUCT等结构则说明了对页面的供应。而页面目录,中间目录,以及页表则是二者中间的桥梁。

 

 

一般的所说的虚拟空间指的是用户空间,因为内核空间的虚拟地址和物理地址是线必的关系的,也就是说在内核态上面图中的PGD对于每个进程都是一样的。但在用户空间时都不相同。也就是在内核态时对于每个进程都是同一个mm_struct结构,而不同进程在用户态都不同的mm_sturct。这也就是虚拟空间一般指用户空间的原因。

CPU从一次页面错误导演处理返回到用户空间时,将会先重新执行因映射失败而中途夭折的那条指令,然后才继续往下执行,这是导演处理的特性。中断以自陷(TRAP指令)发生时,CPU都会将下一条指令,也就是接下去本来应该执行的指令的地址压入堆栈作为中断服务的返回地址。但是异常不同。当异常发生时,CPU将因无法完成(如除以0,映射失败,等)夭折的指令本身的地址(而不是下一条指令的地址)压入堆栈。这样,就可以在从异常处理返回时完成未竟的事业。这个特性是在CPU的内部电路中实现的。而不是由软件实现的。从这个意义上讲,所谓缺页中断是不对的,应该叫缺页异常才对。除CPU之外,对于像LINUX这样的现代操作系统来说,物理存储页面可以说是最基本,最重要的资源了,物理存储页面在系统中的使用和周转就好像资金在企业中使用和周转一样重要。根据媒体介质不同,一个物理页面可以在内存中,也可以在磁盘上。为了区分这两种情况,分别称之为物理内存页面和盘上物理页面。在某项外部上,例如在网络接口上,用来存储一个页面内容的那部分介质,也称为一个物理页面。当谈及物理内存页面的分配和释放的时候,指的仅是物理介质,而在谈及页面的换入和换出时则指的是其内容。每个进程的虚存空间是很大的(用户空间3G),不过,生个进程实陡上使用的空间则小得多,一般不会超过几个MB,特别地,传统的LINUX可执行程序通常都是比较小的,例如几十KB或一三百KB。可是,当系统中有几百个,上千个进程同时存在的时候,对存储空间的需求总量就很大了,在这样的情况下,要为系统配备中够的内在就很难,所以在计算技术的发屏史上很早就有了把内存的内容与一个专用的磁盘空间交换的技术。既把暂时不用的信息存放到磁盘上。为其它急用的信息腾出空间。到需要时再从磁盘上读进来的技术 。早斯的盘区交换技术是建立在段式存储管理的基础上的,当一个进程暂不运行的时候就可以把它(代码段和数据段等)交换出去(把其它进程交换交换进来,帮日交换。),到调度这个进程支行时再交换回来,显然,这样的盘区交换是很粗糙的,对系统性能的影响也比较大。所以后来发民起了建立在页式存储管理 藏在 的按需面交换技术 。

为了(物理)内存页面的管理方便,每个内存页面都对应一个PAGE数据结构,每个物理内存页面对应有一个PAGE数据结构(以及每个进程之有一个task_struct结构),就好像每个人之有户口或者档案一样。一个物理上存在的人,如果没有户口,从管理的角度来说便是不存在的。同样,一个物理上存在的内存页面,如果没有一个应的PAGE结构,就根本不会被系统看到,在系统的初始化阶段,内核根据检测到的物理内存的大小。为每一个页面都建立一个PAGE结构。并使一个全局量mem_map指向这个数组。同时,又按需要将这些页面拼合成物理地址连续的许多内存页面块。再根据块的大小建立起若干管理区ZONE,而在每个管理区中则设置一个空闲块队列。以便物理内存页面的分配使用。与此类似,交换设置(通常是磁盘,也可以普通文件)的每个物理页面也要在内存中有一个相应的数据结构(或者说户口),不过那要简单得多,实际上只是一个计数,表示该页面是否已被分配使用,以及有几个用户在共享这个页面,对盘上页面的管理是按文件或磁盘设备来进行的。内核中定义了一个swap_info_struct数据结构。用以描述和管理用于页面交换的文件或设备。LINUX内核允许使用多个页面交换设备或文件,所以在内核中建立了一个SWAP_INFO_STRUCT结构的阵列(数组).凡是映射到系统空间的页面都不会被换出,但还是可以按使用和周转的不同而大致上成几类。首先我,内核代码和内核中全局量所占的内存页面既不需要经过分配不会被释放。这部分空间是静。(相比之下,进程的代码段和全局量都在用户空,所占的内存页面都是动态的。使用前要经过分配,最后都会被释放。并且中途可能被换出而回收后另行分配)

Vmalloc分配的空间不会被KSWAPD换出,因为KSWAPD只扫描各个进程的用户空间,而根本看不到通过VMALLOC分配的页面表项,至于通过KMALLOC分配的数据结构,则KSWAPD只是从各个SLAB队列中寻找和收集空闲不用的SLAB,闰释放所占用的页面,但是不会将尚在使用中的SLAB所占据的页面换出。

kmem_cache_create kmem_cache_alloc专用缓冲区的分配由cache_cache来管理。通用缓冲池的分配cache_sizes,通用缓冲区分的分配函数是kmalloc

设备通常会提供一组寄存器来用于控制设备,读写设备和获取设备的状态。这些寄存器被称为控制寄存器,数据寄存器和状态寄存器。这些寄存器可能位于IO空间,也可能位于内存空间。当位于IO空间时,通常被称为IO端口,位于内存空间时,对应的内存空间被称为IO内存卡。

Ioremapvmalloc类似,也需要建立新的页表,但并不进行vmalloc函数中执行的内存分配行为。使用ioremap映射的虚拟地址也需要释放,这个工作由iounmap宏完成。

 

这个图为IO端口与IO内存与主存之音的区别图。外设为一个视频采集卡。

在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO PortMem(包括IO Mem)可以达到4G

访问这类IO Port时,我们也可以用IO Port专用寻址方式。至于在对IO Port寻址时,内核是具体如何完成的,这个在内核移植时就已经完成。在这种架构的处理器中,仍然保持对IO Port的支持,完全是i386架构遗留下来的问题,在此不多讨论。而访问IO Mem的方式和i386一致。

s3c24x0处理器使用的是I/O内存,也就是说:s3c24x0处理器使用统一编址方式,I/O寄存器和内存使用的是单一地址空间,并且读写I/O寄存器和读写内存的指令是相同的。所以推荐使用I/O内存的相关指令和函数。但这并不表示I/O端口的指令在s3c24x0中不可用。如果你注意过s3c24x0关于I/O方面的内核源码,你就会发现:其实I/O端口的指令只是一个外壳,内部还是使用和I/O内存一样的代码。

/* __force表示所定义的变量类型是可以做强制类型转换的 */

#define __force __attribute__((force))

/* __iomem是用来修饰一个变量的,这个变量必须是非解引用(no dereference)的,即这个变量地址必须是有效的,而且变量所在的地址空间必须是2,即设备地址映射空间。0表示normal space,即普通地址空间,对内核代码来说,当然就是内核空间地址了。1表示用户地址空间,2表示是设备地址映射空间 */

#define __iomem __attribute__((noderef, address_space(2)))

一、I/O端口

      端口(port)是接口电路中能被CPU直接访问的寄存器的地址。几乎每一种外设都是通过读写设备上的寄存器来进行的。CPU通过这些地址即端口向接口电路中的寄存器发送命令,读取状态和传送数据。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址

二、IO内存

       例如,在PC上可以插上一块图形卡,有2MB的存储空间,甚至可能还带有ROM,其中装有可执行代码

三、IO端口和IO内存的区分及联系

 

         这两者如何区分就涉及到硬件知识,X86体系中,具有两个地址空间:IO空间和内存空间,而RISC指令系统的CPU(如ARMPowerPC等)通常只实现一个物理地址空间,即内存空间。

内存空间:内存地址寻址范围,32位操作系统内存空间为232次幂,即4G

IO空间:X86特有的一个空间,与内存空间彼此独立的地址空间,32X8664KIO空间。

 

IO端口:当寄存器或内存位于IO空间时,称为IO端口。一般寄存器也俗称I/O端口,或者说I/O ports,这个I/O端口可以被映射在Memory Space,也可以被映射在I/O Space

 

IO内存:当寄存器或内存位于内存空间时,称为IO内存。

对于ARM系统内存空间和IO空间统一编址的,对于X86是独立编址的。

四、外设IO端口物理地址的编址方式

 

        CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/Omapped),另一种是内存映射方式(Memorymapped)。而具体采用哪一种则取决于CPU的体系结构。

 

1、统一编址

 

RISC指令系统的CPU(如,PowerPCm68kARM等)通常只实现一个物理地址空间(RAM)。在这种情况下,外设I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。

 

       统一编址也称为I/O内存”方式,外设寄存器位于“内存空间”(很多外设有自己的内存、缓冲区,外设的寄存器和内存统称“I/O空间”)。

 

2、独立编址

 

        而另外一些体系结构的CPU(典型地

IO端口IO内存的描述:

http://www.360doc.com/content/12/1116/14/5037780_248204648.shtml

 

Request_region  release_region 申请和释放IO端口

Request_mem_region release_mem_region申请和释放IO内存

在使用IO端口和IO内存之前申请它们虽然不是必须的,但我们在使用它们之彰应该检查既将使用的IO端口和IO内存是否可用,如果可用,则可以放心使用它他,并将这些IO端口和IO内存的相应标志设置为已经使用,这样其它驱动在这些IO端口和IO内存未释放之前就无法使用它了,申请正在使用的IO端口和IO内存会导致申请失败。

内核空间与用户空间共离数据。内核空间与用户空音共享数据有很多方式,其中共享数据的方式是将内核空间的指定区域的内存映射到用户空间的指定区域的内存,这样操作用户空间的相应内存空间就相当于操作内核空间与之对应的内存。也就是说使用户空间与内核空间对应的内存同步、这样用户空间就可以使用普通的内存操作读写内核空间的数据。而内核程序对映射区域的任何变化都会体现到用户空间的相应内存中。(这种方式叫VMA  virtual memory area

LINUX系统的用户空间虚拟空间管理由TASK_STURCT.MM_STRUCT.VM_AREA_STRUCT数组链表来管理的。内核空间的虚拟空间管理由是队列VMLIST.这是由一串VM_STRUCT数据结构组成的一个单链队列。

每个进程的task_struct结构中都有一个指针指向mm_struct结构,从中可以找到相应的页面目录,但是,内核空间不属于任何一个特定的进程,所以单独设置了一个内核专用的mm_struct,称为init_mm.

KSWAP掏出页面的情意中,我们已经看到KSWAPD定期地,循环地,依次地从TASK结构队列中找出占用内存页面多的进程,然后就对该进程调用swap_out_mm掏出一些页面,而内核的mm_struct结构init_mm是单独的,从任何一个进程的task结构中都达不了init_mm.所以,kswapd根本就看不到init_mm中的虚拟存区间,这些区间就自然不会被换出而长驻于内存。

每个进程的用户空间都是完全独立,互不相干的,用户进程各自有不同的页表,而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间地址有自己对应的页表,内核的虚拟空间独立于其它程序。

LINUX1GB的内核地址空间又被划分为物理内存映射区,虚拟内存分配区,高端页面映射区,专用页面映射区和系统保留映射区这几个区域。

一般情况下,物理内存映射区最大长度为896MB,系统的物理内存被顺序映射在内核空间的这个区域中,当系统物理内存大于896MB时,超过物理内存映射区的那部分内存称为高端内存(而未超过物理内存映射区的内存通常被称为常规内存),内核在存取高端内存时必须将它们映射到高端页面映射区。

 

 

LINUX内核空间申请内在涉及的函数主要包括kmalloc(),__get_free_pages()vmalloc()等。Kmalloc()__get_free_pages()(及其类似函数)申请的内存从头再来地物理内存映射区域,而且在物理上也是连续的。它们与真实的物理地址只有一个固定的偏移。因此存在比较简单的转换关系。而VMALLOC()在虚拟内存空间给出一块连续的内存区。实质上,这片连续的虚拟内存在物理内存中并不一定连续。而VALLOC申请的虚拟内存和物理内存之间也没有简单的换算关系。

设备通常会提供一组寄存器来用于控制设备,读写设备和获取设备状态。既控制寄存器,数据寄存器和状态寄存器。这寄存器可能位于IO空间,也可能位于内存空间。当位于IO空间时,通常被称为IO端口。位于内存空间时,对应的内存空间被称为IO内存。

 

IO端口的另外一种方式

 

一般情况下,用户空间是不可能也不应该直接访问设备的,但是,设备驱动程序中可实现MMAP()函数,这个函数可使得用户空间能直接访问设备的物理地址,实际上,MMAP实现了这样的一个映射过程。它将用户空间的一一段内存与设备的内存关联,当用户访问用户空间这段地址范围时,实际上会转化为对设备的访问。

这种能力对于显示适配器一类的设备非常有意义,如果用户空间可直接通过内存映射访问显存的话,屏幕帧的各点像素将不再需要一个用户空间到内核空间的复制的过程。

实际上,内存只能页为单位进行映射。

当用户调用MMAP()的时候,内核会进行如下处理:

A.在进程的虚拟空间查找一块VMA

B.B将这块VMA进行映射

C.C如果设备驱动程序或者文件系统的FILE_OPERATIONS定义了MMAP操作。则调用它。

D.D将这个VMA插入到进程的VMA链表中。

E.

 


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值