⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sched.c

📁 带中文注释的Linux+0.11+源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/kernel/sched.c * * (C) 1991 Linus Torvalds *//* * 'sched.c' is the main kernel file. It contains scheduling primitives * (sleep_on, wakeup, schedule etc) as well as a number of simple system * call functions (type getpid(), which just extracts a field from * current-task *//* * 'sched.c'是主要的内核文件。其中包括有关调度的基本函数(sleep_on、wakeup、schedule 等)以及 * 一些简单的系统调用函数(比如getpid(),仅从当前任务中获取一个字段)。 */#include <linux/sched.h>	// 调度程序头文件。定义了任务结构task_struct、第1 个初始任务// 的数据。还有一些以宏的形式定义的有关描述符参数设置和获取的// 嵌入式汇编函数程序。#include <linux/kernel.h>	// 内核头文件。含有一些内核常用函数的原形定义。#include <linux/sys.h>		// 系统调用头文件。含有72 个系统调用C 函数处理程序,以'sys_'开头。#include <linux/fdreg.h>	// 软驱头文件。含有软盘控制器参数的一些定义。#include <asm/system.h>		// 系统头文件。定义了设置或修改描述符/中断门等的嵌入式汇编宏。#include <asm/io.h>		// io 头文件。定义硬件端口输入/输出宏汇编语句。#include <asm/segment.h>	// 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。#include <signal.h>		// 信号头文件。定义信号符号常量,sigaction 结构,操作函数原型。#define _S(nr) (1<<((nr)-1))	// 取信号nr 在信号位图中对应位的二进制数值。信号编号1-32。// 比如信号5 的位图数值 = 1<<(5-1) = 16 = 00010000b。#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))	// 除了SIGKILL 和SIGSTOP 信号以外其它都是// 可阻塞的(…10111111111011111111b)。// 显示任务号nr 的进程号、进程状态和内核堆栈空闲字节数(大约)。voidshow_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);}// 显示所有任务的任务号、进程号、进程状态和内核堆栈空闲字节数(大约)。voidshow_stat (void){  int i;  for (i = 0; i < NR_TASKS; i++)	// NR_TASKS 是系统能容纳的最大进程(任务)数量(64 个),    if (task[i])		// 定义在include/kernel/sched.h 第4 行。      show_task (i, task[i]);}// 定义每个时间片的滴答数?。#define LATCH (1193180/HZ)extern void mem_use (void);	// [??]没有任何地方定义和引用该函数。extern int timer_interrupt (void);	// 时钟中断处理程序(kernel/system_call.s,176)。extern int system_call (void);	// 系统调用中断处理程序(kernel/system_call.s,80)。union task_union{				// 定义任务联合(任务结构成员和stack 字符数组程序成员)。  struct task_struct task;	// 因为一个任务数据结构与其堆栈放在同一内存页中,所以  char stack[PAGE_SIZE];	// 从堆栈段寄存器ss 可以获得其数据段选择符。};static union task_union init_task = { INIT_TASK, };	// 定义初始任务的数据(sched.h 中)。long volatile jiffies = 0;	// 从开机开始算起的滴答数时间值(10ms/滴答)。// 前面的限定符volatile,英文解释是易变、不稳定的意思。这里是要求gcc 不要对该变量进行优化// 处理,也不要挪动位置,因为也许别的程序会来修改它的值。long startup_time = 0;		// 开机时间。从1970:0:0:0 开始计时的秒数。struct task_struct *current = &(init_task.task);	// 当前任务指针(初始化为初始任务)。struct task_struct *last_task_used_math = NULL;	// 使用过协处理器任务的指针。struct task_struct *task[NR_TASKS] = { &(init_task.task), };	// 定义任务指针数组。long user_stack[PAGE_SIZE >> 2];	// 定义系统堆栈指针,4K。指针指在最后一项。// 该结构用于设置堆栈ss:esp(数据段选择符,指针),见head.s,第23 行。struct{  long *a;  short b;}stack_start ={&user_stack[PAGE_SIZE >> 2], 0x10};/* * 'math_state_restore()' saves the current math information in the * old math state array, and gets the new ones from the current task *//* * 将当前协处理器内容保存到老协处理器状态数组中,并将当前任务的协处理器 * 内容加载进协处理器。 */// 当任务被调度交换过以后,该函数用以保存原任务的协处理器状态(上下文)并恢复新调度进来的// 当前任务的协处理器执行状态。voidmath_state_restore (){  if (last_task_used_math == current)	// 如果任务没变则返回(上一个任务就是当前任务)。    return;			// 这里所指的"上一个任务"是刚被交换出去的任务。  __asm__ ("fwait");		// 在发送协处理器命令之前要先发WAIT 指令。  if (last_task_used_math)    {				// 如果上个任务使用了协处理器,则保存其状态。      __asm__ ("fnsave %0"::"m" (last_task_used_math->tss.i387));    }  last_task_used_math = current;	// 现在,last_task_used_math 指向当前任务,  // 以备当前任务被交换出去时使用。  if (current->used_math)    {				// 如果当前任务用过协处理器,则恢复其状态。      __asm__ ("frstor %0"::"m" (current->tss.i387));    }  else    {				// 否则的话说明是第一次使用,      __asm__ ("fninit"::);	// 于是就向协处理器发初始化命令,      current->used_math = 1;	// 并设置使用了协处理器标志。    }}/* * 'schedule()' is the scheduler function. This is GOOD CODE! There * probably won't be any reason to change this, as it should work well * in all circumstances (ie gives IO-bound processes good response etc). * The one thing you might take a look at is the signal-handler code here. * * NOTE!! Task 0 is the 'idle' task, which gets called when no other * tasks can run. It can not be killed, and it cannot sleep. The 'state' * information in task[0] is never used. *//* * 'schedule()'是调度函数。这是个很好的代码!没有任何理由对它进行修改,因为它可以在所有的 * 环境下工作(比如能够对IO-边界处理很好的响应等)。只有一件事值得留意,那就是这里的信号 * 处理代码。 * 注意!!任务0 是个闲置('idle')任务,只有当没有其它任务可以运行时才调用它。它不能被杀 * 死,也不能睡眠。任务0 中的状态信息'state'是从来不用的。 */voidschedule (void){  int i, next, c;  struct task_struct **p;	// 任务结构指针的指针。  /* check alarm, wake up any interruptible tasks that have got a signal */  /* 检测alarm(进程的报警定时值),唤醒任何已得到信号的可中断任务 */  // 从任务数组中最后一个任务开始检测alarm。  for (p = &LAST_TASK; p > &FIRST_TASK; --p)    if (*p)      {	// 如果任务的alarm 时间已经过期(alarm<jiffies),则在信号位图中置SIGALRM 信号,然后清alarm。	// jiffies 是系统从开机开始算起的滴答数(10ms/滴答)。定义在sched.h 第139 行。	if ((*p)->alarm && (*p)->alarm < jiffies)	  {	    (*p)->signal |= (1 << (SIGALRM - 1));	    (*p)->alarm = 0;	  }	// 如果信号位图中除被阻塞的信号外还有其它信号,并且任务处于可中断状态,则置任务为就绪状态。	// 其中'~(_BLOCKABLE & (*p)->blocked)'用于忽略被阻塞的信号,但SIGKILL 和SIGSTOP 不能被阻塞。	if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&	    (*p)->state == TASK_INTERRUPTIBLE)	  (*p)->state = TASK_RUNNING;	//置为就绪(可执行)状态。      }  /* this is the scheduler proper: */  /* 这里是调度程序的主要部分 */  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 的结果,则退出124 行开始的循环,执行任务切换(141 行)。      if (c)	break;      // 否则就根据每个任务的优先权值,更新每一个任务的counter 值,然后回到125 行重新比较。      // counter 值的计算方式为counter = counter /2 + priority。[右边counter=0??]      for (p = &LAST_TASK; p > &FIRST_TASK; --p)	if (*p)	  (*p)->counter = ((*p)->counter >> 1) + (*p)->priority;    }  switch_to (next);		// 切换到任务号为next 的任务,并运行之。}//// pause()系统调用。转换当前任务的状态为可中断的等待状态,并重新调度。// 该系统调用将导致进程进入睡眠状态,直到收到一个信号。该信号用于终止进程或者使进程调用// 一个信号捕获函数。只有当捕获了一个信号,并且信号捕获处理函数返回,pause()才会返回。// 此时pause()返回值应该是-1,并且errno 被置为EINTR。这里还没有完全实现(直到0.95 版)。intsys_pause (void){  current->state = TASK_INTERRUPTIBLE;  schedule ();  return 0;}// 把当前任务置为不可中断的等待状态,并让睡眠队列头的指针指向当前任务。// 只有明确地唤醒时才会返回。该函数提供了进程与中断处理程序之间的同步机制。// 函数参数*p 是放置等待任务的队列头指针。(参见列表后的说明)。voidsleep_on (struct task_struct **p){  struct task_struct *tmp;  // 若指针无效,则退出。(指针所指的对象可以是NULL,但指针本身不会为0)。  if (!p)    return;  if (current == &(init_task.task))	// 如果当前任务是任务0,则死机(impossible!)。    panic ("task[0] trying to sleep");  tmp = *p;			// 让tmp 指向已经在等待队列上的任务(如果有的话)。  *p = current;			// 将睡眠队列头的等待指针指向当前任务。  current->state = TASK_UNINTERRUPTIBLE;	// 将当前任务置为不可中断的等待状态。  schedule ();			// 重新调度。  // 只有当这个等待任务被唤醒时,调度程序才又返回到这里,则表示进程已被明确地唤醒。  // 既然大家都在等待同样的资源,那么在资源可用时,就有必要唤醒所有等待该资源的进程。该函数  // 嵌套调用,也会嵌套唤醒所有等待该资源的进程。然后系统会根据这些进程的优先条件,重新调度  // 应该由哪个进程首先使用资源。也即让这些进程竞争上岗。  if (tmp)			// 若还存在等待的任务,则也将其置为就绪状态(唤醒)。    tmp->state = 0;}// 将当前任务置为可中断的等待状态,并放入*p 指定的等待队列中。参见列表后对sleep_on()的说明。voidinterruptible_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 ();  // 如果等待队列中还有等待任务,并且队列头指针所指向的任务不是当前任务时,则将该等待任务置为  // 可运行的就绪状态,并重新执行调度程序。当指针*p 所指向的不是当前任务时,表示在当前任务被放  // 入队列后,又有新的任务被插入等待队列中,因此,既然本任务是可中断的,就应该首先执行所有  // 其它的等待任务。  if (*p && *p != current)    {      (**p).state = 0;      goto repeat;    }  // 下面一句代码有误,应该是*p = tmp,让队列头指针指向其余等待任务,否则在当前任务之前插入  // 等待队列的任务均被抹掉了。参见图4.3。  *p = NULL;  if (tmp)    tmp->state = 0;}// 唤醒指定任务*p。voidwake_up (struct task_struct **p){  if (p && *p)    {      (**p).state = 0;		// 置为就绪(可运行)状态。      *p = NULL;    }}/* * OK, here are some floppy things that shouldn't be in the kernel * proper. They are here because the floppy needs a timer, and this * was the easiest way of doing it. *//* * 好了,从这里开始是一些有关软盘的子程序,本不应该放在内核的主要部分中的。将它们放在这里 * 是因为软驱需要一个时钟,而放在这里是最方便的办法。 */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)。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -