现在的位置: 首页 > 综合 > 正文

内核学习笔记知识点记录(更新中)

2013年08月16日 ⁄ 综合 ⁄ 共 4608字 ⁄ 字号 评论关闭

1. 关于waitpid

A fork   B,  B中的b0 通过pthread_create b1
A waitpid (B)    b0 pthread_join(b1)
b0,b1的ppid都是A
b1的gpid是b0

如果b1退出,则b1通过do_exit进入Z,等待b0通过pthread_join回收
如果b0退出,则A waitpid被唤醒,遍历b0,b1的状态,发现b1不是Z,b0是Z,
而b0是领头线程,不会回收

waitpid流程分析:

kernel/exit.c

SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
infop, int, options, struct rusage __user *, ru)
{
do_wait();
}

static long do_wait(struct wait_opts *wo)
{
        //初始化父进程的等待个体child_wait
init_waitqueue_func_entry(&wo->child_wait, child_wait_callback);
//该个体对应当前进程
wo->child_wait.private = current;
        //将child_wait加到当前进程的wait_chldexit等待队列里。后续可能
        // current进程被切换出去,再被其他进程通过signal->wait_chldexit
        //来唤醒
        add_wait_queue(&current->signal->wait_chldexit, &wo->child_wait);

        //schedule时可以摘除
        set_current_state(TASK_INTERRUPTIBLE);

        do {
retval = do_wait_thread(wo, tsk);
if (retval)
goto end;

retval = ptrace_do_wait(wo, tsk);
if (retval)
goto end;

if (wo->wo_flags & __WNOTHREAD)
break;
} while_each_thread(current, tsk); 

        其中,
        #define while_each_thread(g, t) \
while ((t = next_thread(t)) != g) 即遍历所有和tsk同组的线程
        
}

2. 关于dropcache, 最后会将“干净的”page cache回收

3. 关于文件系统学习。

4. cache mips

4路组相连,cache size 32k,cache line 16 bytes

那么就是4行cache line为一组,总共有32k / (4*32) = 512组

关于cache指令

HIT操作:先用addr的bit4-bit12组选,再尝试用bit31-bit15的va经过tlb得到pa,与组内的tag同时匹配,
如果匹配,则命中(HIT),执行cache后的行操作,否则什么都不做。
疑问,tag就是物理地址的bit31-bit15?

还有一种操作,是index访问。在组算后,不经过TLB,直接用地址的bit14-bit13进行组内索引。主要是用在不需要
刷新物理地址的场合。如内核空间的虚拟地址刷新。同时,为了避免寻址的问题,待index刷新的地址,需要是
unmapped区域。

关于引起cache aliase的终极解释:
什么是cache aliase? 就是同一个物理地址,对应着2个的虚拟地址,而这2个虚拟地址,分别在不同的cache 
组里。造成同步的问题。

为什么会形成这种现象? 因为,如果两个虚拟地址对应的物理地址相同,那么虚拟地址的低12位,一定相同,
而bit12-bit31一定不同。
在VIPT的cache结构中,抽取bit 2 - bit11作为虚拟地址index的话,那么一定会映射到同一个组,不会引起cache 
aliase,如果抽取bit 2 - bit 12,而bit12不相同的话,则映射到不同的组,引起cache aliase问题。
因此问题的根源在于,你设置的内核页面是多大?是否比抽取的虚拟地址低n位(就是way size)大?大的话,则不会有cache aliase
,所以尽量把内核的页面设置的比较大就对了。

5. 伙伴系统判断内存是否够用的__zone_watermark_ok

if (free_pages <= min + z->lowmem_reserve[classzone_idx])
return 0;
for (o = 0; o < order; o++) {
/* At the next order, this order's pages become unavailable */
free_pages -= z->free_area[o].nr_free << o;

/* Require fewer higher order pages to be free */
min >>= 1;

if (free_pages <= min)
return 0;
}

      判断的思路是:
      5.1) 如果本次分配order的页面,即1<<order个页面, 那么剩下的页面要大于水线加上保留页面的个数
      5.2) 如果5.1检查通过了还没完,还要保证,这剩下的页面,可以给剩下的阶,从0 到 order -1, 依次尝试分配完后,每次都还有一定富余的页面。
           通俗的讲,就是 free_pages = o[0] + o[1] +...+o[order]+...+o[11] - o[order]
             首先判断free+pages是否大于min,
             接着判断free_pages - o[0] 是否大于 min/2
             在判断free_pages - o[1] 是否大于min/4

              最后判断free_pages-o[order-1]是否大于min/2*(order-1)
               为什么要这么判断?

6. rtmutex  try_to_steal_lock(lock)
   尝试将一个处于pending 状态的owner的锁偷取
   疑问是,next = rt_mutex_top_waiter(lock); 这里是取当前锁阻塞住的最高优先级对象,
   将此对象从

7. cmpxchg %ecx, %ebx;如果EAX与EBX相等,则ECX送EBX且ZF置1;否则EBX送EAX,且ZF清0

8. 
mips64 xlp
pte entry 
低6位是给软件用,如_PAGE_DIRTY ,低6位的操作由宏_PAGE_GLOBAL_SHIFT来控制
#define _PAGE_GLOBAL_SHIFT 6
接下来的bit就是和entry low一一对应。
如gloabal 位
#define _PAGE_GLOBAL       (1 << _PAGE_GLOBAL_SHIFT)
valid位
#define _PAGE_VALID_SHIFT  (_PAGE_GLOBAL_SHIFT + 1)
以及我们关注的3个C位
#define _CACHE_SHIFT       (_PAGE_DIRTY_SHIFT + 1)
#define _CACHE_MASK        (7 << _CACHE_SHIFT)

#define _PFN_SHIFT     (PAGE_SHIFT - 12 + _CACHE_SHIFT + 3)
这个_PFN_SHIFT为什么会和PAGE_SHIFT搅在一起?我们来看xlp里关于entry low的pfn位(bit6-bit33)
的解释:The PFN field corresponds to bits 35:12 of the physical address
即是说,entry low里的pfn保存的是以4k为大小的页帧号,而pte entry里保存的却是按实际页大小计算
出来的页帧号,所以需要左移相应的位数,写到entry low的pfn字段里。

hello

关于int check_tlb_info(unsigned long vaddr)函数的一点疑问,此处我觉得可能会造成tlb漏打:

这个函数里,判断虚拟地址与tlb条目是否匹配的条件是:

unsigned long vaddr_entry = vaddr&(~0x1fffUL);
if(vaddr_entry == (entryhi & ~0x1fffUL))

即去掉entryhi里bit0 - bit 12后,与虚拟地址的相应位做比较。
我的理解是,此比较的前提是内核采用4k页面。 

当系统采用16k页面时,还应该考虑pagemask的相应位:(见see mips run 6.2 节)

也就是说,判断我觉得应该改为:

unsigned long vaddr_entry = vaddr&(~0x7fffUL);
if(vaddr_entry == (entryhi & ~0x7fffUL))

这样才不会漏打tlb。

 例如:
在16k页面的系统里,
 vaddr:0x120003bf0
 entry:0x12000002d

如果只掩掉低13位,他们是不相等的;
但如果掩掉低15位,他们是相等的,也就是此tlb entry实际上是匹配vaddr虚拟地址的。

当在ade异常里,又发生tlb miss怎么办?
因为exl = 1, 所以会进tlbl异常。在build_r4000_tlb_load_handler里,
首先通过build_r4000_tlbchange_handler_head得到出问题地址所在的页表项pte
并检查是否有效build_pte_present(&p, &r, K0, K1, label_nopage_tlbl);
如果没有_PAGE_PRESENT标志,则跳转至后面nopage_tlbl标记处,并进入do_page_fault;
如果有_PAGE_PRESENT标志,则通过build_r4000_tlbchange_handler_tail将pte设置到tlb entry里。
具体写到哪个tlb entry? 根据虚拟地址probe找到匹配的tlb条目。如果没有匹配的,就通过
random写入。同时,在page_fault流程里,一旦走到最后的handle_pte_fault,进入__update_tlb,
也是先根据虚拟地址probe,找得到就write_index_tlb,找不到就 write_random_tlb

9. 缺页流程里,handle_mm_fault,首先是pmd_alloc,pte_alloc_map检查是否存在对应的页表,没有就分配,
最后传入对应的pte entry的指针,和上一层指向pte表的 pmd entry指针

handle_pte_fault(mm, vma, address, pte, pmd, flags);

重新获取pte entry指针的地址,并加锁
page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
将pte entry设置到条目里 
set_pte_at(mm, address, page_table, entry);
对于mips来说,update_mmu_cache会更新相应的tlb entry并刷一下cache
先是__update_tlb里,会根据地址查找tlb entry,找得到的话就更新至此tlb,找不到就更新到一个random的tlb
对于__update_cache来说,则按L1_CACHE_BYTES,循环刷dcache, 直到刷满一页。

抱歉!评论已关闭.