主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情! |
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作 |
本教程运行环境:linux7.3系统,Dell G3电脑。
Linux驱动程序运行在“内核”空间。
一般应用程序和驱动程序是混合的,有一定水平能力的单片机程序员可以实现应用程序和驱动程序的分层。在Linux系统中,应用程序和驱动程序被强制分层。
在MCU程序中,应用程序可以直接操作底层寄存器。但是,这种行为在Linux系统中是被禁止的。比如Linux应用的编写人员故意在应用中调用驱动关于电源管理的问题,关闭系统,得不偿失。
特定的Linux应用程序调用驱动程序,如图所示:
应用程序运行在用户空间,驱动程序运行在内核空间。用户空间的应用如果要运行内核,必须经过一个“系统调用”的方法,从用户空间进入内核空间,运行底层。
Linux中的内核空间也是一个程序,应该有自己的虚拟内存空间,但是作为一个服务于用户程序的程序,内核空间有自己的特点。
内核空间和用户空间的关系
在32位系统中,一个程序的最大虚拟空间可以是4GB,所以最直接的办法就是把内核当成一个程序,让它和其他程序一样拥有4GB的空间。但这种做法会让系统不断切换用户程序的页表和内核页表,影响计算机的效率。解决这个问题最好的办法就是把4GB的空间分成两部分:一部分是用户空间,一部分是内核空间,这样内核空间可以保持不变,在程序切换的时候,只改变程序的页表。这种方法的唯一缺点是内核空间和用户空间都较小。
比如在i386这样的32位硬件平台上,Linux在文件PAGE中定义了一个常量PAGE_OFFSET,h:
# ifdefconfig _ MMU # Define _ _ page _ offset(0xc 0000000)//0xc 000000为3GB # else # Define _ _ PAGE _ OFFSET(0x 0000000)# endif # Define PAGE _ OFFSET((无符号long)_ PAGE _ OFFSET)Linux以PAGE _ OFFSET为界将4GB的虚拟内存空间分为两部分:地址0~3G-1的低位地址空间为用户空间,大小为3GB;;3GB到4GB-1的高位地址空间是内核空间,大小为1GB。
当系统中运行多个程序时,多个用户空间和内核空间的关系可以如下图所示:
如图所示,节目1、2...共享内核空间。当然这里的共享指的是时间共享,因为任何时刻,对于单核处理器系统来说,只能有一个程序在运行。
内核空间的一般布局
在Linux的发展过程中,随着硬件的更新和技术水平的提高,其内核空间布局的发展也是一种不断打补丁的方式。这样做的后果就是内核空间被划分成不同的区域,不同的区域有不同的映射方式。一般人们认为Linux内核空间有三个区域,分别是DMA区(ZONE_DMA)、普通区(ZONE_NORMAL)和高内存区(zone _)。
当实际物理内存很小时,直接映射内核空间。早期计算机实际配置的物理内存通常只有几MB。所以为了提高内核通过虚拟地址访问物理地址内存的速度,内核空间的虚拟地址和物理内存地址从低位地址到高位地址一一映射,如下图所示:
可以看到,这种固定的映射方式使得虚拟地址和物理地址的关系非常简单,即内核虚拟地址和实际物理地址之差只有一个固定的偏移量PAGE_OFFSET,所以当内核使用虚拟地址访问物理页帧时,可以通过虚拟地址减去PAGE_OFFSET得到实际物理地址,这比使用页表要快得多!
因为这种方式几乎是直接使用物理地址,所以这种映射固定的内核空间也被称为“物理内存空间”,简称物理内存。此外,由于固定映射方法是线性映射,因此该区域也称为线性映射区域。
当然,这种情况下(电脑实际物理内存较小),内核固定映射空间只占整个1GB内核空间的一部分。比如配置一个实际物理内存为32MB的x86计算机系统,内核的固定映射区域就是page _ offset ~(page _ offset+0x 0200000)的32MB空间。那么内核空间中剩余的内核虚拟空间呢?
当然,根据常用虚拟空间的管理,页表的非线性映射使用的是物理内存。具体来说,在整个1GB内核空间中去掉固定映射区,然后在其余部分去掉内核空间开头的一个8MB隔离区,剩下的就是与用户空间映射方式相同的普通虚拟内存映射区。在这个区域中,虚拟地址和物理地址没有固定的映射关系,动态内存是通过调用内核函数vmalloc()获得的,所以这个区域称为vmalloc分配区,如下图所示:
对于实际物理内存为32MB的x86计算机系统,vmalloc分配区的起始位置为page _ offset+0x00000小编0x0080000。
这里解释一下:这里所说的内核空间和物理页框的固定映射,本质上是内核页面对物理页框的一种“预留”,但并不意味着这些页面“占用”了这些物理页框。也就是说,只有当虚拟页面真正需要访问物理页面框架时,虚拟页面才被绑定到物理页面框架。通常,当一个物理页框没有被其对应的虚拟页使用时,该页框可以被后面介绍的用户空间和内核kmalloc分配区使用。
总之,在实际物理内存很小的系统中,实际内存的大小就是内核空间的物理内存区域和vmalloc分配区域的边界。
ZONE_DMA区和ZONE_NORMAL区对于整个1GB的内核空间,人们也把这个空间头部的16MB称为DMA区,也就是ZONE_DMA区,因为以前的硬件把DMA空间固定在物理内存的下部16MB空间;其余区域称为普通区域,即ZONE_NORMAL。
内核空间的高端内存
随着计算机技术的发展,计算机的实际物理内存越来越大,使得内核的固定映射区域(线性区域)越来越大。显然,如果不加以限制,当实际物理内存达到1GB时,vmalloc分配区(非线性区)将不复存在。所以之前开发的名为vmalloc()的内核代码已经不可用了。显然,为了与早期的内核代码兼容,这是不允许的。
下图显示了这个内核空间所面临的情况:
显然,造成上述问题的原因是实际物理内存可以超过1GB,所以内核固定映射区的边界没有限制,允许随着实际物理内存的增加而增加。
解决上述问题的方法是限制内核空间固定映射面积的上限,使其不能随着物理内存的增加而任意增加。Linux规定内核映射区上边界的最大值不能大于小于1G的常量high _ menu。当实际物理内存较大时,以3G+high_menory为界确定物理内存区域。
比如x86系统,high_memory的值是896M,那么剩下的1GB内核空间的128MB就是一个非线性映射区。这就保证了在任何情况下,内核都有足够的非线性映射区域来兼容早期的代码,并能以普通虚拟内存的方式访问1GB以上的真实物理内存。
也就是说,高端内存最基本的思想就是借用一段地址空间,建立临时地址映射,用完后释放,这样这段地址空间就可以被回收,访问所有物理内存。当计算机具有大的物理内存时,内核空间的示意图如下:
传统上,Linux将这部分内核空间称为3G+high_memory~4G-1高内存区。
总结一下:在x86结构的内核空间中,三类区域(从3G开始计算)如下:
ZONE_DMA:从内核空间开始16MBZONE_NORMAL:从固定映射开始16MB~896MB。ZONE_HIGHMEM内存:896MB ~ end (1G)来自内核空间。根据应用目标的不同,高端内存可以分为vmalloc区、持久映射区和临时映射区。高端内存在内核空间的布局如下图所示:
Vmalloc映射区vmalloc映射区是高端内存的主要部分。这个区域的头部和内核线性映射空间之间有一个8MB的隔离区域,尾部和后续的持久映射区域之间有一个4KB的隔离区域。
vmalloc映射区的映射方式和用户空间完全一样,内核可以通过调用函数vmalloc()来获取这个区域的内存。这个函数的作用相当于用户空间的malloc(),提供的内存空间在虚拟地址上是连续的(注意不保证物理地址的连续性)。
如果持久内核映射区通过alloc_page()获取高端内存对应的页面,如何为其寻找线性空间?
内核为此留出了一个线性空间,从PKMAP_BASE开始,用来映射高端内存,也就是持久内核映射区。
在持久内核映射区,可以通过调用函数kmap()建立物理页框和内核虚拟页之间的长期映射。这个空间一般是4MB,最多可以映射1024页框,数量比较稀少。所以为了加强页框的周转,要及时调用函数kunmap()来释放未使用的物理页框。
临时测绘区临时测绘区也叫固定测绘区和保留区。这个区域主要用于多处理器系统,因为在这个区域获得的内存空间没有保护,所以获得的内存必须及时使用;否则一旦有新的请求,这个页框上的内容就会被覆盖,所以这个区域叫做临时映射区。
一篇非常好的关于高内存区的文章:linux用户空间和内核空间——对高端内存的详细讲解。
内核内存分配修饰符gfp
为了解释内核内存请求函数中的请求,Linux定义了各种内存分配修饰符gfp。它们是行为修改器、区域修改器和类型修改器。
内存分配函数中的行为修饰符解释了内核应该如何分配内存。主要的行为修改器如下:
Linux的主内核内存分配行为修饰符表示__GFP_WAIT分配器可以休眠__GFP_HIGH分配器可以访问紧急缓冲池__GFP_IO分配器可以启动磁盘IO__GFP_FS分配器可以启动文件系统IO__GFP_COLD分配器应该使用缓存中即将过时的页帧__GFP_NOWARN分配器不发送。警告__GFP_REPEAT分配未能重新分配__GFP_NOFAILT分配未能重新分配,直到成功__GFP_NORETRY分配未能重新分配区域修饰符area修饰符指示内核空间的哪个区域需要分配内存。默认情况下,内存分配器从内核空间的ZONE_NORMAL开始,逐渐为内存请求者获取内存区域。如果用户故意需要从ZONE_DMA或ZONE_HOGNMEM获取内存,那么内存请求者需要在内存请求函数中使用以下两个区域修饰符:
Linux的主内核内存分配区域修饰符描述__GFP_DMA从ZONE_DMA区域__GFP_HIGHMEM从ZONE_HIGHMEM分配内存类型修饰符区域类型修饰符本质上是上述修饰符的联合应用。也就是说,上面的一些行为修改器和区域修改器由“|”连接,并分别命名。这里就不介绍了。
通用内核内存分配和地址映射函数
vmalloc()函数vmalloc()在vmalloc分配区分配内存,可以获得虚拟地址连续的大内存,但不保证物理页帧连续。与物理空间的内存分配函数malloc()不同,vmalloc()分配的物理页面不会被换出。函数vmalloc()的原型如下:
void *vmalloc(无符号长整型){ return __vmalloc(size,GFP_KERNEL | __GFP_HIGHMEM,PAGE _ KERNEL);} void * _ _ VM alloc(unsigned long size,gfp_t gfp_mask,pgprot _ t prot){ return kmalloc(size,(GFP _ mask | _ _ GFP _ COMP)& amp;~ _ _ GFP _ high mem);}其中参数size是请求内存的大小,返回值是获得内存的虚拟地址指针。
与vmalloc()匹配的release函数如下:
void vfree(const void * addr){ kfree(addr);}其中参数addr是要释放的内存指针。
函数kmalloc()kmalloc()是另一个常用的内核分配函数,可以分配一个连续的不清零的物理内存页,返回值是直接映射地址。kmalloc()分配的内存最多不能超过32页。它的优点是分配速度快,缺点是不能分配大于128KB的内存页(出于跨平台的考虑)。
在linux/slab.h文件中,该函数的原型声明如下:
static _ _ always _ inline void * kmalloc(size _ t size,GFP _ t flags){ struct kmem _ cache * cachep;void * retif(_ _ builtin _ constant _ p(size)){ int I = 0;如果(!size)返回ZERO _ SIZE _ PTR# define CACHE(x)\ if(size & lt;= x)\找到goto\ else \ i++;# include & ltLinux/kmalloc _ sizes . h & gt;# undef CACHEreturn NULL找到:# ifdef CONFIG _ ZONE _ DMAif(flags & amp;GFP_DMA)cachep = malloc_sizes
请参考文章:Kmalloc和Vmalloc的区别。
alloc_pages()函数不同于上面在虚拟空间中分配内存的函数。alloc_pages()是在物理内存空间中分配物理页框的函数,其原型如下:
静态内联结构page * alloc_pages(gfp_t gfp_mask,unsigned int order){if(不太可能(order & gt= MAX_ORDER))返回NULLreturn alloc _ pages _ current(GFP _ mask,order);}其中参数order指示分配的页面帧的数量,这是2^order.order的最大值由include/Linux/Mmzone.h文件中的宏MAX_ORDER决定。参数gfp_mask用于说明内存页框的分配方式和使用场合。
函数的返回值是页框块第一页框的页结构地址。
调用以下函数获取页面框架的虚拟地址:
void * page _ address(struct page * page){无符号长标志;void * ret结构page _ address _ slot * pas如果(!PageHighMem(page))返回low mem _ page _ address(page);pas = page_slot(页面);ret = NULLspin _ lock _ IRQ save(& amp;pas->;锁、标志);如果(!list _ empty(amp;pas->;LH)){ struct page _ address _ map * PAM;每个条目的列表(PAM & amp;pas->;lh,list){ if(PAM-& gt;page = = page){ ret = PAM-& gt;虚拟的;转到完成;} } } done:spin _ unlock _ irqrestore(& amp;pas->;锁、标志);返回ret}使用函数alloc_pages()获得的内存应使用以下函数释放:
void _ _ free _ pages(struct page * page,unsigned int order){ if(put _ page _ test zero(page)){ if(order = = 0)free _ hot _ page(page);else__free_pages_ok(page,order);}}函数kmap()kmap()是一个映射函数,可以将一个物理页框映射到内核空间的持久映射区。这种映射类似于内核ZONE_NORMAL的固定映射,只是虚拟地址和物理地址的偏移量不一定是PAGE_OFFSET。因为内核持久映射区容量有限(总共只有4MB),所以当内存用完时应该立即释放。
kmap()函数的原型如下:
void * kmap(struct page * page){ may _ sleep();如果(!PageHighMem(page))返回page _ address(page);return kmap _ high(page);}摘要
由于CPU的地址总线只有32位,32的地址总线在逻辑和物理上都只能描述4G地址空间(232=4Gbit)。在物理上,它理论上最多有4G内存(除了IO地址空间,实际内存容量不到4G),逻辑空间只能描述4G线性地址空间。
为了合理利用逻辑4G空间,Linux采用3: 1策略,即内核占用1G线性地址空间,用户占用3G线性地址空间。所以用户进程的地址范围是从0到3G,内核的地址范围是从3G到4G,也就是说内核空间只有1G的逻辑线性地址空间。
如果Linux的物理内存小于1G,内核通常会将物理内存与其地址空间进行线性映射,即一对一映射,这样可以提高访问速度。但是当Linux的物理内存超过1G时,线性访问机制就不够用了,因为只能映射1G内存,剩余的物理内存无法被内核管理。所以为了解决这个问题,Linux把内核地址分为线性区和非线性区两部分。最大线性面积896M,其余128M为非线性面积。因此,线性区域映射的物理内存成为低端内存,剩余的物理内存成为高端内存。与线性区域不同,非线性区域不是预先内存映射的,而是在使用时动态映射的。
低端内存分为两部分:内核空间开头的ZONE_DMA: 16MB,内核空间开头的ZONE_NORMAL: 16MB~896MB(固定映射)。剩下的就是高端内存:ZONE_HIGHMEM:内核空间896MB ~ end (1G)。
根据应用目标的不同,高端内存分为三个部分:vmalloc区、持久映射区和临时映射区。vmalloc区域由vmalloc()函数分配;持久映射区使用allc_pages()获取对应页面,然后使用kmap()函数直接映射;临时测绘区一般用于特殊需要。
用户空间和内核空间内核空间(3G~4G)高端内存(3G+High _ mem ~ 4G)ZONE _ High mem
非线性映射区域
临时映射区永久映射区vmalloc区低端内存(3G~3G+high_memory-1)
线性映射区(固定映射区)
ZONE_NORMALZONE_DMA用户空间(0~3G-1)页面目录->:中间页面目录->:页面相关推荐:《linux视频教程》以上是Linux驱动在哪里运行的详细内容。请多关注主机参考其他相关文章!
这几篇文章你可能也喜欢:
- 在Linux上操作vi编辑器(Linux上的vi编辑器命令)
- Mondoze:住宅IP/原生IP/IDC IP,VPS低至$8.33/马来西亚服务器/AS152742/11.11促销
- torchbyte 罗马尼亚 VPS 起价为 20 美元/年,AMD Ryzen9+ NVMe 硬盘,免费 DDoS 防护
- zlidc(智联IDC):韩国原生IP云服务器,35.9美元/季度,4核/4G内存/50G SSD/300M优质网络@2.5T月流量
- 椰草云双11活动:香港云服务器81元/年,香港实体服务器199元/月(香港云服务商)
本文由主机参考刊发,转载请注明:linux驱动在什么空间运行(linux驱动在什么空间运行) https://zhujicankao.com/88565.html
评论前必须登录!
注册