linux 阻塞操作系统阻塞进程与唤醒,说明下具体的过程,

操作系统实验指导书(linux版)_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
29页免费106页免费21页免费73页免费43页免费 22页免费94页免费15页免费45页免费7页免费
喜欢此文档的还喜欢9页免费73页免费4页免费5页免费18页1下载券
操作系统实验指导书(linux版)|操​作​系​统​实​验​指​导​书​(​l​i​n​u​x​版​)
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢【Linux 驱动】第六章 高级字符驱动程序操作 ----阻塞型I/O - 推酷
【Linux 驱动】第六章 高级字符驱动程序操作 ----阻塞型I/O
& & &序言:试想如果在驱动方法中的read/write中,当数据不可用时,用户可能调用read,当输出缓冲区满时,设备并未准备好接受数据,这种情况下驱动程序可以阻塞该进程,并且置入休眠状态直到满足条件。
& & & & & 当一个进程休眠时,它会被标记为一种特殊状态并从调度器的
中移走,直到某些情况修改了这个状态,进程才会在任意cpu上调度,即运行该进程。
& &在linux下,为了让进程安全的进入休眠状态,有两条规则需要牢记:
& & & & &1)永远不要在原子上下文中休眠。
& & & & & & & &说明:原子上下文:在执行多个步骤时,不能有任何并发访问。不能再拥有自旋锁,seqlock,rcu锁时休眠,进程1在拥有信号量时休眠是合法的,但是要确保进程1拥有信号量不会阻塞唤醒我们的那个进程2。
& & & & &2)对唤醒之后的状态不能做任何假定,必须检查以确保我们等待的条件为真。
& & & 初始化一个等待队列有两种方法:
& & & & &1)静态
& & & & & & & & DECLARE_WAIT_QUEUE_HEAD(my_queue);
& & & & &2)动态
& & & & & & & &wait_queue_head_t & &my_
& & & & & & & &init_waitqueue_head(&my_queue);
&一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在
&linux/wait.h&
struct __wait_queue_head {
struct list_head task_
typedef struct __wait_queue_head wait_queue_head_t;
二, 简单休眠
& & 等待队列:一个进程链表,包含了等待某个特定事件的
& & 在linux中,一个等待队列通过一个&wait queue head&来管理,类型为 wait_queue_head_t,在&linux/wait.h&中定义。
& &休眠宏: &wait_event(queue,condition); &//queue是等待队列头 &condition是boolea类型
& & & & & & & & & & & wait_event_interruptible(queue,condition); //可以中断休眠
& & & & & & & & & & & wait_event_timeout(queue,condition,timeout);//等待限定的时间
& & & & & & & & & & & wait_event_interruptible_timeout(queue,condition,timeout);
& & &上述宏的调用如果condition条件不满足将引起调用进程的阻塞,并且宏会在休眠前后对condition求值,从名字可以看出带interruptible的宏用户可以中断休眠,返回非0值表示被中断,此时驱动程序要返回 & & &-ERESTARTSYS.而带timeout的宏表示在给定的时间到期时,宏都会返回0.
& &唤醒函数:
& & & & & & & & & &void & wake_up(wait_queue_head_t & *queue);
& & & & & & & & & &void & wake_up_interruptible(wait_queue_head_t & &*queue);
wake_up会唤醒在queue上的所有进程,而wake_up_interruptible只唤醒那些可中断的进程。
三,高级休眠
& 进程如何休眠?
& & & &step1:分配初始化一个wait_queue_t 结构,然后加入到对应的等待队列。
& & & &step2:通过函数set_current_state(int new_state)设置进程状态。
& & & & & & & & & & & 驱动程序关心的进程状态主要是TASK_RUNNING(可运行),TASK_INTERRUPTIBLE(可中断休眠),TASK_UNINTERRPUTIBLE(不可中断休眠)。
& & & step3:调用schedule(),记住在调用之前,必须先检查进入休眠的条件。如果不做检查会引入竞态: 如果在忙于上面的这个过程时有其他的线程刚刚试图唤醒你,你可能错过
& 唤醒且长时间休眠,经典的检查代码如下:
& & & & & & &if(!condition)
& & & & & & & & & & & & schedule();
手工休眠 &&linux/sched.h&中包含必须的定义
& & & &&(1)创建和初始化一个等待队列。常由宏定义完成:
& & & & & & & & & & & & & &DEFINE_WAIT(my_wait); & //&name 是等待队列入口项的名字.
& & & & & & & & & &&
& & & & & & & & & & 方法二:
& & & & & & & & & & &wait_queue_t & &my_
& & & & & & & & & & &init_wait(&my_wait);
& & & & & & & & &常用的做法是放一个 DEFINE_WAIT 在循环的顶部,来实现休眠。
&(2)添加等待队列入口到队列,并设置进程状态:
& & & & & & & void & &prepare_to_wait(wait_queue_head_t & & *queue, wait_queue_t & &*wait, int & &state);&
& & & & & & & & & &queue & 等待队列头
& & & & & & & & & &
& & & & & & & & & & state & 进程的新状态:TASK_INTERRUPTIBLE(可中断休眠,推荐)或TASK_UNINTERRUPTIBLE(不可中断休眠,不推荐)。
(3)在检查确认仍然需要休眠之后调用 schedule
& & & & & & & & & & & schedule();
(4)schedule 返回,就到了清理时间:
& & & &void & & &finish_wait(wait_queue_head_t & & *queue, wait_queue_t & &*wait);&
& & & 认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue, condition) 底层源码会发现,其实他们只是手工休眠中的函数的组合。所以怕麻烦的话还是用wait_event比较好。
&&当一个进程调用 wake_up 在等待队列上,所有的在这个队列上等待的进程被置为可运行的。 这在许多情况下是正确的做法。但有时,可能只有一个被唤醒的进程将成功获得需要的资源,而其余的将再次休眠。这时如果等待队列中的进程数目大,这可能严重降低系统性能。为此,内核开发者增加了一个“独占等待”选项
独占等待进程与普通休眠进程的不同:
& & & & & 1)等待队列入口设置了WQ_FLAG_EXCLUSIVE标志,并且添加到等待队列的尾部,没有这个标志的进程被添加到等待队列的头部。
& & & & & &2)在某个等待队列上调用wake_up时,它会唤醒第一个具有WQ_FLAG_EXCLUSIVE标志的进程之后再唤醒其他进程。
& &什么时候采用独占等待进程呢?满足以下两个条件可以考虑:
& & & & & 1)对某个资源存在严重竞争。
& & & & & 2)唤醒单个进程就能完整消耗该资源
六,唤醒的相关函数
& & & &很少会需要调用wake_up_interruptible 之外的唤醒函数,但为完整起见,这里是整个集合:
& & & & & & & wake_up(wait_queue_head_t *queue); //
唤醒队列中的每个非独占等待进程和一个独占等待进程
& & & & & & & wake_up_interruptible(wait_queue_head_t *queue); //
过处于不可中断休眠的进程。它们在返回之前, 使一个或多个进程被唤醒、被调度(如果它们被从一个原子上下文调用, 这就不会发生)
& & & & & & & wake_up_nr(wait_queue_head_t *queue, int nr);&
& & & & & & & wake_up_interruptible_nr(wait_queue_head_t &*queue, int nr);&
& & & & & & & 这些函数类似 wake_up, 除了它们能够唤醒多达 nr 个独占等待者, 而不只是一个. 注意传递 0 被解释为请求所有的互斥等待者都被唤醒&
& & & & & & &wake_up_all(wait_queue_head_t *queue);&
& & & & & & &wake_up_interruptible_all(wait_queue_head_t &*queue);&& & &
& & & & & & 这种 wake_up 唤醒所有的进程, 不管它们是否进行独占等待(可中断的类型仍然跳过在做不可中断等待的进程)
& & & & & & &wake_up_interruptible_sync(wait_queue_head_t *queue);&
& & & & & & &一个被唤醒的进程可能抢占当前进程, 并且在 wake_up 返回之前被调度到处理器。 但是, 如果你需要不要被调度出处理器时,可以使用 & & & & & &wake_up_interruptible 的&同步&变体. 这个函数最常用在调用者首先要完成剩下的少量工作,且不希望被调度出处理器时。
七,阻塞和非阻塞操作
&设置非阻塞I/O
& &显式的非阻塞I/O由filp-&f_flags中的O_NONBLOCK标志决定,为了保持和System V代码兼容,标志O_NDELAY和O_NONBLOCK是一个意思,非阻塞io的read和write会返回-EAGAIN.
& &应用程序有两种方式制定非阻塞IO:&
& &1)在open的时候指定,用于在open调用可能会阻塞很长的时间。
& &2)调用fcntl函数。具体使用方法可在网上查哦这里就不列出了。
阻塞操作的标准语义
& & 如果一个进程调用了read但是没有数据可读,进程阻塞,数据到达时进程被唤醒,并把数据返回给调用者,即使数据少于count;如果一个进程调用了write但是缓冲区满,该进程必须阻塞,休眠在与read进程不同的等待队列上,当缓冲区有空闲时,进程被唤醒,写入数据成功,即使写入了小于count的字节数。
一个阻塞IO的例子scullp
static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
struct scull_pipe *dev = filp-&private_
if (down_interruptible(&dev-&sem))
return -ERESTARTSYS;
while (dev-&rp == dev-&wp) { /* nothing to read */
up(&dev-&sem); /* release the lock */
if (filp-&f_flags & O_NONBLOCK)
return -EAGAIN;
PDEBUG(&\&%s\& reading: going to sleep\n&, current-&comm);
if (wait_event_interruptible(dev-&inq, (dev-&rp != dev-&wp)))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
if (down_interruptible(&dev-&sem))
return -ERESTARTSYS;
/* ok, data is there, return something */
if (dev-&wp & dev-&rp)
count = min(count, (size_t)(dev-&wp - dev-&rp));
else /* the write pointer has wrapped, return data up to dev-&end */
count = min(count, (size_t)(dev-&end - dev-&rp));
if (copy_to_user(buf, dev-&rp, count)) {
up (&dev-&sem);
return -EFAULT;
dev-&rp +=
if (dev-&rp == dev-&end)
dev-&rp = dev-& /* wrapped */
up (&dev-&sem);
/* finally, awake any writers and return */
wake_up_interruptible(&dev-&outq);
PDEBUG(&\&%s\& did read %li bytes\n&,current-&comm, (long)count);
已发表评论数()
&&登&&&陆&&
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见您现在正在浏览:
linux操作系统进程调度方法(SCHED_OTHER,SCHED_FIFO,SCHED_RR)
发布时间:
02:03:15 &
浏览次数:
linux进程调度方法(SCHED_OTHER,SCHED_FIFO,SCHED_RR)
linux进程调度方法(SCHED_OTHER,SCHED_FIFO,SCHED_RR)
09:12:15 |1次阅读 |评论:0条|
linux内核的三种调度方法:
1,SCHED_OTHER 分时调度策略,
2,SCHED_FIFO实时调度策略,先到先服务
3,SCHED_RR实时调度策略,时间片轮转
linux内核的三种调度方法:
1,SCHED_OTHER 分时调度策略,
2,SCHED_FIFO实时调度策略,先到先服务
3,SCHED_RR实时调度策略,时间片轮转
实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。
SHCED_RR和SCHED_FIFO的不同:
当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。
SCHED_FIFO一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃。
如果有相同优先级的实时进程(根据优先级计算的调度权值是一样的)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。而RR可以让每个任务都执行一段时间。
相同点:
RR和FIFO都只用于实时任务。
创建时优先级大于0(1-99)。
按照可抢占优先级调度算法进行。
就绪态的实时任务立即抢占非实时任务。
所有任务都采用linux分时调度策略时。
1,创建任务指定采用分时调度策略,并指定优先级nice值(-20~19)。
2,将根据每个任务的nice值确定在cpu上的执行时间(counter)。
3,如果没有等待资源,则将该任务加入到就绪队列中。
4,调度程序遍历就绪队列中的任务,通过对每个任务动态优先级的计算(counter+20-nice)结果,选择计算结果最大的一个去运行,当这个时间片用完后(counter减至0)或者主动放弃cpu时,该任务将被放在就绪队列末尾(时间片用完)或等待队列(因等待资源而放弃cpu)中。
5,此时调度程序重复上面计算过程,转到第4步。
6,当调度程序发现所有就绪任务计算所得的权值都为不大于0时,重复第2步。
所有任务都采用FIFO时,
1,创建进程时指定采用FIFO,并设置实时优先级rt_priority(1-99)。
2,如果没有等待资源,则将该任务加入到就绪队列中。
3,调度程序遍历就绪队列,根据实时优先级计算调度权值(1000+rt_priority),选择权值最高的任务使用cpu,该FIFO任务将一直占有cpu直到有优先级更高的任务就绪(即使优先级相同也不行)或者主动放弃(等待资源)。
4,调度程序发现有优先级更高的任务到达(高优先级任务可能被中断或定时器任务唤醒,再或被当前运行的任务唤醒,等等),则调度程序立即在当前任务堆栈中保存当前cpu寄存器的所有数据,重新从高优先级任务的堆栈中加载寄存器数据到cpu,此时高优先级的任务开始运行。重复第3步。
5,如果当前任务因等待资源而主动放弃cpu使用权,则该任务将从就绪队列中删除,加入等待队列,此时重复第3步。
所有任务都采用RR调度策略时
1,创建任务时指定调度参数为RR,并设置任务的实时优先级和nice值(nice值将会转换为该任务的时间片的长度)。
2,如果没有等待资源,则将该任务加入到就绪队列中。
3,调度程序遍历就绪队列,根据实时优先级计算调度权值(1000+rt_priority),选择权值最高的任务使用cpu。
4,如果就绪队列中的RR任务时间片为0,则会根据nice值设置该任务的时间片,同时将该任务放入就绪队列的末尾。重复步骤3。
5,当前任务由于等待资源而主动退出cpu,则其加入等待队列中。重复步骤3。
系统中既有分时调度,又有时间片轮转调度和先进先出调度
1,RR调度和FIFO调度的进程属于实时进程,以分时调度的进程是非实时进程。
2,当实时进程准备就绪后,如果当前cpu正在运行非实时进程,则实时进程立即抢占非实时进程。
3,RR进程和FIFO进程都采用实时优先级做为调度的权值标准,RR是FIFO的一个延伸。FIFO时,如果两个进程的优先级一样,则这两个优先级一样的进程具体执行哪一个是由其在队列中的未知决定的,这样导致一些不公正性(优先级是一样的,为什么要让你一直运行),如果将两个优先级一样的任务的调度策略都设为RR,则保证了这两个任务可以循环执行,保证了公平。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/99982/showart_2000704.html
上一篇:linux利用grep查看打印匹配的下几行或前后几行的命令下一篇:linux下apache+php+mysql升级安装过程(源代码详细安装步骤和gd库的安装)
评论加载中...
(新注册)
验证码:匿名:ITOKIT友示:文明上网、拒绝恶骂
相关文章
linux利用grep查看打印匹配的下几行或前后几行...
linux系统下利用find命令查找一些特征的文件信...
linux系统中使用head命令和tail命令查看指定文...
日常linux系统安全检查小知识 让你linux更安全
linux系统下ssh远程登录出现错误:WARNING: R...
linux系统下如何安装php的curl扩展 轻松使用p...
如何在Linux系统下安装网卡的驱动程序 详解li...
php在linux系统下实现百度文库功能 doc或pdf转...
如何在linux系统下查看各种网络连接情况和网络...
Linux系统下对历史命令的操作或清除以及登录的...
本周热门排行Linux操作系统原理与应用--陈莉君_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
318页¥5.00464页1下载券151页1下载券318页免费27页免费 102页免费81页免费44页免费4页免费74页1下载券
喜欢此文档的还喜欢464页1下载券232页1下载券814页1下载券39页1下载券151页1下载券
Linux操作系统原理与应用--陈莉君|
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢}

我要回帖

更多关于 linux 阻塞 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信