Linux高端内存管理之内核映射
作者:网络转载 发布时间:[ 2014/2/28 9:14:48 ] 推荐标签:Linux 内存管理 内核映射
page_address()函数返回页框对应的线性地址
* Returns the page's virtual address.
*/
/*返回页框对应的线性地址*/
void *page_address(struct page *page)
{
unsigned long flags;
void *ret;
struct page_address_slot *pas;
/*如果页框不在高端内存中*/
if (!PageHighMem(page))
/*线性地址总是存在,通过计算页框下标
然后将其转换成物理地址,后根据相应的
物理地址得到线性地址*/
return lowmem_page_address(page);
/*从page_address_htable散列表中得到pas*/
pas = page_slot(page);
ret = NULL;
spin_lock_irqsave(&pas->lock, flags);
if (!list_empty(&pas->lh)) {/*如果对应的链表不空,
该链表中存放的是page_address_map结构*/
struct page_address_map *pam;
/*对每个链表中的元素*/
list_for_each_entry(pam, &pas->lh, list) {
if (pam->page == page) {
ret = pam->virtual;/*返回线性地址*/
goto done;
}
}
}
done:
spin_unlock_irqrestore(&pas->lock, flags);
return ret;
}
kmap()函数建立内核映射。
/*高端内存映射,运用数组进行操作分配情况
分配好后需要加入哈希表中;*/
void *kmap(struct page *page)
{
might_sleep();
if (!PageHighMem(page))/*如果页框不属于高端内存*/
return page_address(page);
return kmap_high(page);/*页框确实属于高端内存*/
}
/**
* kmap_high - map a highmem page into memory
* @page: &struct page to map
*
* Returns the page's virtual memory address.
*
* We cannot call this from interrupts, as it may block.
*/
void *kmap_high(struct page *page)
{
unsigned long vaddr;
/*
* For highmem pages, we can't trust "virtual" until
* after we have the lock.
*/
lock_kmap();/*保护页表免受多处理器系统上的
并发访问*/
/*检查是否已经被映射*/
vaddr = (unsigned long)page_address(page);
if (!vaddr)/*如果没有*/
/*把页框的物理地址插入到pkmap_page_table的
一个项中并在page_address_htable散列表中加入一个
元素*/
vaddr = map_new_virtual(page);
pkmap_count[PKMAP_NR(vaddr)]++;/*分配计数加一,此时流程都正确应该是2了*/
BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2);
unlock_kmap();
return (void*) vaddr;/*返回地址*/
}
static inline unsigned long map_new_virtual(struct page *page)
{
unsigned long vaddr;
int count;
start:
count = LAST_PKMAP;
/* Find an empty entry */
for (;;) {
/*加1,防止越界*/
last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK;
/*
接下来判断什么时候last_pkmap_nr等于0,等于0表示1023(LAST_PKMAP(1024)-1)个页表项已经被分配了
,这时候需要调用flush_all_zero_pkmaps()函数,把所有pkmap_count[] 计数为1的页表项在TLB里面的entry给flush掉
,并重置为0,这表示该页表项又可以用了,可能会有疑惑为什么不在把pkmap_count置为1的时候也
是解除映射的同时把TLB也flush呢?
个人感觉有可能是为了效率的问题吧,毕竟等到不够的时候再刷新,效率要好点吧。
*/
if (!last_pkmap_nr) {
flush_all_zero_pkmaps();
count = LAST_PKMAP;
}
if (!pkmap_count[last_pkmap_nr])
break; /* Found a usable entry */
if (--count)
continue;
/*
* Sleep for somebody else to unmap their entries
*/
{
DECLARE_WAITQUEUE(wait, current);
__set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&pkmap_map_wait, &wait);
unlock_kmap();
schedule();
remove_wait_queue(&pkmap_map_wait, &wait);
lock_kmap();
/* Somebody else might have mapped it while we slept */
if (page_address(page))
return (unsigned long)page_address(page);
/* Re-start */
goto start;
}
}
/*返回这个页表项对应的线性地址vaddr.*/
vaddr = PKMAP_ADDR(last_pkmap_nr);
/*
v
set_pte_at(mm, addr, ptep, pte)函数在NON-PAE i386上的实现其实很简单,其实等同于下面的代码:
static inline void native_set_pte(pte_t *ptep , pte_t pte)
{
*ptep = pte;
}
*/ set_pte_at(&init_mm, vaddr,/*设置页表项*/
&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));
/*接下来把pkmap_count[last_pkmap_nr]置为1,1不是表示不可用吗,
既然映射已经建立好了,应该赋值为2呀,其实这个操作
是在他的上层函数kmap_high里面完成的(pkmap_count[PKMAP_NR(vaddr)]++).*/
pkmap_count[last_pkmap_nr] = 1;
/*到此为止,整个映射完成了,再把page和对应的线性地址
加入到page_address_htable哈希链表里面可以了*/
set_page_address(page, (void *)vaddr);
return vaddr;
}
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11