<!--
p { margin-bottom: 0.08in; }
-->
当进程等待资源或者事件时,就进入睡眠状态。有两种睡眠态,不可中断睡眠态(
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中。但是代码比较难理解,因为构造的等待队列是一个隐式队列,利用进程地址空间的独立性隐式地连接成一个队列。这个想法很奇妙。
<!--
p { margin-bottom: 0.08in; }
-->
sleep_on()
<!--
p { margin-bottom: 0.08in; }
-->
这个函数牵涉到
3个指针,
p,
tmp和
current。
p是指向指针的指针,实际上
*p指向的是等待队列头。系统资源(高速缓冲块,文件
i节点或者文件系统的超级块)的数据结构中都一个
struct
task_struct
*类型的指针,指向的就是等待该资源的进程队列头。比如
i节点中的
i_wait,高速缓冲块中的
b_wait,超级块中的
s_wait。
*p对于等待队列上的所有进程都是一样的。
current指向的是当前进程指针,是全局变量。
tmp位于当前进程的地址空间内,是局部变量。不同的进程有不同
tmp变量。等待队列就是利用这个变量把所有等待同一个资源的进程连接起来。具体的说,所有等待在队列上的进程,都是在
sleep_on()中
schedule()中被切换出去的,这些进程还停留在
sleep_on()函数中,在函数的堆栈空间里面,存放了局部变量
tmp。
假如当前进程要进入某个高速缓冲块的等待队列,而且该等待队列上已经有另外两个进程
task1和
task2先后进入。形成的队列如图。等待队列是堆栈式的,先进入队列的进程排在最后。
<!--
p { margin-bottom: 0.08in; }
-->
在调用了
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()
<!--
p { margin-bottom: 0.08in; }
-->
<!--
p { margin-bottom: 0.08in; }
-->
下面分析
sleep_on()
和
wait_up()配合使用的情况
情况一
游离队列的产生
先分析一下
sleep_on()和
wake_up()在通常情况下的工作原理。考虑一个非常简单的情况,假设目前系统只有
3个进程,且都等在队列上,队列的头指针设为
wait。
<!--
p { margin-bottom: 0.08in; }
-->
然后系统资源得到释放,当前进程调用
wake_up(wait)。这时
Task
C变成了运行态。
<!--
p { margin-bottom: 0.08in; }
-->
之后进程调度发生,
Task
C被选中,开始运行。
Task
C是从
sheep_on()中的
schedule()的后一条语句开始运行,它把
Task
B的状态变成运行态。随后
Task
C退出
sheep_on()函数,堆栈中的局部变量
tmp消失,这样再没有指向
Task
B的指针,
Task B开头的队列游离了。
<!--
p { margin-bottom: 0.08in; }
-->
情况
1-1
这时对同一个资源有两个进程是可运行状态,但是当前进程是
Task
C,只要它不调用
schedule,它是不会被抢断的。因此
Task
C继续运行,取得了它想要的资源,这时
Task
C可以完成它的任务了。当进程调度再次发生时,
Task
B会被选中,同样,
Task B会把
Task
A变成可运行态,而它自己得到了资源。最终
Task
A也会得到执行。这样,等待在一个资源上的三个任务最终都得到运行。
情况
1-2
假设
Task
C在得到资源后,又主动调用了
schedule(),进程调度程序这时选中了
Task
B。
Task
B从上次中断的地方开始运行,即从
sleep_on()中
schedule()后面的语句开始运行。它会把
Task
A也变成可运行状态。然后退出
sleep_on(),
tmp变量消失。但是不幸的是它发现资源仍然被占用,所以再次进入睡眠,又连接到
wait队列上了。
<!--
p { margin-bottom: 0.08in; }
-->
从这个情况可以看到,虽然系统运行过程中,可能会把等待队列切分成很多游离队列,但是这些队列头上的进程都是运行态,这保证
schedule()函数最终还是会找到它。
情况二
游离队列的合并
假设目前进程等待资源的情况如下,某个进程占用资源不放,导致有
7个进程等待该资源。产生
3个队列,其中两个游离。
<!--
p { margin-bottom: 0.08in; }
-->
这时调度函数选中
Task
E执行,
Task E先唤醒
Task
D但发现资源不能用,再次睡眠,把自己移到
wait队列,脱离了游离队列。调度再次发生。
<!--
p { margin-bottom: 0.08in; }
-->
假如这时
Task
B得到运行,同样
Task B也只能唤醒
Task
A,而把自己移动到等待队列
p { margin-bottom: 0.08in; }
这样,只要游离队列头上的进程是运行态,游离队列可以再次合并到原先的等待队列上。
p { margin-bottom: 0.08in; }
interruptible_sleep_on()
分享到:
相关推荐
linux-0.11-devel-060625.ziplinux-0.11-devel-060625.ziplinux-0.11-devel-060625.ziplinux-0.11-devel-060625.ziplinux-0.11-devel-060625.ziplinux-0.11-devel-060625.ziplinux-0.11-devel-060625.ziplinux-0.11-...
Linux 0.11内核具有高度的可移植性和可扩展性,可以在多个硬件平台上运行,并且可以根据不同的应用场景进行定制。 尽管Linux 0.11内核已经经历了多次更新和升级,但它的源代码仍被视为学习Unix/Linux内核的入门级...
linux-0.11-devel-050518.ziplinux-0.11-devel-050518.ziplinux-0.11-devel-050518.ziplinux-0.11-devel-050518.ziplinux-0.11-devel-050518.ziplinux-0.11-devel-050518.ziplinux-0.11-devel-050518.ziplinux-0.11-...
自己的linux-0.11编译开发环境,增加了nasm可执行文件,增加了dos软盘,虚拟机用的是bochs-2.1.1。
linux 0.11内核下载,linux 内核
windows下面编译linux-0.11内核 MinGW32 Bochs
Linux0.11内核代码,对于学习内核的人来说,Linux0.11内核代码确实是个不错的选择。。
linux内核完全注释根据linux0.11编写的。搭配内核设计的艺术2版,学习linux内核的神组合。学习内核建议从linux0.11看起,初始版本内容比较简单,但包含的都是内核的精髓。先上手,学到了方法,后面再看最新版本就更...
LINUX0.11内核源码 真男人就读源码
《Linux 0.11内核完全注释》
Linux内核Linux内核0.11完全注解.rar0.11完全注解.rar
linux0.11内核源代码 linux0.11内核源代码 linux0.11内核源代码
本书对早期的Linux操作系统内核(v0.11)全部源代码文件进行了详细的注释和 说明,旨在让读者能够在短时间内对Linux的工作机理获得全面而深刻的理解,为进一步学习和研究Linux系统打下坚实的基础。书中首先介绍了 ...
linux-0.11-040327-rh9.diff.gz
linux0.11内核原理框图 VISIO版
可以直接make的linux-0.11源码,得到Image
linux-0.11.rar linux0.11版内核源代码 下载
linux0.11资料,linux0.11内核源码及完全注释
看linux-0.11代码,从启动0.11开始
linux0.11内核源代码.麻雀虽小,五脏俱全,linux内核的早期版本,学习LINUX内核的好东西!