sched.c分析
kernel/里面一个非常重要的角色,就是它。所有进程运行都在它的管控之内,因此学习好这个.c事关重大.
功能描述
sched.c 是内核中有关进程调度管理的程序,其中有关调度的基本函数(sleep_on() , wakeup() ,schedule() 函数等) ,其中比较重要的一个函数是schedule()函数,
该函数负责选择系统中,下一个将要运行的进程,它首先对所有的任务进行选择,唤醒任何一个已经得到信号的任务。
具体方法是针对任务数组中的每个任务,检查其报警定时值alarm。如果任务的alarm时间已经过期(alarm < jiffies), 则在它的信号位图中设置SIGALRM信号,
然后清alarm的值,jiffies是系统是从开机开始算起的滴答数,如果进程的信号位图中除被阻塞的的信号外还有其他的信号,并且读进程处于可中断睡眠状态,
则置进程为就绪状态。
随后是调度函数的核心处理部分,这部分代码根据进程的时间片和优先权调度机制,来选择随后要执行的任务。它首先循环检查任务数组中的所有任务,根据每个就绪态任务剩余执行时间的值counter,来选取该值最大的一个任务,并利用switch_to()函数切换到该任务。若所有就绪态任务该值都为0,表示此刻所有任务的时间片都
已经运行完毕,于是就根据任务的优先权值priority,重置每个任务的运行时间片值counter,再重新执行循环检查所有任务的执行时间片值。
sleep_on()函数的主要功能是当一个进程所请求的资源正忙或不在内存中时暂时切换出去,放在等待队列中等待一段时间, 当切换回来之后再继续运行,放入等待队列的方式利用了函数中的tmp指针作为各个正在等待任务的联系。
源码分析
一、变量(define)
1. _BLOCKABLE:它将SIGKILL和SIGSTOP信号或起来在取反,看起来是要屏蔽这两个信号,看看,它只在schedule函数中使用了,表示同意带有这两个信号的可中断进程被置为就绪态。这个应用其实我还是有些印象,比如应用程序无法手动屏蔽这两个信号,这两个信号是一定会被触发的,导致的结果就是进程退出。
2. LATCH:抓住,占有的意思。在shced_init中被使用了一次,第一个是将(LATCH & 0xff)这个值写入0x40端口,0x40属于8253定时器的端口寻址范围,第一个值是写的是低字节,第二个值(LATCH >> 8)写的是高字节,也就是设定8253定时器的计数值,即定时器要响应LATCH次才发出一次时钟中断,而定时器的芯片的输入频率是1.193180MHz,那么这里我们做个算术题,计算1s发生一次时钟中断的LATCH值是多少?(哈哈),不要迷糊,其实很简单,就是时钟芯片的频率1193180,紧着着我们就可以轻松算得n秒发生一次时钟中断的LATCH值为1193180*n,如果10ms呢?n=0.01s,也即11931.8,如果换算成n Hz,LATCH=1193180/n。
3. TIME_REQUESTS:time list数组的大小。这里设为64。
二、变量(static)
1. static union task_union init_task:联合体就是一个page的内存,保存着第一个进程结构体内容,在sched.h中已经将它初始化好了,我们在来体会一下这个最初的生命体。
1)long state:0,表示就绪态。
2)long counter:15,时间片滴答数。
3)long priority:15,优先级值。
4)long signal:0,还没有任何信号存在。
5)struct sigaction sigaction[32]:{{},},空。
6)long blocked:0,不屏蔽任何信号。
7)long exit_code:0。
8)long start_code,end_code,end_data,brk,start_stack:0,0,0,0,0,对于进程0用不到。
13)long pid:0,初始进程。
14)long pgrp,session,leader:0,0,0,初始化为0,其实也只有0号进程。
17)int groups[32]:{-1,},暂时还没有。
18)struct task_struct *p_pptr,*p_cptr,*p_ysptr,*p_osptr:&init_task.task,0,0,0:,它将父进程指向了自己,其他的都还没有。
22)unsigned short uid,euid,suid,gid,egid,sgid:0,0,0,0,0,0,都是0号进程。
28)unsigned long timeout:0,没有启动。
29)unsigned long alarm:0,没有启动。
30)long utime,stime,cutime,cstime,start_time:0,0,0,0,0,都没有需要。
35)struct rlimit rlim[6]:{{0x7fffffff,0x7fffffff},...},都赋值为最大值。
36)unsigned int flags:0。
37)unsigned short used_math:0,没有使用数学协处理器。
38)int tty:-1,没有对应的tty设备。
39)unsigned short umask:0022,八进制,用户进程不可读不可写不可执行,同一用户组进程不可读可写不可执行,其他用户不可读可写不可执行。怎么感觉怪怪的!
40)struct m_inode *pwd,*root,*executable,*library:NULL,NULL,NULL,NULL,是的,都没有。
44)unsigned long close_on_exec:0,木有。
45)struct file *filp[20]:{NULL,},木有。
46)struct desc_struct ldt[3]:{{0,0},{0x9f,0xc0fa00},{0x9f,0xc0f200}},嘿嘿,这个是重点,这些可是进程0的段描述符哦,非常重要。ldt[0]没有,ldt[1]是代码段,ldt[2]是数据段,我们拆开看看:
1-段限长:0xc0fa00[19:16]+0x9f[15:0]=0x9f,+1,0xa0,x4Kb,640Kb。
2-基地址:0xc0fa00[31:24][7:0]+0x9f[31:16]=0x0。
3-TYPE:类型,0xc0fa00[11:8]=0xa,b1100,代码,一致性,仅执行。
0xc0f200[11:8]=0x2,b0010,数据,可读写。
4-DPL:特权级,0xc0fa00[14:13]=0x3,ring 3环,表示用户态。
5-P:存在位,0xc0fa00[15:15]=0x1,存在。
6-AVL:软件可用位,0xc0fa00[20:20]=0x0。
7-B:默认操作大小,0xc0fa00[22:22]=0x1,32位地址。
8-G:颗粒度标志,0xc0fa00[23:23]=0x1,4Kb。
47)struct tss_struct tss:
1> long back_link:0。
2> long esp0,ss0:PAGE_SIZE+(long)&init_task,0x10。
4> long esp1,ss1,esp2,ss2:0,0,0,0。
8> long cr3:(long)&pg_dir。
9> long eip:0,到时候会通过iret重设的。
10> long eflags:0,到时候会通过iret重设的。
11> long eax,ecx,edx,ebx:0,0,0,0,用不到。
15> long esp:0,到时候会通过iret重设的。
16> long ebp:0,用不到。
17> long esi,edi:0,0,用不到。
18> long es,cs,ss,ds,fs,gs:0x17,0x17,0x17,0x17,0x17,0x17,都要用数据段即可记得这个是用户态,因此得用LDT,bit2=1。
24> long ldt:_LDT(0),任务0的LDT选择子。
25> long trace_bitmap:0x80000000,?。
26> struct i387_struct i387:{},到时候任务切换的时候会将该内容填满。
2. static struct task_struct *wait_motor[4]:
3. static int mon_timer[4]:
4. static int moff_timer[4]:
5. static unsigned char current_DOR:[color=DimGray]以上2~5都是关于floppy driver的,之所以放到了sched.c里面,是因为需要用到一些关于timer的处理的,这里写是最近的[/color]。
6. [b]static struct timer_list timer_list[64],*next_timer[/b]:这两个变量也是用于floppy的,可是他们已经脱离driver,组成了一个实现定时队列的机制。下面我们详细分析一下这个变异的算法。
1)struct timer_list:用于实现定时队列的结构体。
1> long jiffies:最重要的成员,用于实现优先执行序列。
2> void (*fn)():执行的无参数函数指针。
3> struct timer_list *next:用于链表。
2)add_timer:添加定时队列成员。
3)do_timer:检查执行定时函数。
第一种方法:我们可以利用插入排序来实现[color=Green]优先队列[/color],时间复杂度为o(n),这样jiffies就表示闹钟时间,这样我们只需每次弹出队列头来和当前时间比较即可,小于等于当前时间就需要执行它。
第二种方法:作者采用了一种稍微复杂的做法来实现优先队列,时间复杂度仍是o(n),其实也是插入排序的思想,不过变量jiffies有了另外的意思,剩余的时间滴答数。我们需要分析出进行插入的情况,假设优先队列的jiffies为x0,x1,x2,..xi,..xn,那么我们就可以计算出每个成员执行的时间点,y0,y1,y2,..yi,..yn,这里yi=y(i-1)+xi,i>=1,i=0,y0=x0,即yi=x0+x1+x2+...+xi,假设现在有一个jiffies为yq的成员想插入优先队列中,yq的时间点刚好i-1 xi'=y(i-1)-yq+xi=xi-xq,而xq我们已经计算出来了,可以轻松得到xi'这个新的xi,这样我们就完成了yq的插入。如果队列头为0,说明时间到了可以弹出成员执行其函数。
作者写的这个方法是有问题的,少考虑一种情况,已经修改了原来的方案(自add_timer/sched.c):
while (p->next) { if (p->jiffies <= p->next->jiffies) { p->next->jiffies -= p->jiffies; break; } p->jiffies -= p->next->jiffies; fn = p->fn; p->fn = p->next->fn; p->next->fn = fn; jiffies = p->jiffies; p->jiffies = p->next->jiffies; p->next->jiffies = jiffies; p = p->next; }
三、变量(global)
1. unsigned long volatile jiffies:0,时钟滴答,系统设定每隔10ms发生一次。
2. unsigned long startup_time,0,系统启动时间,单位是秒。
3. int jiffies_offset,0,主要是用于时间设定相关的。
4. struct task_struct *current:&(init_task.task),当前运行进程。
5. struct task_struct *last_task_used_math:NULL,数学协处理器,这个有专门的模块负责。
6. struct task_struct *task[64]:{&(init_task.task),},任务指针数组。
7. long user_stack[PAGE_SIZE>>2]:任务0的用户堆栈,1Kb,在系统启动跳入保护模式后使用的内核堆栈是同一个。
8. struct {long *a,short b;} stack_start:{&user_stack[PAGE_SIZE>>2],0x10},系统进入任务0前使用的内核堆栈。
四、函数
1. show_task:打印一些任务结构体中的成员值。
2. show_state:打印所有任务的信息。
3. math_state_restore:在device_not_available(sys_call.s)中被call,存储协处理器的状态。
4. schedule:进程调度。
5. __sleep_on:睡眠调度。
6. interruptible_sleep_on:可中断睡眠。
7. sleep_on:不可中断睡眠。
8. wake_up:唤醒。
9. ticks_to_floppy_on:
10. floppy_on:
11. floppy_off:
12. do_floppy_timer:9~12这4个函数是floppy driver的,且不谈。
13. add_timer:也是floppy driver的,已经分析了。
14. do_timer:时钟中断C语言区,在timer_interrupt(sys_call.s)中被call。
15. sched_init:进程调度模块初始化。
五、系统调用
sys_pause,sys_alarm,sys_getpid,sys_getppid,sys_getuid,sys_geteuid,sys_getgid,sys_getegid,sys_nice。
=> sched_init:
进程调度模块初始化函数。主要是任务0的设定,包括tss和ldt,初始化task数组,清NT以免造成麻烦,设定时钟响应频率,设定时钟中断门和系统调用门,开启时钟中断,开启后就可以认为任务0在内核态已经开始运行了,不过此时用的栈还是任务0的用户态栈。
=> schedule:
进程调度,可以从当前进程切换到其他进程去。该函数可以分为3段说,第一段将超时的、定时的、有信号的可中断进程都切换到就绪态,第二段调度算法,找到下一个合适运行的进程,第三段开始切换。我们重点分析第二段和第三段,调度算法个人总结了几点:1)只调度就绪态进程,2)选择剩余时间片更多的进程,3)如果没有就绪态进程,选择0号进程,4)就绪态进程时间片都用完,重新分配时间片,这里包括了非就绪态进程,5)非就绪态进程变为就绪态的时间越晚,被分配的时间片就越多。切换进程其实只有一句ljmp长跳转,运用一个TSS选择子即可找到TSS的地址位置,找到后就可以开始自动的任务切换了,把当前进程的所有register都保存到当前的TSS段中,记得此时的eip已经指向了下一条指令,然后把新任务的TSS段中的所有保存的register覆盖CPU register,开始运行新任务。
=> __sleep_on:
这个函数在0.11上写了两套,还有网友出贴分析了其中的bug,在0.12中,作者改进了睡眠队列的运作情况,保证了同一时刻一个资源只会存在一个睡眠队列。其实0.11的思想也应该是想这样的,不过可能code写的时候没有考虑完全导致出现的bug。保证一个睡眠队列的原因就是“*p = tmp”这点code,这就保证了资源队列头不会游离出来,于是条件“*p && *p != current”的判断也就起到了正确的效果,这个条件可以卡主不是队列头的进程(可中断进程)却先执行起来了,那么我们直接把它设为不可中断进程,然后继续调度即可,这都是为了保证队列的步伐整齐一致。
=> interruptible_sleep_on:
可中断的睡眠,这样的话,进程就可以因为超时、定时和信号而变为就绪态得到执行权。
=> sleep_on:
直接睡去,除非wake up主动唤醒。
=> wake_up:
唤醒进程。
=> do_timer:
我们先来看看它的前奏,timer_interrupt(sys_call.s),堆栈情况(貌似我很喜欢这个),进入前的堆栈:old ss,old esp,eflags,old cs,old eip,进入do_timer前的堆栈:old ss,old esp,eflags,old cs,old eip,ds,es,fs,-1,edx,ecx,ebx,eax,CPL。进入内核前都有几件必须做的事情,像ds,es这些数据段都需设为0x10,而fs数据段用作读取用户态数据,因此设为0x17。中断的话,要中断复位。do_timer完成后再返回去执行信号的东西,这个中断比较特殊一点。
下面在来看看do_timer函数做了些什么,第一部分和控制台相关,第二部分和harddisk driver相关,第三部分也和控制台相关,第四部分计算本进程的内核态用户态运行滴答数,第五部分和floppy driver相关,最后将本进程的时间片递减一次,若小于等于0了,那么可以调度其他进程运行了,然后在判断如果本进程是从内核态过来的就不能被调度即抢占,否则开始调度。
#include #include #include #include #include #include #include #include //读宏取信号nr在信号位图中对应位的二进制数值,信号编号1-32.比如信号5的位图就是1<<(5-1),等于0010000b #define _S(nr) (1<<((nr)-1)) //定义阻塞信号位图 #define _BLOCKABLE(~(_S(SIGKILL) | _S(SIGSTOP))) //内核调试函数。显示任务号nr的进程号,进程状态,和内核堆栈空闲字节数 void show_task(int nr , struct task_struct * p) { int i , j = 4096 - sizeof(struct task_struct) ; printk("%d: pid= %d , state=%d," , nr, p->pid , p->state) ; i = 0 ; while(i < j && !((char *)(p + 1))[i]) //检测指定任务数据结构以后等于0的字节数 i++ ; printk("%d(of%d) chars free in kernel stack \n\r" , i , j) ; } //显示所有任务的任务号,进程号,进程状态和内核堆栈空闲字节数 void show_stat(void) { int i ; for(i = 0 ; i < NR_TASKS ; i++) if(task[i]) show_task(i , task[i]) ; } ///设置8253芯片初值 #define LATCH(1193180/HZ) extern void mem_use(void) ; extern int timer_interrupt(void) ; extern int system_call(void) ; //每个任务在内核态运行时,都会有自己的内核态堆栈,这里定义了任务的内核态堆栈结构 union task_union{ struct task_struct_task ; //因为一个任务的数据结构与其内核态堆栈结构在同一个内存页中 char stack[PAGE_SIZE] ; //所以从堆栈段寄存器ss可以获得其数据段选择符 } ; static union task_union init_task = {INIT_TASK , } ; long volatile jiffies = 0 ; //volatile 表示要从内存取值,因为CPU会把经常使用的变量放在 //通用寄存器中,但是若其他的程序修改这些变量之后,寄存器中的值,可能 //并不发生变化,这就造成了脏数据。 使用volatile 表示每次取值,都会从内存取值 long start_time = 0 ; struct task_struct * current = &(init_task.task) ; //当前任务指针,默认指向任务0 struct task_struct * last_task_uesd_math = NULL ; //使用协处理器的任务指针 struct task_struct * task[NR_TASKS] = {&(init_task.task) , } ; //定义任务指针数组 long user_stack[PAGE_SIZE >> 2] ; struct { long * a ; short b ; } stack_start = {&user_stack[PAGE_SIZE >> 2] , 0x10} ; void math_state_restore() { //如果任务没变,则返回 if(last_task_used_math == current) return ; _asm_("fwait") ; if(last_task_used_math) { _asm_("fnsave %0" :: "m"(last_task_used_math->tss.i387)) ; } last_task_used_math = current ; if(current->uesd_math){ //已经使用过协处理器 _asm_("frstor %0"::"m"(current->tss.i387)) ; } else{ //第一次使用协处理器,需要初始化相关信息 _asm_("fninit"::) ; current->used_math = 1 ;//设置已经使用过协处理器 } } void schedule(void) { int i , next , c ; struct task_struct **p ;//任务结构指针的指针 for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) if(*p){ //如果设置国任务的定时值alarm,并且已经过期(alarmalarm && (*p)->alarm < jiffies) { (*p)->signal |= (1<<(SIGALRM - 1)) ; (*P)->alarm = 0 ; } //如果信号位图中除被阻塞的信号外还有其他的信号,并且任务处于可中断状态,则置任务为就绪状态。 if(((*p)->signal & ~(_BLOCKABLE&(*p)->blocked))&&(*p)->state == TASK_INTERRUPTIBLE) (*p)->state - TASK_RUNNING ; //置为就绪状态 } //这是调度程序的主要部分 while(1) { c = -1 ; next = 0 ; i = NR_TASKS ; p = &task[NR_TASKS] ; //这段代码也是从任务数组的最后一个任务开始循环处理,并跳过不含任务的数组槽 //比较每个就绪状态任务的counter(任务运行时间的递减滴答数),哪个值最大,运行时间还不长, //next就指向哪个任务号 while(--i){ if(!*--p) continue ; if((*p)->state == TASK_RUNNING && (*p)->counter > c) c = (*p)->counter, next = i ; } //如果比较得出有counter值不等于0的结果,或者系统中没有一个可以运行的程序,那么就跳出最上层的 //while循环,执行任务切换程序 if(c != 0 ) break ; //全部的任务的时间片都已经使用完毕,那么就需要重新设置时间片值,重新执行。 // counter值最大的先执行 for(p = &LAST_TASK ; p > &FIRST_TASK ;p++) if(*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority ; } switch_to(next) ; //切换到任务号为next的任务,并运行之 } /* 线程中断系统调用 */ int sys_pause(void) { current->state = TASK_INTERRUPTIBLE ; schedule() ; return 0 ; } //把任务变为不可中断的等待状态,并让睡眠队列的头指针指向当前的任务 //只有明确的唤醒,才会返回,该函数提供了进程与中断处理程序之间的同步机制 //函数参数P是等待任务队列的头指针,为了修改调用该函数程序中原来的指针变量的值, //就需要提供(*p)指针的指针。 void sleep_on(struct task_struct **p) //等待任务队列的头指针 { struct task_struct * tmp ; if(!p) return ; //如果进程0将要休眠,则死机 if(current == &(init_task.task)) panic("task[0] trying to sleep"); //让tmp指向已经在等待队列之上的任务,并且将等待队列头的等待指针指向当前任务 //这样就把当前任务插入到了*p的等待队列中。然后将当前任务变为不可中断的等待状态,并执行重新调度 tmp = *p ; *p = current ; current->state = TASK_UNINTERRUPTIBLE ; schedule() ; if(tmp) //若在其前还有等待任务,则将其变为就绪状态 tmp->state = 0 ; } //将当前的任务置为可中断的等待状态,并放入*p指定的等待队列中 void interruptible_sleep_on(struct task_struct ** p) { struct task_struct * tmp ; if(!p) return ; if(current == &(init_task.task)) panic("task[0] trying to sleep") ; tmp = *p ; *p = current ; repeat: current->state = TASK_INTERRUPTIBLE ; schedule() ;//执行调度程序,切换任务 //只有当这个等待程序被唤醒的时候,程序才会又回到这里执行,表示进程已被明确地唤醒并执行。 if(*p && *p!= current){ (**p).state = 0 ; goto repeat ; } *p = NULL ; if(tmp) tmp->state = 0 ; } /*唤醒等待的任务*/ void wake_up(struct task_struct **p) { if(p && *p) { (**p).state = 0 ;//置为就绪状态 *p = NULL ; } } //下列进程数组存放的是软驱马达启动到正常转数的进程指针,数组索引0-3对应软驱A-D static struct task_struct * wait_motor[4] = {NULL , NULL , NULL , NULL} ; //每个马达启动所需要的时间数 static int mon_timer[4] = {0 , 0 , 0 ,0} ; //记录每个马达停转之前维持的时间 static int moff_timer[4] = {0,0,0,0} ; unsigned char current_DOR = 0x0C ;//这里设置初值,允许DMA和中断请求。启动FDC //指定软驱启动到正常运转状态所需要的等待时间 int ticks_to_floppy_on(unsigned int nr) { extern unsigned char selected ; unsigned char mask = 0x10 << nr ; if(nr > 3) panic("floppy_on : nr > 3") ; moff_timer[nr] = 10000 ; cli() ; mask |= current_DOR ; if(!selected){ mask &= 0xFC ; mask |= nr ; } if(mask != current_DOR) { outb(mask , FD_DOR) ; if((mask ^ current_DOR) & 0xf0) mon_timer[nr] = HZ / 2 ; else if(mon_timer[nr] < 2) mon_timer[nr] = 2 ; current_DOR = mask ; } sti() ; return mon_timer[nr] ; } void floppy_on(unsigned int nr) { cli() ; while(ticks_to_floppy_on(nr)) sleep_on(nr + wait_motor) ; sti() ; } void floppy_off(unsigned int nr) { moff_timer[nr] = 3 * HZ ; } /*软盘定时处理子程序*/ /*更新马达启动定时值和马达关闭停转定时值*/ /*系统每当经过一个滴答就会被调用一次,随时更新马达开启或者停转的时间*/ void do_floppy_timer(void) { int i ; unsigned char mask = 0x10 ; for(i = 0 ; i < 4 ; i ++ , mask <<= 1) { if(!(mask & current_DOR)) continue ; if(mon_timer[i]){ if(!--mon_timer[i]) wake_up(i + wait_motor) ; } else if(!moff_timer[i]) { current_DOR &= ~mask ; outb(current_DOR , FD_DOR) ; } else moff_timer[i]-- ; } } #define TIME_REQUEST 64 //下面是定时器的代码。最多可以有64个计时器 static struct timer_list { long jiffies ; //定时滴答数 void(* fn)() ; //定时处理程序 struct timer_list * next ; //链接指向下一个定时器 } timer_list[TIME_REQUEST] , * next_timer = NULL ; /*添加一个新的定时器*/ void add_timer(long jiffies , void(*fn)(void)) { struct timer_list * p ; if(!fn) return ; cli() ; if(jiffies <= 0)//如果定时值为0,则立即执行处理程序,并且该定时器不加入链表 (fn)() ; //否则从定时器数组中,找出一个空的项 else{ for(p = timer_list ; p < timer_list + TIMER_REQUEST ; p++) if(!p->fn) //找到一个空项,然后退出 break ; //如果已经用完了定时器数组,则系统崩溃,否则向定时器的数据结构中填入相应的信息,并连入链表头 if(p >= timer_list + TIME_REQUEST) panic("no more time request free"); p->fn = fn ; p->jiffies = jiffies ; p->next = next_timer ; next_timer = p ; //下面的函数其实是一个 双向队列 while(p->next && p->next->jiffies < p->jiffies){ p->jiffies -= p->next->jiffies ; fn = p->fn ; p->fn = p->next->fn ; p->next->fn = fn ; jiffies = p->jiffies ; p->next->jiffies = jiffies ; p = p->next ; } } } void do_timer(long cpl) { extern int beepcount ; //扬声器发生时间滴答数 extern void sysbeepstop (void) ; //关闭扬声器 if(beepcount) if(!--beppcount) sysbeepstop() ; if(cpl) current->utime++ ; else current->stime++ ; if(next_timer){ next_timer->jiffies-- ; while(next_timer && next_timer->jiffies <= 0){ void(*fn)(void) ; fn = next_timer->fn ; next_timer->fn = NULL ; next_timer = next_timer->next ; (fn)() ; } } if(current_DOR & 0xf0) do_floppy_timer() ; if((--current->counter) > 0) return ; current->counter = 0; if(!cpl) return ; schedule() ; } //系统调用功能,设置报警定时时间值 int sys_alarm(long seconds) { int old = current->alarm ; if(old) old = (old - jiffies) / HZ ; current->alarm = (seconds > 0 ) ? (jiffies + HZ * seconds):0 ; return old ; } //取当前的进程号pid int sys_getpid(void) { return current->pid ; } //取父进程号ppid int sys_getppid(void) { return current->father ; } //取用户号 int sys_getuid(void) { return current->uid ; } //取有效的用户号euid int sys_geteuid(void) { return current->euid ; } //取组号gid int sys_getgid(void) { return current->gid ; } //取有效的组号 int sys_getegid(void) { return current->egid ; } //系统调用功能---降低对CPU的优先使用权 int sys_nice(long increment) { if(current->priority - increment > 0) current->priority -= increment ; return 0 ; } //内核调度程序初始化 void sched_init(void) { int i ; struct desc_struct * p ; //描述符表结构指针 set_tss_desc(gdt + FIRST_TSS_ENTRY , &(init_task.tss)) ; set_ldt_desc(gdt + FIRST_LDT_ENTRY , &(init_task.task.ldt)); p = gdt + 2 + FIRST_TSS_ENTRY ; for(i = 1 ; i < NR_TASKS ;i++) { task[i] = NULL ; p->a = p->b = 0 ; p++ ; p->a = p->b = 0 ; p++ ; } _asm_("pushfl ; andl $0xffffbfff , (%esp) ; popfl" ) ; ltr(0) ; lldt(0) ; outb_p(0x36 , 0x43) ; outb_p(LATCH & 0xff , 0x40) ; outb(LATCH >> 8 , 0x40) ; set_intr_gate(0x20 , &timer_interrupt) ; outb(inb_p(0x21)&~0x01 , 0x21) ; set_system_gate(0x80 , &system_call) ; }
本文来源 我爱IT技术网 http://www.52ij.com/jishu/4413.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
