📄 sched.c
字号:
// 指定软盘到正常运转状态所需延迟滴答数(时间)。// nr -- 软驱号(0-3),返回值为滴答数。intticks_to_floppy_on (unsigned int nr){ extern unsigned char selected; // 当前选中的软盘号(kernel/blk_drv/floppy.c,122)。 unsigned char mask = 0x10 << nr; // 所选软驱对应数字输出寄存器中启动马达比特位。 if (nr > 3) panic ("floppy_on: nr>3"); // 最多4 个软驱。 moff_timer[nr] = 10000; /* 100 s = very big :-) */ cli (); /* use floppy_off to turn it off */ mask |= current_DOR; // 如果不是当前软驱,则首先复位其它软驱的选择位,然后置对应软驱选择位。 if (!selected) { mask &= 0xFC; mask |= nr; } // 如果数字输出寄存器的当前值与要求的值不同,则向FDC 数字输出端口输出新值(mask)。并且如果 // 要求启动的马达还没有启动,则置相应软驱的马达启动定时器值(HZ/2 = 0.5 秒或50 个滴答)。 // 此后更新当前数字输出寄存器值current_DOR。 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];}// 等待指定软驱马达启动所需时间。voidfloppy_on (unsigned int nr){ cli (); // 关中断。 while (ticks_to_floppy_on (nr)) // 如果马达启动定时还没到,就一直把当前进程置 sleep_on (nr + wait_motor); // 为不可中断睡眠状态并放入等待马达运行的队列中。 sti (); // 开中断。}// 置关闭相应软驱马达停转定时器(3 秒)。voidfloppy_off (unsigned int nr){ moff_timer[nr] = 3 * HZ;}// 软盘定时处理子程序。更新马达启动定时值和马达关闭停转计时值。该子程序是在时钟定时// 中断中被调用,因此每一个滴答(10ms)被调用一次,更新马达开启或停转定时器的值。如果某// 一个马达停转定时到,则将数字输出寄存器马达启动位复位。voiddo_floppy_timer (void){ int i; unsigned char mask = 0x10; for (i = 0; i < 4; i++, mask <<= 1) { if (!(mask & current_DOR)) // 如果不是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_REQUESTS 64 // 最多可有64 个定时器链表(64 个任务)。// 定时器链表结构和定时器数组。static struct timer_list{ long jiffies; // 定时滴答数。 void (*fn) (); // 定时处理程序。 struct timer_list *next; // 下一个定时器。}timer_list[TIME_REQUESTS], *next_timer = NULL;// 添加定时器。输入参数为指定的定时值(滴答数)和相应的处理程序指针。// jiffies – 以10 毫秒计的滴答数;*fn()- 定时时间到时执行的函数。voidadd_timer (long jiffies, void (*fn) (void)){ struct timer_list *p; // 如果定时处理程序指针为空,则退出。 if (!fn) return; cli (); // 如果定时值<=0,则立刻调用其处理程序。并且该定时器不加入链表中。 if (jiffies <= 0) (fn) (); else { // 从定时器数组中,找一个空闲项。 for (p = timer_list; p < timer_list + TIME_REQUESTS; p++) if (!p->fn) break; // 如果已经用完了定时器数组,则系统崩溃?。 if (p >= timer_list + TIME_REQUESTS) panic ("No more time requests free"); // 向定时器数据结构填入相应信息。并链入链表头 p->fn = fn; p->jiffies = jiffies; p->next = next_timer; next_timer = p; // 链表项按定时值从小到大排序。在排序时减去排在前面需要的滴答数,这样在处理定时器时只要 // 查看链表头的第一项的定时是否到期即可。[[?? 这段程序好象没有考虑周全。如果新插入的定时 // 器值 < 原来头一个定时器值时,也应该将所有后面的定时值均减去新的第1 个的定时值。]] 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->jiffies = p->next->jiffies; p->next->jiffies = jiffies; p = p->next; } } sti ();}//// 时钟中断C 函数处理程序,在kernel/system_call.s 中的_timer_interrupt(176 行)被调用。// 参数cpl 是当前特权级0 或3,0 表示内核代码在执行。// 对于一个进程由于执行时间片用完时,则进行任务切换。并执行一个计时更新工作。voiddo_timer (long cpl){ extern int beepcount; // 扬声器发声时间滴答数(kernel/chr_drv/console.c,697) extern void sysbeepstop (void); // 关闭扬声器(kernel/chr_drv/console.c,691) // 如果发声计数次数到,则关闭发声。(向0x61 口发送命令,复位位0 和1。位0 控制8253 // 计数器2 的工作,位1 控制扬声器)。 if (beepcount) if (!--beepcount) sysbeepstop (); // 如果当前特权级(cpl)为0(最高,表示是内核程序在工作),则将超级用户运行时间stime 递增; // 如果cpl > 0,则表示是一般用户程序在工作,增加utime。 if (cpl) current->utime++; else current->stime++; // 如果有用户的定时器存在,则将链表第1 个定时器的值减1。如果已等于0,则调用相应的处理 // 程序,并将该处理程序指针置为空。然后去掉该项定时器。 if (next_timer) { // next_timer 是定时器链表的头指针(见270 行)。 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) (); // 调用处理函数。 } } // 如果当前软盘控制器FDC 的数字输出寄存器中马达启动位有置位的,则执行软盘定时程序(245 行)。 if (current_DOR & 0xf0) do_floppy_timer (); if ((--current->counter) > 0) return; // 如果进程运行时间还没完,则退出。 current->counter = 0; if (!cpl) return; // 对于超级用户程序,不依赖counter 值进行调度。 schedule ();}// 系统调用功能 - 设置报警定时时间值(秒)。// 如果已经设置过alarm 值,则返回旧值,否则返回0。intsys_alarm (long seconds){ int old = current->alarm; if (old) old = (old - jiffies) / HZ; current->alarm = (seconds > 0) ? (jiffies + HZ * seconds) : 0; return (old);}// 取当前进程号pid。intsys_getpid (void){ return current->pid;}// 取父进程号ppid。intsys_getppid (void){ return current->father;}// 取用户号uid。intsys_getuid (void){ return current->uid;}// 取euid。intsys_geteuid (void){ return current->euid;}// 取组号gid。intsys_getgid (void){ return current->gid;}// 取egid。intsys_getegid (void){ return current->egid;}// 系统调用功能 -- 降低对CPU 的使用优先权(有人会用吗??)。// 应该限制increment 大于0,否则的话,可使优先权增大!!intsys_nice (long increment){ if (current->priority - increment > 0) current->priority -= increment; return 0;}// 调度程序的初始化子程序。voidsched_init (void){ int i; struct desc_struct *p; // 描述符表结构指针。 if (sizeof (struct sigaction) != 16) // sigaction 是存放有关信号状态的结构。 panic ("Struct sigaction MUST be 16 bytes"); // 设置初始任务(任务0)的任务状态段描述符和局部数据表描述符(include/asm/system.h,65)。 set_tss_desc (gdt + FIRST_TSS_ENTRY, &(init_task.task.tss)); set_ldt_desc (gdt + FIRST_LDT_ENTRY, &(init_task.task.ldt)); // 清任务数组和描述符表项(注意i=1 开始,所以初始任务的描述符还在)。 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++; } /* Clear NT, so that we won't have troubles with that later on */ /* 清除标志寄存器中的位NT,这样以后就不会有麻烦 */ // NT 标志用于控制程序的递归调用(Nested Task)。当NT 置位时,那么当前中断任务执行 // iret 指令时就会引起任务切换。NT 指出TSS 中的back_link 字段是否有效。 __asm__ ("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); // 复位NT 标志。 ltr (0); // 将任务0 的TSS 加载到任务寄存器tr。 lldt (0); // 将局部描述符表加载到局部描述符表寄存器。 // 注意!!是将GDT 中相应LDT 描述符的选择符加载到ldtr。只明确加载这一次,以后新任务 // LDT 的加载,是CPU 根据TSS 中的LDT 项自动加载。 // 下面代码用于初始化8253 定时器。 outb_p (0x36, 0x43); /* binary, mode 3, LSB/MSB, ch 0 */ outb_p (LATCH & 0xff, 0x40); /* LSB */// 定时值低字节。 outb (LATCH >> 8, 0x40); /* MSB */// 定时值高字节。 // 设置时钟中断处理程序句柄(设置时钟中断门)。 set_intr_gate (0x20, &timer_interrupt); // 修改中断控制器屏蔽码,允许时钟中断。 outb (inb_p (0x21) & ~0x01, 0x21); // 设置系统调用中断门。 set_system_gate (0x80, &system_call);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -