版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、.:.;一、Linux进程切换深化分析#define CLONE_KERNEL(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)创建内核线程时运用的CLONE标志。1#define unlikely(x)_builtin_expect(!(x), 0)编译器优化,实践前往值x是整型表达式,0表示并不预期该事件发生,也就是说x为0的能够性很小,这是为了让编译器对下面得语句进展优化。2进程内核态堆栈构造:进程是动态实体,进程描画符是存放在动态内存中的。在一块进程内存区上,Linux存放了两个数据构造:指向task_struct得thread_info和内核态的进程栈。
2、大小普通2页8K,这要求页面帧对齐2的13次幂,在X86上编译时可以配置大小为4K。thread_info在内存区开场处,内核栈从内存尾向下增长。在C言语中可以用union构造表示:图1. 8K内核栈和进程描画符task_struct及thread_info的相互关系union thread_union struct thread_info thread_info;unsigned long stack2048; /* 1024 for 4KB stacks */;CPU的esp存放器用于执行堆栈的顶部指针,当从用户态转向内核态时,进程内核栈总是空的,所以esp就会执行堆栈底部。运用alloc
3、_thread_info和free_thread_info用于分配和释放一个存放thread_info构造和内核堆栈的内存区。内核经过当前esp指针可以很方便的得到thread_info构造的地址。current_thread_info(void)的原理即如下:movl $0 xffff2000,%ecx /* or 0 xfffff000 for 4KB stacks */andl %esp,%ecxmovl %ecx,pthread_info中task指针是第一个,所以current宏相当于current_thread_info( )-task,从而也就得到task指针。每个进程有本人独立
4、得进程空间,一切进程共享CPU存放器。进程继续执行时必需装入存放器恢复得数据集称为硬件上下文环境。在Linux中部分硬件上下文存放在进程描画符中,部分存放到内核态堆栈里。3.进程切换堆栈原理:每个进程有本人独立得进程空间,一切进程共享CPU存放器。进程继续执行时必需装入存放器恢复得数据集称为硬件上下文环境。在Linux中部分硬件上下文存放在进程描画符中,部分存放到内核态堆栈里。80 x86体系支持在进程TSS段跳转时自动执行进程硬件上下文切换。Linux运用软件方法实现。软件方式效率差不多,当更灵敏,可以控制流程,留下优化空间。80 x86用TSS段保管硬件上下文内容,每个CPU有一个TSS段
5、。从用户态到内核态切换时,从TSS中取出内核栈地址。用户态进程访问I/O端口时,TSS中的I/O访问位图可以验证权限。tss_struct描画了TSS格式,init_tss存放初始TSS内容,每次进程切换,内核更新TSS中的某些字段,以反映当前运转进程的权限等级。每个进程有个反映义务CPU形状的thread_struct构造变量thread,除eax、ecx等通用存放器内容保管在内核态堆栈中,其他大部分存放器都保管在次构造中。该构造一部分对应于tss_struct中的内容,进程切换时把thread中某些内容更新到tss_struct中就可以反映当前义务的运转CPU环境。struct tss_s
6、tructunsigned shortback_link,_blh;unsigned longesp0;unsigned shortss0,_ss0h;unsigned longesp1;unsigned shortss1,_ss1h;/* ss1 is used to cache MSR_IA32_SYSENTER_CS */unsigned longesp2;unsigned shortss2,_ss2h;unsigned long_cr3;unsigned longeip;unsigned longeflags;unsigned longeax,ecx,edx,ebx;unsigned
7、longesp;unsigned longebp;unsigned longesi;unsigned longedi;unsigned shortes, _esh;unsigned shortcs, _csh;unsigned shortss, _ssh;unsigned shortds, _dsh;unsigned shortfs, _fsh;unsigned shortgs, _gsh;unsigned shortldt, _ldth;unsigned shorttrace, io_bitmap_base;/* The extra 1 is there because the CPU wi
8、ll access an* additional byte beyond the end of the IO permission* bitmap. The extra byte must be all 1 bits, and must* be within the limit.*/unsigned longio_bitmapIO_BITMAP_LONGS + 1;/* Cache the current maximum and the last task that used the bitmap:*/unsigned long io_bitmap_max;struct thread_stru
9、ct *io_bitmap_owner;/* pads the TSS to be cacheline-aligned (size is 0 x100)*/unsigned long _cacheline_filler35;/* . and then another 0 x100 bytes for emergency kernel stack*/unsigned long stack64; _attribute_(packed);struct thread_struct/* cached TLS descriptors. */struct desc_struct tls_arrayGDT_E
10、NTRY_TLS_ENTRIES;unsigned longesp0;unsigned longsysenter_cs;unsigned longeip;unsigned longesp;unsigned longfs;unsigned longgs;/* Hardware debugging registers */unsigned longdebugreg8;/* %db0-7 debug registers */* fault info */unsigned longcr2, trap_no, error_code;/* floating point info */union i387_
11、unioni387;/* virtual 86 mode info */struct vm86_struct _user * vm86_info;unsigned longscreen_bitmap;unsigned longv86flags, v86mask, saved_esp0;unsigned intsaved_fs, saved_gs;/* IO permissions */unsigned long*io_bitmap_ptr;unsigned longiopl;/* max allowed port in the bitmap, in bytes: */unsigned long
12、io_bitmap_max;4进程切换流程解析switch_to进程切换本质上两步:1)进程页表PGD切换;2)内核态堆栈和硬件上下文切换包括CPU存放器;上面两步经过context_switch()实现,它经过调用switch_mm()切换进程空间,switch_to切换内核上下文环境。首先看看context_switch()做了些什么:1)进程描画符中active_mm执行进程运用的地址空间,mm执行进程拥有的地址空间,对于普通进程它们一样。对于内核线程,它的mm总为NULL。所以context_switch()首先判别if (!next-mm)即next为内核线程,那么运用prev的进程
13、地址空间:if (!next-mm) next-active_mm = prev-active_mm; atomic_inc(&prev-active_mm-mm_count); enter_lazy_tlb(prev-active_mm, next);2)否那么,假设next是普通进程,那么用next进程空间交换prev的地址空间:switch_mm(oldmm, mm, next);3)假设prev是内核线程或者正在退出,那么设置prev-active_mm和runqueue的prev_mm为NULL:if (!prev-mm) prev-active_mm = NULL;WARN_ON(
14、rq-prev_mm);rq-prev_mm = oldmm;下面看看switch_mm()如何切换进程空间:1)获取cpu逻辑号。2)cpu_clear(cpu, prev-cpu_vm_mask)去除cpu_vm_mask位标志。3)per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK设置cpu_tlbstate形状。4)per_cpu(cpu_tlbstate, cpu).active_mm = next设置cpu_tlbstate的active_mm为next。5)cpu_set(cpu, next-cpu_vm_mask)设置next的cpu_
15、vm_mask标志。6)load_cr3(next-pgd)装载next的pgd页表到cr3存放器。7)假设next的LDT描画符改动,那么加载next的LDT描画符。if (unlikely(prev-context.ldt != next-context.ldt)load_LDT_nolock(&next-context);最后,switch_to进展内核堆栈和CPU环境切换操作:#define switch_to(prev,next,last) do unsigned long esi,edi;asm volatile(pushflnt/* Save flags */pushl %ebp
16、ntmovl %esp,%0nt/* save ESP */movl %5,%espnt/* restore ESP */movl $1f,%1nt/* save EIP */pushl %6nt/* restore EIP */jmp _switch_ton1:tpopl %ebpntpopfl:=m (prev-thread.esp),=m (prev-thread.eip),=a (last),=S (esi),=D (edi):m (next-thread.esp),m (next-thread.eip),2 (prev), d (next); while (0)流程描画,prev是进
17、程A的task构造,next是进程B的task构造,last是进程C的构造:1)保管prev和next指针的值到eax和edx:movl prev, %eaxmovl next, %edx2)保管eflags和ebp存放器内容到prev内核态堆栈中:pushflpushl %ebp3)将esp内容保管到prev-thread.esp中,该字段执行prev内核堆栈的top地址。movl %esp,484(%eax)4)将next-thread.esp加载到esp中,如今开场,esp执行next的内核堆栈,进程切换完成。movl 484(%edx), %esp5)保管下面Label 1到prev-
18、thread.eip指针中,当prev进程恢复运转时,从该位置开场运转。movl $1f, 480(%eax)6)将next-thread.eip的指针内容压到next的内核态堆栈中,通常它的内容也是Label 1。pushl 480(%edx)7)跳转到_switch_toC函数执行。jmp _switch_to8)被交换的进程A继续执行,它在Label 1处,首先是恢复eflags和ebp存放器内容。留意这里是发生在调度器选择prev在CPU上运转后,次数esp曾经执行了prev的内核堆栈。1:popl %ebppopfl9)将eax内容保管到last义务构造中。这里eax是被进程A切换下
19、来的进程C的task构造指针。movl %eax, last5_switch_to深化分析_switch_to参数是存放在eax和edx中的内容,这经过#define fastcall_attribute_(regparm(3)通知gcc编译器。1)获取tss_struct tss、prev_p和next_p的thread_struct构造prev和next、当前CPU逻辑ID。2)调用_unlazy_fpu(prev_p)根据条件标志选择能否保管prev_p的FPU, MMX,和XMM存放器内容。3)load_esp0(tss, next)将next的堆栈地址存放到tss中:tss-esp0
20、 = thread-esp0。4)savesegment(gs, prev-gs)保管gs存放器到prev-gs,fs曾经在栈入口保管,es和ds在内核态下不需求保管。5)load_TLS(next, cpu)从next的tls_array缓存中加载线程的Thread-Local Storage描画符。TLS在GDT表中位置6、7、8。cpu_gdt_tablecpu6 = next_p-thread.tls_array0;cpu_gdt_tablecpu7 = next_p-thread.tls_array1;cpu_gdt_tablecpu8 = next_p-thread.tls_arr
21、ay2;6)假设当前特权级别是0并且prev-iopl != next-iopl那么恢复IOPL设置set_iopl_mask(next-iopl)。7)根据thread_info的TIF标志_TIF_WORK_CTXSW和TIF_IO_BITMAP判别能否需求处置debug存放器和IO位图:_switch_to_xtra(next_p, tss);l只需当next_p挂起时即if (test_tsk_thread_flag(next_p, TIF_DEBUG)运用了debug存放器才需求恢复set_debugreg(next-debugregi, i)。只需调试器需求监控prev的形状时,p
22、rev_p-thread.debugreg数组的内容才会被修正。Debug存放器dr0dr7,dr4和dr5不用。l当prev_p或者next_p定义了本人的I/O访问位图时,必需更新TSS的I/O bitmap。if (prev_p-thread.io_bitmap_ptr | next_p-thread.io_bitmap_ptr)handle_io_bitmap(&next_p-thread, &init_tsscpu);进程的I/O访问位图存放在io_bitmap_ptr指针里,通常进程很少修正IO位图,只需当前时间片中访问IO端口才会把实践的IO位图加载到TSS中。当next_p没有
23、自定义位图时:tss-io_bitmap_base = INVALID_IO_BITMAP_OFFSET;前往假设next = tss-io_bitmap_owner那么设置有效的偏移量:tss-io_bitmap_base = IO_BITMAP_OFFSET;前往否那么tss-io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY;只需第二种情况tss-io_bitmap_base设置的是有效的io_bitmap偏移量,对于其他两种情况,当用户进程访问I/O端口时将会触发General protection的异常,do_general_protecti
24、on( )异常处置函数根据io_bitmap的值处置异常:假设是0 x8000(INVALID_IO_BITMAP_OFFSET)那么发送SIGSEGV信号给用户进程;假设是0 x9000(INVALID_IO_BITMAP_OFFSET_LAZY)那么拷贝进程的thread中的io_bitmap_ptr内容到io_bitmap中,并设置io_bitmap_base为正确的偏移量(104)。8)disable_tsc(prev_p, next_p)设置cr4中的TSC Disable位。9)arch_leave_lazy_cpu_mode()设置CPU的lazy方式。10)假设next_p-f
25、pu_counter 5那么恢复next_p的FPU存放器内容:math_state_restore()。FPU存放器存放在next_p-thread-i387中,i387是i387_union的union构造:union i387_union struct i387_fsave_structfsave;struct i387_fxsave_structfxsave;struct i387_soft_struct soft;struct i387_fxsave_struct unsigned shortcwd;unsigned shortswd;unsigned shorttwd;unsign
26、ed shortfop;longfip;longfcs;longfoo;longfos;longmxcsr;longmxcsr_mask;longst_space32;/* 8*16 bytes for each FP-reg = 128 bytes */longxmm_space32;/* 8*16 bytes for each XMM-reg = 128 bytes */longpadding56; _attribute_ (aligned (16);11)假设需求,那么从next-gs中恢复gs存放器内容。if (prev-gs | next-gs)loadsegment(gs, nex
27、t-gs);二、Linux实时调度schedule1概述三种调度战略:SCHED_FIFO,SCHED_RR和SCHED_NORMAL。FIFO实时调度算法当调度器将CPU指定给某个进程时,它把该进程放到运转队列首;除非有更高优先级的进程,否那么该进程将不断占用CPU。Round Robin实时进程调度把CPU指定给某进程,把它放到运转队列尾。时间片运转完再选择其他进程调度。这样保证了同优先级的公平竞争CPU。SCHED_NORMAL是普通的基于运转时间和等待时间等,动态调整进程优先级的一种调度战略。实时进程优先级1100,普通101。2实时进程调度的时机1)该进程被更高优先级的进程抢占;2)
28、进程执行一个阻塞操作,被放到睡眠队列,形状为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE;3)进程被终止(形状为TASK_STOPPED或TASK_TRACED),或者进程被杀死(形状为EXIT_ZOMBIE或EXIT_DEAD)4)进程调用sched_yield()自动放弃CPU;5)RR实时进程用完了CPU分配的时间片;3调度器相关函数1)scheduler_tick( )更新当前进程的运转时间片tick值,在update_process_times( )中调用,判别进程的时间片能否用完。2)try_to_wake_up( )唤醒一个睡眠的进程并把它的形状
29、设为TASK_RUNNING,插入到运转队列中。3)recalc_task_prio( )更新进程的睡眠时间和动态优先级,SCHED_NORMAL调度。4)schedule( )进程调度5)load_balance()SMP系统的负载平衡。4schedule( )函数进程调度有两种方式:直接调用和延迟调用。直接调用schedule,当前进程资源不可用时会直接调用调度器,这种情况下,内核线程进展如下处置:1)将current插入到适宜的等待队列中;2)将current形状变为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE3)调用schedule();4)检查资源能
30、否可用,假设不可用,转到第2步;5)一旦资源可用,从等待队列中移除current进程;在设备驱动程序中也经常会检查TIF_NEED_RESCHED并调用schedule()。延迟调用方式是经过设置current进程的TIF_NEED_RESCHED标志为1。当恢复用户态进程的执行前,会检查该标志并决议能否调用schedule()。延迟调度的情形有:1)在scheduler_tick()中假设current用完了时间片那么设置该标志;2)在try_to_wake_up( )中唤醒一个进程并且该进程比当前运转进程优先级高。3)调用sched_setscheduler()时。schedule()函数
31、任务流程:进程切换前的任务:1)制止内核抢占,初始化部分变量prev,释放prev占有的大内核锁;need_resched:preempt_disable();prev = current;release_kernel_lock(prev);2)读取调度TSC时间,计算调整run_time时间, 更新调度形状rq-sched_cnt参数,获取rq的spin锁:spin_lock_irq(&rq-lock)。3)检查prev形状:假设形状不是TASK_RUNNING且没有在内核态被抢占,那么从运转队列中移除;但是假设prev形状是TASK_INTERRUPTIBLE并且拥有非阻塞挂起的信号,那么
32、把进程形状设为TASK_RUNNING不移出运转队列。if (prev-state & !(preempt_count() & PREEMPT_ACTIVE) switch_count = &prev-nvcsw;if (unlikely(prev-state & TASK_INTERRUPTIBLE) &unlikely(signal_pending(prev)prev-state = TASK_RUNNING;else if (prev-state = TASK_UNINTERRUPTIBLE)rq-nr_uninterruptible+;deactivate_task(prev, rq)
33、;4)获取当前CPU逻辑号,假设当前运转队列为空,那么调用idle_balance(cpu, rq)从其他CPU运转队列上拉进程到本地CPU的运转队列上。假设调整后,当前运转队列仍为空那么next赋为idle进程,跳转到义务切换代码行去。if (unlikely(!rq-nr_running) idle_balance(cpu, rq);if (!rq-nr_running) next = rq-idle;rq-expired_timestamp = 0;goto switch_tasks;5)假设runqueue中有进程,并且当前活得进程数为0,那么交换active和expired队列指针。
34、array = rq-active;if (unlikely(!array-nr_active) schedstat_inc(rq, sched_switch);rq-active = rq-expired;rq-expired = array;array = rq-active;rq-expired_timestamp = 0;rq-best_expired_prio = MAX_PRIO;6)从运转队列的活动prio_array数据的位图中查找第一个位设置为1的索引,根据索引找到该优先级队列的第一个task。idx = sched_find_first_bit(array-bitmap);
35、queue = array-queue + idx;next = list_entry(queue-next, struct task_struct, run_list);7)假设next是普通进程,并且next-sleep_type是SLEEP_INTERACTIVE或SLEEP_INTERRUPTED,那么重新计算进程睡眠时间和进程优先级。进程切换任务:8)更新sched_goidle,预期next构造数据,去除TIF_NEED_RESCHED标志,设置quiescent形状计数为1:rcu_data-passed_quiesc = 1;switch_tasks:if (next = rq
36、-idle)schedstat_inc(rq, sched_goidle);prefetch(next);prefetch_stack(next);clear_tsk_need_resched(prev);rcu_qsctr_inc(task_cpu(prev);9)更新prev进程运转时间戳prev-sleep_avg,prev-timestamp;10)调度信息切换到next,更新next;时间戳和运转队列信息:sched_info_switch(prev, next);if (likely(prev != next) next-timestamp = next-last_ran = no
37、w;rq-nr_switches+;rq-curr = next;+*switch_count;11)进展进程切换,context_switch参见前面的分析,它进展进程空间和内核堆栈切换。prepare_lock_switch功能是在定义了_ARCH_WANT_INTERRUPTS_ON_CTXSW情况下,在切换前开中断spin_unlock_irq(&rq-lock);barrier()是保证代码执行顺序不变。prepare_task_switch(rq, next);prev = context_switch(rq, prev, next);barrier();finish_task_s
38、witch(this_rq(), prev);进程切换后的任务:进程切换context_switch语句之后的代码并不是由next进程立刻执行的,而是由调度器选择prev进程继续执行的。次时prev变量指向的曾经是被prev进程交换的其他进程的指针。12)finish_task_switch()必需与prepare_task_switch配对运用,并主要锁的顺序。它所做的任务,finish_lock_switch调用local_irq_enable(),获取prev的形状和rq-prev_mm,假设mm非空,那么调用mmdrop(mm)减少mm的援用计数,假设为0那么释放进程页表和虚拟空间。假
39、设prev_state为TASK_DEAD那么释放进程的task构造。struct mm_struct *mm = rq-prev_mm;long prev_state;rq-prev_mm = NULL;prev_state = prev-state;finish_arch_switch(prev);finish_lock_switch(rq, prev);if (mm)mmdrop(mm);if (unlikely(prev_state = TASK_DEAD) kprobe_flush_task(prev);put_task_struct(prev);13)最后,if (unlikely
40、(task-lock_depth = 0)那么重新获取大内核锁_reacquire_kernel_lock,否那么goto need_resched_nonpreemptible;允许抢占,假设TIF_NEED_RESCHED被设置,那么跳转到need_resched重新进展调度。prev = current;if (unlikely(reacquire_kernel_lock(prev) vm_ops提供了nopage()函数,那么用它填充数据;否那么调用do_anonymous_page()匿名函数来填充数据。假设被文件或设备映射,假设时文件映射,filemap_nopage()将替代no
41、page()函数,假设由虚拟文件映射而来,那么shmem_nopage()。每种设备驱动将提供不同的nopage()函数,该函数前往struct page构造。3恳求换页:将页面交换至后援存储器后,函数do_swap_page()担任将页面读入内存,将在后面讲述。经过PTE的信息就足够查找到交换的页面。页面交换出去时,普通先放到交换高速缓存中。缺页中断时假设页面在高速缓存中,那么只需简单添加页面计数,然后把它放到进程页表中并计数次缺页中断发生的次数。假设页面仅存在磁盘中,Linux将调用swapin_readahead()读取它及后续的假设干页面。4页面帧回收除了slab分配器,系统中一切正在
42、运用的页面都存放在页面高速缓存中,并由lru链接在一同。Slab页面不存放到高速缓存中由于基于被slab运用的对象对页面计数很困难。除了查找每个进程页表外没有其他方法能把struct page映射为PTE,查找页表代价很大。假设页面高速缓存中存在大量的进程映射页面,系统将会遍历进程页表,经过swap_out()函数交换出页面直到有足够的页面空闲,而共享页会给swap_out()带来问题。假设一个页面是共享的,同时一个交换项曾经被分配,PTE就会填写所需信息以便在交换分区里重新找到该页并将援用计数减1。只需援用计数为0时该页才干被交换出去。内存和磁盘缓存恳求越来越多的页面但确无法判别如何释放进程
43、页面,恳求分页机制在进程页面缺失时恳求新页面,但它却不能强迫释放进程不再运用的页面。The Page Frame Reclaiming Algorithm(PFRA)页面回收算法用于从用户进程和内核cache中回收页面放到同伴系统的空闲块列表中。PFRA必需在系统空闲内存到达某个最低限制时进展页面回收,回收的对象必需是非空闲页面。可将系统页面划分为四种:1)Unreclaimable不可回收的,包括空闲页面、保管页面设置了PG_reserved标志、内核动态分配的页面、进程内核栈的页面、设置了PG_locked标志的暂时锁住的页面、设置了VM_LOCKED标志的内存页面。2)Swappable
44、可交换的页面,用户进程空间的匿名页面(用户堆栈)、tmpfs文件系统的映射页面入IPC共享内存页面,页面存放到交换空间。3)Syncable可同步的页面,入用户态地址空间的映射页面、维护磁盘数据的页面缓存的页面、块设备缓冲页、磁盘缓存的页面入inode cache,假设有必要的话,需同步磁盘映像上的数据。4)Discardable可丢弃的页面,入内存缓存中的无用页面slab分配器中的页面、dentry cache的页面。PFRA算法是基于阅历而非实际的算法,它的设计原那么如下:1)首先释放无损坏的页面。进程不再援用的磁盘和内存缓存应该先于用户态地址空间的页面释放。2)标志一切进程态进程的页面为
45、可回收的。3)多进程共享页面的回收,要先去除援用该页面的进程页表项,然后再回收。4)回收“不在运用的页面。PFRA用LRU链表把进程划分为in-use和unused两种,PFRA仅回收unused形状的页面。Linux运用PTE中的Accessed比特位实现非严厉的LRU算法。页面回收通常在三种情况下执行:1)系统可用内存比较低时进展回收(通常发生在恳求内存失败)。2)内核进入suspend-to-disk形状时进展回收。3)周期性回收,内核线程周期性激活并在必要时进展页面回收。Low on memory回收有以下几种情形:1)_ _getblk( )调用的grow_buffers( )函数分
46、配新缓存页失败;2)create_empty_buffers( )调用的alloc_page_buffers( )函数为页面分配暂时的buffer head失败;3)_ _alloc_pages( )函数在给定内存区里分配一组延续的页面帧失败。周期性回收涉及的两种内核线程:1)Kswapd内核线程在内存区中检测空闲页面能否低于pages_high的门槛值;2)预定义任务队列中的事件内核线程,PFRA周期性调度该任务队列中的task回收slab分配器中一切空闲的slab;一切用户空间进程和页面缓存的页面被分为活动链表和非活动链表,统称LRU链表。每个区描画符中包括active_list和inac
47、tive_list两个链表分别将这些页面链接起来。nr_active和nr_inactive分别表示这两种页面数量,lru_lock用于同步。页描画符中的PG_lru用于标志一个页面能否属于LRU链表,PG_active用于标志页面能否属于活动链表,lru字段用于把LRU中的链表串起来。活动链表和非活动链表的页面根据最近的访问情况进展动态调整。PG_referenced标志就是此用途。处置LRU链表的函数有:add_page_to_active_list()、add_page_to_inactive_list()、activate_page()、lru_cache_add()、lru_cach
48、e_add_active()等,这些函数比较简单。shrink_active_list ( )用于将页表从活动链表移到非活动链表。该函数在shrink_zone()函数执行用户地址空间的页面回收时执行。5交换分区:系统可以有MAX_SWAPFILES的交换分区,每个分区可放在磁盘分区上或者普通文件里。每个交换区由一系列页槽组成。每个交换区有个swap_header构造描画交换区版本等信息。每个交换区有假设干个swap_extent组成,每个swap_extent是延续的物理区域。对于磁盘交换区只需一个swap_extent,对于文件交换区那么由多个swap_extent组成,由于文件并不是放在
49、延续的磁盘块上的。mkswap命令可以创建交换分区。图 交换分区构造图 交换页构造swp_type()和swp_offset()函数根据页槽索引和交换区号得到type和offset值,函数swp_entry(type,offset)得到交换槽。最后一位总是清0表示页不在RAM上。槽最大224(64G)。第一个可用槽索引为1。槽索引不能全为0。一个页面能够被多个进程共用,它能够被从一个进程地址空间换出但依然在物理内存上,因此一个页面能够被多次换出。但物理上仅第一次被换出并存储在交换区上,接下来的换出操作只是添加swap_map的援用计数。swap_duplicate(swp_entry_t en
50、try)的功能正是用户尝试换出一个曾经换出的页面。6交换缓存:多个进程同时换进一个共享匿名页时或者一个进程换入一个正被PFRA换出的页时存在竞争条件,引入交换缓存处理这种同步问题。经过PG_locked标志可以保证对一个页的并发的交换操作只作用在一个页面上,从而防止竞争条件。7.页面回收算法描画:以下图是各种情况下进展页面回收时的函数调用关系图。可以看出最终调用函数为cache_reap()、shrink_slab()和shrink_list()。cache_reap()用于周期性回收slab分配器中的无用slab。shrink_slab()用于回收磁盘缓存的页面。shrink_list()是
51、页面回收的中心函数,在最新代码中该函数名改为shrink_page_list()。下面会重点讲解。图中shrink_caches()最新函数名为shrink_zones()、shrink_cache()最新函数名为shrink_inactive_list()。其他函数不变。图PFRA函数构造调用关系低内存回收页面:如上图所示,当内存分配失败时,内核调用free_more_memory(),该函数首先调用wakeup_bdflush( )唤醒pdflush内核线程触发写操作,从磁盘页面缓冲中写1024个dirty页面到磁盘上以释放包含缓冲、缓冲头和VFS的数据构造所占用的页表;然后进展系统调用s
52、ched_yield( ),以使pdflush线程可以有时机运转;最后该函数循环遍历系统节点,对每个节点上的低内存区(ZONE_DMA和ZONE_NORMAL)调用try_to_free_pages( )函数。try_to_free_pages(struct zone *zones, gfp_t gfp_mask)函数的目的是经过循环调用shrink_slab()和shrink_zones()释放至少32个页帧,每次调用添加优先级参数,初始优先级是12,最高为0。假设循环13次,依然没有释放掉32个页面,那么PFRA进展内存异出维护:调用out_of_memory()函数选择一个进程回收它一切
53、的页面。shrink_zones(int priority, struct zone *zones,struct scan_control *sc)函数对zones列表中每个区调用shrink_zone()函数。调用shrink_zone()前,先用sc-priority的值更新zone描画符中的prev_priority,假设zone-all_unreclaimable字段不为0且优先级不是12,那么不对该区进展页面回收。shrink_zone(int priority, struct zone *zone, struct scan_control *sc)函数尝试回收32个页面。该函数循环
54、进展shrink_active_list()和shrink_inactive_list的操作以到达目的。该函数流程如下:1)atomic_inc(&zone-reclaim_in_progress)添加区的回收计数;2)添加zone-nr_scan_active,根据优先级,添加范围是zone-nr_active/212tozone-nr_active/20。假设zone-nr_scan_active = 32那么赋给nr_active变量,同时zone-nr_scan_active设为0,否那么nr_active0;3)zone-nr_scan_inactive和nr_inactive做同样
55、处置;4)假设nr_active和nr_inactive不同时为空,那么进展while循环进展5、6步操作:5)假设nr_active非0,那么从active链表挪动某些页面到inactive链表:nr_to_scan = min(nr_active,(unsigned long)sc-swap_cluster_max);nr_active -= nr_to_scan;shrink_active_list(nr_to_scan, zone, sc, priority);6)假设nr_inactive非0,那么回收inactive链表中的页面:nr_to_scan = min(nr_inacti
56、ve,(unsigned long)sc-swap_cluster_max);nr_inactive -= nr_to_scan;nr_reclaimed += shrink_inactive_list(nr_to_scan, zone, sc);7)atomic_dec(&zone-reclaim_in_progress)减小回收计数,并前往回收页面数nr_reclaimed;shrink_inactive_list(unsigned long max_scan, struct zone *zone, struct scan_control *sc)函数从区的inactive链表中抽取一组页
57、面,放到暂时链表中,调用shrink_page_list()对链表中的每个页面进展回收。下面是shrink_inactive_list()主要步骤:1)调用lru_add_drain()将当前CPU上pagevec构造的lru_add_pvecs和lru_add_active_pvecs中的页面分别移到活动链表和非活动链表中;2)获取LRU锁spin_lock_irq(&zone-lru_lock);3)最多扫描max_scan个页面,对每个页面添加运用计数,检查该页面能否正被释放到同伴系统中,将该页面挪动一个暂时链表中;4)从zone-nr_inactive中减去移到暂时链表中的页面数;5)
58、添加zone-pages_scanned计数;6)释放LRU锁:spin_unlock_irq(&zone-lru_lock);7)对暂时链表调用shrink_page_list(&page_list, sc)回收页面;8)添加nr_reclaimed计数;9)获取LRU锁spin_lock(&zone-lru_lock);10)将shrink_page_list(&page_list, sc)没有回收掉的页面重新添加到active链表和inactive链表中。该函数在回收过程中能够会设置PG_active标志,所以也要思索往active链表中添加。11)假设扫描页面数nr_scanned小于
59、max_scan那么循环进展310的操作;12)前往回收的页面数;shrink_page_list(struct list_head *page_list, struct scan_control *sc)做真正的页面回收任务,该函数流程如下:图shrink_page_list()页面回收逻辑处置流程1)调用cond_resched()进展条件调度;2)循环遍历page_list中每个页面,从列表中移出该页面描画符并回收该页面,假设回收失败,那么把该页面插入一个部分链表中;该步流程参见流程图。l调用cond_resched()进展条件调度;l从LRU链表中取出第一个页面并从LRU链表中删除;l
60、假设页面被锁定,这调过该页面,该页加到暂时链表中;l假设页面不能部分回收并且页面是进程页表的映射,这跳过该页;l假设进程是回写的dirty页面,那么跳过;l假设页面被援用并且页面映射在运用,这跳过并激活该页面,以便后面放入active列表;l假设是匿名页面且不在交换区中,这调用add_to_swap()为该页面分配交换区空间并把该页加到交换缓存中;l假设页面是进程空间映射并且页面映射地址非空,那么调用try_to_unmap()移除该页面的页表映射;l假设页面为dirty页面并且无援用、交换可写、且是fs文件系统映射,调用pageout()写出该页面。3)循环终了,把部分链表中的页面移回到pa
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 交通行业话务员工作总结
- 娱乐休闲行业行政后勤工作总结
- 评估自我能力的有效方法计划
- 礼品饰品销售员工作总结
- 中班游戏活动小青蛙跳荷叶
- 家具家居行业会计工作总结
- 幼儿园小班教案《挑战天空的小猫》及教学反思
- 小学安全工作总结汇编六篇
- 家长如何教育小孩子
- 咖啡厅卫生整治规范
- 2023年中国软件行业基准数据SSM-BK-202310
- 华为管理法读后感
- 2024年中考英语复习:阅读七选五 专项练习题汇编(含答案解析)
- 南京财经大学国际服务贸易(考试重点)
- 玻璃生产工艺及装备培训
- 2024年国投信托有限公司招聘笔试参考题库含答案解析
- 自身免疫性肝炎的护理查房
- 《卜算子-黄州定慧院寓居作》理解性默写(含答案)
- 部编版语文五年级上册第一单元大单元教学设计
- 肝脓肿健康宣教
- 少先队大队辅导员胜任力模型研究
评论
0/150
提交评论