Linux-0.11 kernel目录进程管理sched.c详解
Linux-0.11 kernel目录进程管理sched.c详解
sched.c主要功能是负责进程的调度,其最核心的函数就是schedule。除schedule以外, sleep_on和wake_up也是相对重要的函数。
schedule
void schedule(void)
schedule函数的基本功能可以分为两大块, 第一块是检查task中的报警信息和信号, 第二块则是进行任务的调度。
在第一块中,首先从任务数组的尾部任务开始,检查alarm是否小于当前系统滴答值,如果小于则代表alarm时间已经到期。将进程的signal中的SIGALARM位置1。
接着就看如果检查进程的信号中如果处理BLOCK位以外还有别的信号,并且如果任务处于可中断状态,则将任务置为就绪状态。
int i,next,c;
struct task_struct ** p;for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p) {if ((*p)->alarm && (*p)->alarm < jiffies) { //如果设置了任务定时的值alarm, 并且已经过期(*p)->signal |= (1<<(SIGALRM-1)); //将信号的SIGALARM位置为1(*p)->alarm = 0;}if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&(*p)->state==TASK_INTERRUPTIBLE)//如果信号位图中除了被阻塞的信号外还有其他信号, 并且任务处于可中断状态(*p)->state=TASK_RUNNING; //修改任务的状态为就绪态}
第二块的代码就是任务调度的核心代码。
这里会从任务数组的尾部任务开始进行遍历,从所有任务从选取counter值最大的任务作为下一个运行的任务去执行。
while (1) {c = -1;next = 0;i = NR_TASKS;p = &task[NR_TASKS];//从最后一个任务开始while (--i) { //遍历所有的task, 取出其中counter最大的taskif (!*--p)continue;if ((*p)->state == TASK_RUNNING && (*p)->counter > c)//取出所有任务中counter值最大的任务作为下一个任务c = (*p)->counter, next = i;}if (c) break;//如果当前没有RUNNING状态的任务的counter可以大于-1,那么则去更新counter的值,counter = counter/2 + priorityfor(p = &LAST_TASK ; p > &FIRST_TASK ; --p)if (*p)(*p)->counter = ((*p)->counter >> 1) +(*p)->priority;//更新counter值 counter = counter/2 + priority
}
//切换任务执行next
switch_to(next);
show_task
void show_task(int nr,struct task_struct * p)
该函数的作用是显示任务序号为nr的进程的pid,进程状态以及内核栈剩余的大小。
int i,j = 4096-sizeof(struct task_struct);printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
i=0;
此时j指向PCB所在内存页的顶部, i指向task_struct结构体的下一个字节。下面这段代码的所用实际就是统计内核栈中空闲大小。

while (i<j && !((char *)(p+1))[i])i++;
printk("%d (of %d) chars free in kernel stack\n\r",i,j);
show_stat
void show_stat(void)
该函数内部调用show_task函数,实际上就是遍历task数组, 调用show_stat函数显示进程相关信息。
int i;for (i=0;i<NR_TASKS;i++)//遍历task数组if (task[i])show_task(i,task[i]);//调用show_task
math_state_restore
void math_state_restore()
该函数的作用是将当前协处理器内容保存到老协处理器状态数组中,并将当前任务的协处理器内容加载进协处理器。
sys_pause
int sys_pause(void)
该函数是pause的系统调用。该函数会将当前任务的状态修改为可中断的状态, 并调用schedule函数去进行进程的调度。
调用pause函数的进程会进入睡眠状态, 直到收到一个信号。
current->state = TASK_INTERRUPTIBLE;
schedule();
sleep_on
void sleep_on(struct task_struct **p)
该函数的作用是将当前的task置为不可中断的等待状态, 直到被wake_up唤醒再继续执行。入参p是等待任务队列的头指针。通过p指针和tmp变量将等待的任务串在了一起。

该函数首先对一些异常情况进行了处理他, 例如p是空指针。或者当前task是任务0。
struct task_struct *tmp;// 若指针无效,则退出。(指针所指的对象可以是NULL,但指针本身不会为0)。
if (!p)return;
if (current == &(init_task.task)) // 如果当前任务是任务0,则死机(impossible!)。panic ("task[0] trying to sleep");
接着让当前等待任务的头指针指向当前任务。并将当前任务修改为不可中断的等待状态。进行调用schedule函数让操作系统切换其他任务执行。
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
当程序从schedule()返回继续执行时,说明任务已经被显式的wake_up,如果此时还有其他进程仍然在等待,那么也一同唤醒。
因为任务都在等待同样的资源, 那么当资源可用的时候, 就可以唤醒所有等待的任务。
if (tmp) // 若还存在等待的任务,则也将其置为就绪状态(唤醒)。tmp->state = 0;
interruptible_sleep_on
void interruptible_sleep_on (struct task_struct **p)
该函数与sleep_on类似,但是该函数会将任务的状态修改为可中断的等待状态, 而sleep_on则是将任务修改为不可中断的等待状态。因此通过interruptible_sleep_on而等待的task是可以被信号唤醒的。 而通过sleep_on而等待的task是不会被信号唤醒的,只能通过wake_up函数唤醒。

下面这段代码与sleep_on并无太大区别, 只是将进程的状态修改为可中断的等待状态。
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 = tmp,让队列头指针指向其余等待任务,否则在当前任务之前插入
等待队列的任务均被抹掉了
*p = NULL;
if (tmp)tmp->state = 0;
wake_up
void wake_up(struct task_struct **p)
该函数的作用就是唤醒某一个任务。其用于唤醒p指向的等待队列中的任务。
if (p && *p)
{(**p).state = 0; // 置为就绪(可运行)状态。*p = NULL;
}
ticks_to_floppy_on
int ticks_to_floppy_on(unsigned int nr)
该函数指定软盘到正常运转状态所需延迟滴答数(时间)。
floppy_on
void floppy_on(unsigned int nr)
该函数等待指定软驱马达启动所需时间。
floppy_off
void floppy_off(unsigned int nr)
关闭相应的软驱马达停转定时器3s。
moff_timer[nr]=3*HZ;
do_floppy_timer
void do_floppy_timer(void)
如果马达启动定时到则唤醒进程。
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);
add_timer
add_timer(long jiffies, void (*fn)(void))
```、
该函数的作用是设置定时值和相应的处理函数。如果定时的值小于0, 那么立即调用处理函数。
```c
if (jiffies <= 0)(fn)();
如果定时的值大于0, 那么首先取timer_list数组中寻找一个位置,将该位置上的滴答数设置为jiffies,将该位置上的fn设置为入参fn。并让next_timer指向它。
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;
下面这段代码的作用是将刚刚插入链表中的timer移动的合适的位置。
由于next_timer这个链表上的jiffies是一个相对值,即相对于前面一个timer还有多久到期。因此上面步骤的timer也需要进行转换。

while (p->next && p->next->jiffies < p->jiffies) {p->jiffies -= p->next->jiffies;//减去下一个timer的jiffiesfn = p->fn;//将当前的fn保存给临时变量p->fn = p->next->fn;//将当前的fn设置为下一个timer的fnp->next->fn = fn;//将下一个timer的fn设置为临时变量fnjiffies = p->jiffies;//将jiffies保存给一个临时变量p->jiffies = p->next->jiffies;//将当前的jiffies设置为下一个timer的jiffiesp->next->jiffies = jiffies;//将下一个timer的jiffies设置为当前的jiffiesp = p->next;//这一步骤实际上将p向后挪动到合适的位置, 并把jiffies转化成相对值。
}
do_timer
void do_timer(long cpl)
该函数是时钟中断的处理函数。其在system_call.s中的timer_interrupt函数中被调用。
参数cpl表示的是当前的特权级, 0表示时钟中断发生时,当前运行在内核态,3表示时钟中断发生时,当前运行在用户态。
下面的代码根据cpl的值将进程PCB中的utime和stime进行修改。如果cpl为0,则增加stime(supervisor time), 如果cpl为3, 则增加utime。
if (cpl)current->utime++;
elsecurrent->stime++;
下面对定时器的链表进行遍历。 将链表的第一个定时器的滴答数减1。如果滴答数已经等于0, 代表该定时器已经到期,那么需要调用相应的处理程序进行处理。
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)();}
}
下面代码则是将当前运行的进程的时间片减去1,如果此时进程时间片没有用完,该函数则返回。 如果此时进程时间已经用完,则将时间片设置为0。并且如果此时cpl表明中断发生用户态,那么还将会触发进程的调度。
if ((--current->counter)>0) return;
current->counter=0;
sys_alarm
int sys_alarm(long seconds)
该函数用于设置报警值。
jiffies是指的是系统开机到目前经历的滴答数。
current->alarm的单位也是系统滴答数。
因此(current->alarm - jiffies) /100 就代表就是当前的定时器还剩下多少秒。
而设置alarm值则需要加上系统当前的滴答数据jiffies, 如下图所示:

sys_getpid
int sys_getpid(void)
该函数用于获取进程的pid。
sys_getppid
int sys_getppid(void)
该函数用于获取父进程的pid。
sys_getuid
int sys_getuid(void)
该函数用于获取用户的uid。
sys_geteuid
int sys_geteuid(void)
该函数用于获取用户的有效id(euid)。
sys_getgid
int sys_getgid(void)
获取组和id号(gid)。
sys_getegid
int sys_getegid(void)
取有效的组id(egid)
sys_nice
int sys_nice(long increment)
该函数的作用是降低进程在调度时的优先级。
sched_init
void sched_init(void)
该函数的作用是初始化进程调度模块。
首先在gdt表中设置任务0的tss和ldt值。接着对其他任务的tss和ldt进行初始化。
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.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++;
}
显式地将任务0的tss加载到寄存器tr中, 显式地将任务0的ldt加载到ldtr中。
ltr(0);
lldt(0);
下面的代码用于初始化8253定时器。通道0,选择工作方式3,二进制计数方式。
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);
相关文章:
Linux-0.11 kernel目录进程管理sched.c详解
Linux-0.11 kernel目录进程管理sched.c详解 sched.c主要功能是负责进程的调度,其最核心的函数就是schedule。除schedule以外, sleep_on和wake_up也是相对重要的函数。 schedule void schedule(void)schedule函数的基本功能可以分为两大块,…...
AI已到,普通人的机会在哪里?
“普通人赚到钱很难 但是被骗到钱很容易”。每当火起来一个行业(或者仅是一个概念),都会有人来问:现在去做点什么,能够踩上风口?普通人的赚钱机会在哪?怎么做能够暴富?让我们先来看看…...
CSP-J2022入门组二轮补赛试题(山东)T2:宴会
题目链接 CSP-J2022入门组二轮补赛(山东)第2题:宴会 题目背景 今人不见古时月,今月曾经照古人。梦回长安,大唐风华,十里长安花,一日看尽。 唐长安城是当时世界上规模最大、建筑最宏伟、规划布局最为规范化的一座都城。其营建 制度规划布局的特点是规模空前、创设皇城…...
ubuntu 使用 CMake 构建 Qt5 项目
Qt 概述 概念 Qt 是一个跨平台的 C 图形用户界面应用程序框架 常见的 C GUI: Qt 和 MFC 跨平台 Windows Linux MacOS 嵌入式平台 版本 包括商业版和开源免费版 案例 Linux 桌面环境 KDE WPS Office Qt 安装 下载地址: https://download.qt.io/archive/qt/ http…...
ZooKeeper命令及JavaAPI操作
ZooKeeper数据模型 ZooKeeper是一个树形目录服务,其数据模型和Uiix的文件目录树很类似,拥有一个层次化结构。这里面的每一个节点都被称为:ZNode,每个节点上都会保存自己的数据和节点信息。节点可以拥有子节点,同时也允…...
云医疗信息系统源码(云HIS)商业级全套源代码
云his系统源码,有演示 一个好的HIS系统,要具有开放性,便于扩展升级,增加新的功能模块,支撑好医院的业务的拓展,而且可以反过来给医院赋能,最终向更多的患者提供更好地服务。 私信了解更多&…...
u盘拔掉再插上去文件没了原因|文件恢复方法
如果您遇到了“u盘拔了再插文件变空了”的类似问题困扰,请仔细阅读文本,下面将分享几种方法来恢复u盘上丢失的文件,赶紧来试试!为什么u盘拔掉再插上去文件没了“我的u盘为什么放进东西后拔出,再插进电脑去东西就没有了…...
CorelDRAW2023详解新增七大功能 ,CorelDRAW2023最新版本更新怎么样?
CorelDRAW2023新功能有哪些?CorelDRAW2023最新版本更新怎么样?让我们带您详细了解! CorelDRAW Graphics Suite 2023是矢量制图行业的标杆软件,2023年全新版本为您带来多项新功能和优化改进。本次更新强调易用性,包括更…...
LearnOpenGL-光照-4.光照贴图
本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject 文章目录光照贴图漫反射贴图例子1镜面光贴图例子2 采样镜面光贴图小结什么是光照贴图光照贴图如何影响颜色光…...
ThreadLocal解析
ThreadLocal是一个存储线程本地变量的对象,在ThreadLocal中存储的对象在其他线程中是不可见的,本文介绍ThreadLocal的原理。 1、threadLocal使用 有如下代码: Slf4j public class TestThreadLocal {public static void main(String[] args…...
时间格式表
时间格式化对照表 仅供参考标识符含义aAM/PM(上午/下午)A0~86399999 (一天的第A微秒)c/cc1~7 (一周的第一天, 周天为1)cccSun/Mon/Tue/Wed/Thu/Fri/Sat (星期几简写)ccccSunday/Monday/Tuesday/Wednesday/Thursday/Friday/Saturday (星期几全拼)d1~31 (月份的第几天, 带0)D1~36…...
enscape和twinmotion哪个好用?
Twinmotion 和 Enscape这2款渲染软件最近受到了一些初学者的关注。这 2 个软件适用于那些需要 3D 渲染但质量不是他们项目的首要任务的人。在本文中,我们将对Twinmotion 和 Enscape 进行面对面的比较,并帮助您确定哪一个更适合您。什么是 Twinmotion&…...
Canvas
canvas介绍 什么是 Canvas?Canvas 是为了解决 Web 页面中只能显示静态图片这个问题而提出的,一个可以使用 JavaScript 等脚本语言向其中绘制图像的 HTML 标签。 Canvas 解决了什么问题 我在 MSDN(《Microsoft Developer Network》是微软一…...
旅游预约APP开发具有什么优势和功能
旅游活动目前正在作为广大用户休闲娱乐的一个首选内容,不仅是公司团建活动可以选择旅游,而且一些节假日也可以集结自己的亲朋好友来一次快乐有趣的旅游活动,随着当代人对于旅游的需求呈现上升的趋势,也让旅游预约APP开发开始流行并…...
Python之函数参数细讲
文章目录前言一、了解形式参数和实际参数1. 通过作用理解2. 通过一个比喻来理解形式参数和实际参数二、位置参数1. 数量必须与定义时一致2. 位置必须与定义时一致三、关键字参数四、为参数设置默认值五、可变参数1. *parameter2. **parameter总结前言 在调用函数时,…...
跑步耳机入耳好还是不入耳好、十大跑步运动耳机品牌排行榜推荐
健身房经常会播放一些节奏较快的歌曲,这样能够激发大家在运动过程中的动力,所以运动时聆听音乐确实比较有效果,居家运动、室外跑步时选择运动耳机就变成了刚需,首先不能影响其他人、佩戴时要稳定,音质和续航要有保证&a…...
Go语言容器之数组和切片
Go语言的容器分为值类型和引用数据类型 一、数组 1.数组的声明和初始化 (1) 数组声明的语法 var 数组变量名 [数组大小]数组类型 举例: package main import "fmt"func main(){//数组的声明var arr[10]int//打印数组长度fmt.Println("arr的长度为…...
【ROS2知识】humble下使用插件编程
Creating and using plugins (C++) — ROS 2 Documentation: Humble documentation 一、说明 接口编程的好处不言自明,有兴趣的朋友可以看看相关文章。此处在ROS2上进行接口编程,这是个技术难点,如果不能突破,那么许多方面将不能进行,比如:navigation中的costmap_2d包中…...
MySQL 主备一致
MySQL 主备一致主备切换binlog 格式statementrowmixed生产格式循环复制问题主备切换 MySQL 主备切换流程 : 状态 1 : 客户端的读写都直接访问节点 A,而节点 B 是 A 的备库,只将 A 的更新都同步过来 , 并本地执行。来保持节点 B 和 A 的数据是相同当切换…...
玩转CodeQLpy之用友GRP-U8漏洞挖掘
0x01 前言CodeQLpy是作者使用python3实现的基于CodeQL的java代码审计工具,github地址https://github.com/webraybtl/CodeQLpy。通过CodeQLpy可以辅助代码审计人员快速定位代码中的问题,目前支持对SprintBoot的jar包,SpringMVC的war包…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
