Linux0.11内核--进程的调度(运行态(就绪态)和睡眠态之间的转换)
来源:互联网 发布:手机游戏制作软件 编辑:程序博客网 时间:2024/05/18 01:03
当进程等待资源或者事件时,就进入睡眠状态。有两种睡眠态,不可中断睡眠态(TASK_UNINTERRUPTIBLE)和可中断睡眠态(TASK_INTERRUPTIBLE)。
处于可中断睡眠态的进程不光可以由wake_up直接唤醒,还可以由信号唤醒。在schedule()函数中,会把处于可中断睡眠态并且收到信号的进程变成运行态,使他参与调度选择。Linux0.11中进入可中断睡眠状态的方法有3中
调用interruptible_sleep_on()函数
调用sys_pause()函数
调用sys_waitpid()函数。
第一种情况用于等待外设资源时(如等待I/O设备),这时当前进程会挂在对应的等待队列上。第二第三种情况用于事件,即等待信号。
进程要进入不可中断睡眠态,只能通过sleep_on()函数。要使处于不可中断睡眠态的进程进入运行态,只能由其他进程调用wake_up()将它唤醒。当进程等待系统资源(比如高速缓冲块,文件i节点或者文件系统的超级块)时,会调用sleep_on()函数,使当前进程挂起在相关资源的等待队列上。
这部分代码很短,一共三个函数sleep_on(),wake_up()和interruptible_sleep_on()。在sched.c中。但是代码比较难理解,因为构造的等待队列是一个隐式队列,利用进程地址空间的独立性隐式地连接成一个队列。这个想法很奇妙。
sleep_on()
这个函数牵涉到3个指针,p,tmp和current。
p是指向指针的指针,实际上*p指向的是等待队列头。系统资源(高速缓冲块,文件i节点或者文件系统的超级块)的数据结构中都一个structtask_struct*类型的指针,指向的就是等待该资源的进程队列头。比如i节点中的i_wait,高速缓冲块中的b_wait,超级块中的s_wait。*p对于等待队列上的所有进程都是一样的。
current指向的是当前进程指针,是全局变量。
tmp位于当前进程的地址空间内,是局部变量。不同的进程有不同tmp变量。等待队列就是利用这个变量把所有等待同一个资源的进程连接起来。具体的说,所有等待在队列上的进程,都是在sleep_on()中schedule()中被切换出去的,这些进程还停留在sleep_on()函数中,在函数的堆栈空间里面,存放了局部变量tmp。
假如当前进程要进入某个高速缓冲块的等待队列,而且该等待队列上已经有另外两个进程task1和task2先后进入。形成的队列如图。等待队列是堆栈式的,先进入队列的进程排在最后。
在调用了sleep_on()的地方,我们可以发现sleep_on()往往是放在一个循环中的(比如wait_on_buffer(),wait_on_inode(),lock_inode(),lock_super(),wait_on_super()等函数)。当进程从sleep_on()返回时,并不能保证当前进程取得了资源使用权,因为调用wake_up()进程切换到从sleep_on()中苏醒的过程中,发生了进程调度,中间很可能有别的进程取得了资源。
wake_up()
下面分析sleep_on()和 wait_up()配合使用的情况
情况一游离队列的产生
先分析一下sleep_on()和wake_up()在通常情况下的工作原理。考虑一个非常简单的情况,假设目前系统只有3个进程,且都等在队列上,队列的头指针设为wait。
然后系统资源得到释放,当前进程调用wake_up(wait)。这时TaskC变成了运行态。
之后进程调度发生,TaskC被选中,开始运行。TaskC是从sheep_on()中的schedule()的后一条语句开始运行,它把TaskB的状态变成运行态。随后TaskC退出sheep_on()函数,堆栈中的局部变量tmp消失,这样再没有指向TaskB的指针,Task B开头的队列游离了。
情况1-1
这时对同一个资源有两个进程是可运行状态,但是当前进程是TaskC,只要它不调用schedule,它是不会被抢断的。因此TaskC继续运行,取得了它想要的资源,这时TaskC可以完成它的任务了。当进程调度再次发生时,TaskB会被选中,同样,Task B会把TaskA变成可运行态,而它自己得到了资源。最终TaskA也会得到执行。这样,等待在一个资源上的三个任务最终都得到运行。
情况1-2
假设TaskC在得到资源后,又主动调用了schedule(),进程调度程序这时选中了TaskB。TaskB从上次中断的地方开始运行,即从sleep_on()中schedule()后面的语句开始运行。它会把TaskA也变成可运行状态。然后退出sleep_on(),tmp变量消失。但是不幸的是它发现资源仍然被占用,所以再次进入睡眠,又连接到wait队列上了。
从这个情况可以看到,虽然系统运行过程中,可能会把等待队列切分成很多游离队列,但是这些队列头上的进程都是运行态,这保证schedule()函数最终还是会找到它。
情况二游离队列的合并
假设目前进程等待资源的情况如下,某个进程占用资源不放,导致有7个进程等待该资源。产生3个队列,其中两个游离。
这时调度函数选中TaskE执行,Task E先唤醒TaskD但发现资源不能用,再次睡眠,把自己移到wait队列,脱离了游离队列。调度再次发生。
假如这时TaskB得到运行,同样Task B也只能唤醒TaskA,而把自己移动到等待队列
p { margin-bottom: 0.08in; }
这样,只要游离队列头上的进程是运行态,游离队列可以再次合并到原先的等待队列上。
p { margin-bottom: 0.08in; }
interruptible_sleep_on()
- Linux0.11内核--进程的调度(运行态(就绪态)和睡眠态之间的转换)
- Linux0.11内核--进程的调度(就绪态和运行态之间的转换)
- Linux0.11内核--进程的调度(就绪态和运行态之间的转换)
- Linux0.11内核--进程的调度schedule和switch_to解析
- Linux0.11内核 进程睡眠和唤醒
- 对于Linux0.11内核版本调度与睡眠机制的一些见解
- Linux0.11 由进程睡眠函数sleep_on()中的堆栈变量tmp引发的思考 关于进程内核堆栈
- Linux0.11内核--进程的结束
- Linux0.11内核--进程的结束
- Linux0.11内核--内核空间、用户空间之间的数据传输
- Linux0.11内核--内核空间、用户空间之间的数据传输
- linux0.12内核的内存组织和进程结构
- Linux0.11内核分析三之进程调度
- linux0.11进程调度分析
- Linux0.11-内核态与用户态
- Linux0.11内核进程数据结构
- linux0.11,进程0,进程1,进程2运行的先后顺序
- linux0.11内核 任务0 特权级 用户态 的疑问
- atol 函数
- .NET批量大数据插入性能分析及比较(3.使用事务)
- Spark源代码编译生成全攻略
- struts2中出现input类型的视图的返回的原因:
- Flex视频截图并通过WebService(C#)保存
- Linux0.11内核--进程的调度(运行态(就绪态)和睡眠态之间的转换)
- 纵使年华已逝,但我不曾放开爱你的手
- .NET批量大数据插入性能分析及比较(4.使用DataAdapter批量插入)
- 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--PS2解码(九)
- 三)静态链表
- exchange 2007 收到重复邮件
- Android下载源码,编译等的官网说明网址
- Android的Orientation Sensor三个坐标的含义
- 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--VGA驱动(十)