CPU访问各种外设有2种方式:一种是类似于访问内存的方式,即把外设的寄存器当作一个内存地址来读写,从而以访问内存相同的方式来操作外设寄存器,这时,IO与内存统一编址,IO的地址与内存的地址在同一个地址空间下,这种编址方式叫做IO与内存统一编址(ARM);另一种编址方式是IO地址与内存地址分开独立编址,这种编址方式叫做IO与内存独立编址(x86),此时,CPU访问内存和访问IO就需要不同的CPU指令去访问。
两种编址方式的比较
IO与内存统一编址方式,优势是IO当作内存来访问,编程简单;缺点是IO也需要占用一定的CPU地址空间,而CPU的地址空间是有限资源。IO与内存独立编织方式,优势是不占用CPU地址空间,缺点是CPU设计变复杂了。
![内存和IO访问 - shangchunjing_06 - 我的博客 内存和IO访问 - shangchunjing_06 - 我的博客](https://i-blog.csdnimg.cn/blog_migrate/ce44ddf078f7ea14d3a0d623c65a3115.png)
问:
linux的虚拟内存是4G,而每个进程都有自己独立的4G内存空间,怎么理解?
每个进程所拥有的4G独立的虚拟内存空间是什么意思?linux系统的虚拟4G空间中,高位的1G是用于系统内核运行的,那么每个进程都有4G的话岂不都要运行内核了,这样是不是很浪费很低效?
答:
4G 指的是最大的寻址空间为4G
一个进程用到的虚拟地址是由内存区域表来管理的,实际用不了4G。而用到的内存区域,会通过页表映射到物理内存。所以每个进程都可以使用同样的虚拟内存地址而不冲突,因为它们的物理地址实际上是不同的。
内核用的是3G以上的1G虚拟内存地址,其中896M是直接映射到物理地址的,128M按需映射896M以上的所谓高位内存。各进程用的是同一个内核,什么叫“都运行内核”?
见笑了,小生刚入门,只是用一个极端的想法来探究为什么这样设计,
"都运行内核"是每个进程都有独立的4G虚拟内存空间,其中有1G是跑系统的。
你说的”每个进程都可以使用同样的虚拟内存地址而不冲突“
是多个进程共用这4G的虚拟内存么,这样岂不和那句“每个进程所拥有的4G独立的虚拟内存空间” 起冲突了
还有896M是直接映射到物理内存的,是不是就一直跑在物理内存上了,这样的话是不是物理内存至少要有896M了啊
首先你得分清“可以寻址”和“实际使用”的区别。其实我们讲的每个进程都有4G虚拟地址空间,讲的都是“可以寻址”4G,意思是虚拟地址的0-3G对于一个进程的用户态和内核态来说是可以访问的,而3-4G是只有进程的内核态可以访问的。并不是说这个进程会用满这些空间。
其次,所谓“独立拥有的虚拟地址”是指对于每一个进程,你可以访问自己的0-4G的虚拟地址。虚拟地址是“虚拟”的,需要转化为“真实”的物理地址。好比你有你的地址簿,我有我的地址簿。你和我的地址簿都有1、2、3、4页,但是每页里面的实际内容是不一样的,我的地址簿第1页写着3,你的地址簿第1页写着4,对你我自己来说都是用第1页(虚拟),实际上用的分别是第3、4页(物理),不冲突。
内核用的896M虚拟地址是直接映射的,意思是只要把虚拟地址减去一个偏移量(3G)就等于物理地址。同样,这里指的还是寻址,实际使用前还是要分配内存。而且896M只是个最大值。如果物理内存小,内核能使用(分配)的可用内存也小。
kmalloc - kfree
kmalloc申请的内存在物理内存上是连续的,他们与真实的物理地址只有一个固定的偏移,因此存在简单的转换关系。这个API 多用来申请不到一个page大小的内存。kmalloc的底层需要调用__get_free_pages,参数中表示内存类型的gtp_t flags正是这个函数的缩写,常用的内存类型有,GFP_KERNEL,GFP_ATOMIC。
- GFP_KERNEL是最常用的flag,注意,使用这个flag来申请内存时,如果暂时不能满足,会引起进程阻塞,So,一定不要在中断处理函数,tasklet和内核定时器等非进程上下文中使用GFP_KERNEL!!!
GFP_ATOMIC就可以用于上述三种情境,这个flag表示如果申请的内存不能用,则立即返回,中断上下文中用该标志
__get_free_pages - free_pages
__get_free_pages()与kmalloc()一样是物理连续的内存,这一系列函数是Linux内核中最底层的用于获取空闲内存的方法,因为底层的buddy算法都是以(2^n)×PAGE_SIZE来管理内存的,所以他们总是以页为单位分配内存的
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
同系列API还有
unsigned long __get_free_page(gfp_t gfp)
unsigned long get_zeroed_page(gfp_t gfp_mask)
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
void free_page(unsigned long addr)
vmalloc - vfree
vmalloc在虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,所以vmalloc申请的虚拟内存和物理内存之间也就没有简单的换算关系,正因如此,vmalloc()通常用于分配远大于__get_free_pages()的内存空间,它的实现需要建立新的页表,此外还会调用使用GFP_KERN的kmalloc,so,一定不要在中断处理函数,tasklet和内核定时器等非进程上下文中使用vmalloc!
/**
* vmalloc - allocate virtually contiguous memory
* @size: allocation size
* Allocate enough pages to cover @size from the page level allocator and map them into contiguous kernel virtual space.
*/
void *vmalloc(unsigned long size)
/**
* vfree - release memory allocated by vmalloc()
* @addr: memory base address
*/
void vfree(const void *addr)
同系列的API还有
/**
* vmalloc_32 - allocate virtually contiguous memory (32bit addressable)
* @size: allocation size
* Allocate enough 32bit PA addressable pages to cover @size from the page level allocator and map them into contiguous kernel virtual space.
*/
void *vmalloc_32(unsigned long size)
slab缓存
我们知道,页是内存映射的基本单位,但内核中很多频繁创建的对象所需内存都不到一页,此时如果仍然按照页映射的方式,频繁的进行分配和释放就会造成资源的浪费,同时也会降低系统性能。为了解决的这样的问题,内核引入了slab机制,使对象在前后两次被使用时被分配在同一块内存或同一类内存空间,且保留了基本的数据结构,就可以大大提高效率。kmalloc的底层即是使用slab算法管理分配的内存的。注意,slab依然是以页为单位进行映射,只是映射之后分割这些页为相同的更小的单元,从而节省了内存。slab分配的单元不能小于32B或大于128K。
/**
* kmem_cache_create - 创建slab缓存对象
* @name:slab缓存区名字,
* @size:slab分配的缓存区的每一个单元的大小
* @align:缓存区内存的对齐方式,一般给0
* @flags:控制分配的位掩码,
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5) to catch references to uninitialised memory.
* %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check for buffer overruns.
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware cacheline. This can be beneficial if you're counting cycles as closely as davem.
* %SLAB_CACHE_DMA - Use GFP_DMA memory
* %SLAB_STORE_USER - Store the last owner for bug hunting
*define SLAB_PANIC - Panic if kmem_cache_create() fails
*/
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,unsigned long flags, void (*ctor)(void *))
/**
* kmem_cache_alloc - Allocate an object from this cache.
* @cachep: The cache to allocate from.
* @flags: See kmalloc().
* The flags are only relevant if the cache has no available objects.
*/
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
/**
* kmem_cache_free - Deallocate an object
* @cachep: The cache the allocation was from.
* @objp: The previously allocated object.
* Free an object which was previously allocated from this cache.
*/
void kmem_cache_free(struct kmem_cache *cachep, void *objp)
void kmem_cache_destroy(struct kmem_cache *s)
范例
//创建slab对象
struct kmem_cache_t *xj_sbcache;
xj_sbcache = kmem_cache_create("xjslab",sizeof(struct xj_unit_t),0,SLAB_CACHE_DMA|SLAB_PANIC,NULL,NULL);
//分配slab缓存
struct xj_unit_t *xj_unit;
xj_unit = kmem_cache_alloc(xj_sbcache,GFP_KERNEL);
/* 使用slab缓存 */
/* 释放slab缓存 */
kmem_cache_free(xj_sbcache, xj_unit);
/* 销毁slab缓存 */
kmem_cache_destroy(xj_sbcache);
内存池
除了slab机制,内核还提供了传统的内存池机制来管理小块内存的分配。内存池主要是用来解决可能出现的内存不足的情况,因为一个内存池在创建的时候就已经分配好了一内存,当我们用mempool_alloc向一个已经创建好的内存池申请申请内存时,该函数首先会尝试回调内存池创建时的分配内存函数,如果已经没有内存可以分配,他就会使用内存池创建时预先分配的内存,这样就可以避免因为无内存分配而陷入休眠,当然,如果预分配的内存也已经使用完毕,还是会陷入休眠。slab机制的目的是提高内存使用率以及内存管理效率,内存池的目的是避免内存的分配失败。下面是内核中提供的关于内存池的API
/**
* mempool_create - create a memory pool
* @min_nr: the minimum number of elements guaranteed to be allocated for this pool.
* @alloc_fn: user-defined element-allocation function.
* @free_fn: user-defined element-freeing function.
* @pool_data: optional private data available to the user-defined functions.
*
* this function creates and allocates a guaranteed size, preallocated memory pool. The pool can be used from the mempool_alloc() and mempool_free() functions.
* This function might sleep. Both the alloc_fn() and the free_fn() functions might sleep - as long as the mempool_alloc() function is not called from IRQ contexts.
*/
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data)
/**
* mempool_alloc - allocate an element from a specific memory pool
* @pool: pointer to the memory pool which was allocated via mempool_create().
* @gfp_mask: the usual allocation bitmask.
* this function only sleeps if the alloc_fn() function sleeps or returns NULL. Note that due to preallocation, this function never* fails when called from process contexts. (it might fail if called from an IRQ context.)
*/
void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask)
/**
* mempool_free - return an element to the pool.
* @element: pool element pointer.
* @pool: pointer to the memory pool which was allocated via mempool_create().
*
* this function only sleeps if the free_fn() function sleeps.
*/
void mempool_free(void *element, mempool_t *pool)
/**
* mempool_destroy - deallocate a memory pool
* @pool: pointer to the memory pool which was allocated via mempool_create().
*
* Free all reserved elements in @pool and @pool itself. This function only sleeps if the free_fn() function sleeps.
*/
void mempool_destroy(mempool_t *pool)