Linux内核0.11版本sched.c中sleep_on()函数分析

本文详细分析了Linux内核0.11版本中sleep_on()函数的工作原理,特别是在多进程等待同一资源时如何构建等待队列。当资源可用时,wake_up()函数如何按顺序唤醒等待的进程,确保调度的正确性。通过示例说明了task1, task2, task3的唤醒过程。" 124042230,1368632,PHP ArrayAccess接口:实现数组式访问对象,"['PHP', '接口', '数组操作']
 void sleep_on(struct task_struct **p)
 {
     struct task_struct *tmp;
     if (!p)   //若指针无效,则退出
         return;
     if (current == &(init_task.task)) //若当前任务是任务0则死机
         panic("task[0] trying to sleep");
     tmp = *p; //让tmp指向已经在等待队列上的任务
     *p = current; //将睡眠队列头的等待指针指向当前任务
     current->state = TASK_UNINTERRUPTIBLE;  //将当前任务置为不可中断的等待状态
     schedule();   //重新调度
// 只有当这个等待任务被唤醒时,调度程序才又返回到这里,则表示进程已被明确地唤醒。
     if (tmp)  

         tmp->state=0;  // 若在其前还存在等待的任务,则也将其置为就绪状态(唤醒)。

 }

在几个进程为等待同一资源而多次调用该函数时,程序就隐式地构筑出一个等待队列。

在插入等待队列后,sleep_on()函数就会调用schedule()函数去执行别的进程。当进程被唤醒而重新
执行时就会执行后续的语句,把比它早

任务描述 在Linux-0.11中实现一个系统调用函数,其功能是完成进程的切换,并打印进程切换前后的进程号。 相关知识 为了完成本关任务,你需要掌握: (1)理解在Linux-0.11中,进程切换的过程及原理; (2)了解进程切换时,系统是如何保存当前进程的执行上下文(如寄存器值、程序计数器等); (3)了解进程切换时,相关寄存器值的变化过程; Linux-0.11中进程切换的过程是什么 1)用TR中存储的段选择符在GDT表中找到当前TSS的内存位置; 2)通过该TSS描述符找到当前的TSS段在内存中的位置,于是将CPU中的寄存器值存放到这段内存区域中,即保存现场; 3)通过ljmp指令的操作数(目标进程的TSS描述符的段选择子); 4)通过该TSS描述符找到了目标的TSS段在内存中的位置,并将其中信息放入CPU寄存器中; 5)修改TR寄存器的值为目标进程的TSS段在GDT表中的段描述符所在的位置; 如何查看TR寄存器的值 TR寄存器是一个16位的寄存器,用于指向当前任务的任务状态段(TSS)描述符,TSS描述符存放在GDT中,在bochs中课使用sreg指令查看TR寄存器的值; switch_to是如何切换进程的 切换当前任务到任务n,首先检测任务n是不是当前任务,如果是则什么也不做退出。其中%0是偏移地址(*&__tmp.a),%1用于存放新TSS的段选择符,dx是新任务n的TSS段选择符,ecx是新任务指针task[n]; #define switch_to(n) \ { \ struct { \ long a, b; \ } __tmp; \ __asm__("cmpl %%ecx,_current\n\t" \ "je 1f\n\t" \ "movw %%dx,%1\n\t" \ "xchgl %%ecx,_current\n\t" \ "ljmp %0\n\t" // 执行长跳转,使任务切换 \ "cmpl %%ecx,_last_task_used_math\n\t" \ "jne 1f\n\t" \ "clts\n" \ "1:" ::"m"(*&__tmp.a), \ "m"(*&__tmp.b), "d"(_TSS(n)), "c"((long)task[n])); \ } 如何实现进程切换的系统调用 修改Linux-0.11内核源码,添加一个sys_sched系统调用。 1)向unistd.h头文件中添加sys_sched系统调用符号常数的宏定义,这里需要添加两处: 首先在oslab/linux-0.11/内核源码下找到unistd.h头文件进行添加(启动Linux-0.11之前); 此外需要启动Linux-0.11后在/usr/include/目录下找到unistd.h头文件以同样的方式添加,使用指令vi unistd.h编辑,并在相应位置添加sys_sched的系统调用号,添加方式与在内核中添加是一致的(这一步骤放在make指令重新编译并启动Linux-0.11之后在完成); 2)在system_call.s文件中修改Linux-0.11内核中系统调用的数量; 3)向sys.h头文件中维护系统调用表并添加系统调用名; 4)在sched.c文件中编写sys_sched系统调用(其中函数体部分复用schedule()函数的实现方式),并在执行switch_to进程切换之前,使用prink()函数打印进程切换前后的进程号,打印格式例如:Schedule 5->7,表示从进程5切换到进程7;schedule()函数如下: void schedule(void) { int i, next, c; struct task_struct **p; // 任务结构指针的指针。 // 从任务数组中最后一个任务开始检测 alarm。 for (p = &LAST_TASK; p > &FIRST_TASK; --p) if (*p) { if ((*p)->alarm && (*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 的结果,则执行任务切换 if (c) break; // 否则就根据每个任务的优先权值,更新每一个任务的 counter 值, for (p = &LAST_TASK; p > &FIRST_TASK; --p) if (*p) (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; } switch_to(next); } 5)修改完内核后在linux-0.11/目录下使用make指令重新编译Linux-0.11。使用指令./run启动Linux-0.11后在/usr/root/路径下编写测试程序sched_test.c用于使用sys_sched系统调用进行进程的切换; 程序首先添加行:#define __LIBRARY__用于包括定义在unistd.h中的内嵌汇编代码等信息; 其次引入相关头文件,以及需要内嵌用于系统调用的宏函数,即在代码中单独添加行:_syscall0(int,sched); 补充: schedule()调度函数:首先对所有任务(进程)进行检测,唤醒任何一个已经得到信号的任务。 sched.c源文件:Linux-0.11内核中有关任务调度函数的程序。其中sleep_on()函数是当一个进程(或任务)所请求的资源正忙或不在内存中时暂时切换出去,进入等待队列中;wake_up()函数把正在等待可用资源的指定任务置为就绪状态。(文件路径:kernel/sched.c) 编程要求 1.修改Linux-0.11内核源码,添加一个sys_sched系统调用,功能是进程的切换。 2.向相应文件填写相关代码(提示:可以仿造其他系统调用的写法)。 3.编写测试程序sched_test.c用于sys_sched系统调用(直接调用即可),并查看打印结果(是从任务1切换到了任务几),最后将打印结果(注意打印的格式有严格的要求)写入实验目录oslab下的test.txt文件(需要单独创建,答案之间通过“;”间隔)中。 测试说明 平台会检测修改后的源码并进行评估,请将实验操作进行保存后再评测。 开始你的任务吧,祝你成功! 实验环境1 启动环境 给出我每一步的详细步骤帮助我完成头歌实验
最新发布
11-11
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值