在 Linux 内核的之前的版本, 正式的睡眠要求程序员手动处理所有上面的步骤. 它是一 个繁琐的过程, 包含相当多的易出错的样板式的代码. 程序员如果愿意还是可能用那种方 式手动睡眠; <linux/sched.h> 包含了所有需要的定义, 以及围绕例子的内核源码. 但是, 有一个更容易的方式.

第一步是创建和初始化一个等待队列. 这常常由这个宏定义完成: DEFINE_WAIT(my_wait);

其中, name 是等待队列入口项的名子. 你可用 2 步来做:

wait_queue_t my_wait; init_wait(&my_wait);

但是常常更容易的做法是放一个 DEFINE_WAIT 行在循环的顶部, 来实现你的睡眠. 下一步是添加你的等待队列入口到队列, 并且设置进程状态. 2 个任务都由这个函数处理: void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);

这里, queue 和 wait 分别地是等待队列头和进程入口. state 是进程的新状态; 它应当 或者是 TASK_INTERRUPTIBLE(给可中断的睡眠, 这常常是你所要的)或者 TASK_UNINTERRUPTIBLE(给不可中断睡眠).

在调用 prepare_to_wait 之后, 进程可调用 schedule -- 在它已检查确认它仍然需要等 待之后. 一旦 schedule 返回, 就到了清理时间. 这个任务, 也, 被一个特殊的函数处理:

void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait); 之后, 你的代码可测试它的状态并且看是否它需要再次等待.

我们早该需要一个例子了. 之前我们看了 给 scullpipe 的 read 方法, 它使用 wait_event. 同一个驱动中的 write 方法使用 prepare_to_wait 和 finish_wait 来实 现它的等待. 正常地, 你不会在一个驱动中象这样混用各种方法, 但是我们这样作是为了 能够展示 2 种处理睡眠的方式.

为完整起见, 首先, 我们看 write 方法本身:

/* How much space is free? */

static int spacefree(struct scull_pipe *dev)

{

if (dev->rp == dev->wp)

return dev->buffersize - 1;

return ((dev->rp + dev->buffersize -
dev->wp) % dev->buffersize) - 1;

}

static ssize_t scull_p_write(struct file *filp, const
char user *buf, size_t count,

loff_t
*f_pos)

{

struct scull_pipe *dev = filp->private_data; int
result;

if
(down_interruptible(&dev->sem)) return -ERESTARTSYS;

/* Make sure there's space to write */ result =
scull_getwritespace(dev, filp); if (result)

return result; /* scull_getwritespace called
up(&dev->sem) */

/*
ok, space is there, accept something */ count = min(count,
(size_t)spacefree(dev)); if (dev->wp >= dev->rp)

count = min(count, (size_t)(dev->end -
dev->wp)); /* to end-

of-buf */

else /* the write pointer
has wrapped, fill up to rp-1 */ count = min(count, (size_t)(dev->rp -
dev->wp - 1));

PDEBUG("Going to accept %li bytes to %p from
%p\n", (long)count, dev-

>wp, buf);

if (copy_from_user(dev->wp, buf, count))

{

up (&dev->sem); return -EFAULT;

}

dev->wp += count;

if (dev->wp == dev->end)

dev->wp =
dev->buffer; /* wrapped */ up(&dev->sem);

/* finally, awake any reader */

wake_up_interruptible(&dev->inq); /* blocked
in read() and select() */

/* and signal asynchronous readers, explained late in
chapter 5 */ if (dev->async_queue)

kill_fasync(&dev->async_queue,
SIGIO, POLL_IN); PDEBUG("\"%s\" did write %li
bytes\n",current->comm, (long)count); return count;

}

这个代码看来和
read 方法类似, 除了我们已经将睡眠代码放到了一个单独的函数, 称为 scull_getwritespace. 它的工作是确保在缓冲中有空间给新的数据, 睡眠直到有空间可
用. 一旦空间在, scull_p_write 可简单地拷贝用户的数据到那里, 调整指针, 并且唤醒 可能已经在等待读取数据的进程.

处理实际的睡眠的代码是:

/* Wait for space for writing; caller must hold
device semaphore. On

* error the semaphore will be released before
returning. */

static int scull_getwritespace(struct scull_pipe
*dev, struct file *filp)

{

while (spacefree(dev) == 0)

{ /* full */

DEFINE_WAIT(wait);

up(&dev->sem);

if (filp->f_flags
& O_NONBLOCK) return -EAGAIN;

PDEBUG("\"%s\" writing: going to
sleep\n",current->comm); prepare_to_wait(&dev->outq, &wait,
TASK_INTERRUPTIBLE); if (spacefree(dev) == 0)

schedule(); finish_wait(&dev->outq, &wait); if
(signal_pending(current))

handle it */

}

return
-ERESTARTSYS; /* signal: tell the fs layer to

if
(down_interruptible(&dev->sem)) return -ERESTARTSYS;

return 0;

}

再次注意
while 循环. 如果有空间可用而不必睡眠, 这个函数简单地返回. 否则, 它必 须丢掉设备旗标并且等待. 这个代码使用 DEFINE_WAIT 来设置一个等待队列入口并且
prepare_to_wait 来准备好实际的睡眠. 接着是对缓冲的必要的检查; 我们必须处理的情 况是在我们已经进入 while 循环后以及在我们将自己放入等待队列之前
(并且丢弃了旗 标), 缓冲中有空间可用了. 没有这个检查, 如果读进程能够在那时完全清空缓冲, 我们

可能错过我们能得到的唯一的唤醒并且永远睡眠. 在说服我们自己必须睡眠之后, 我们调 用 schedule.

值得再看看这个情况: 当睡眠发生在 if 语句测试和调用 schedule 之间, 会发生什么? 在这个情况里,
都好. 这个唤醒重置了进程状态为 TASK_RUNNING 并且 schedule 返回 -

- 尽管不必马上. 只要这个测试发生在进程放置自己到等待队列和改变它的状态之后, 事 情都会顺利.

为了结束, 我们调用 finish_wait. 对
signal_pending 的调用告诉我们是否我们被一个 信号唤醒; 如果是, 我们需要返回到用户并且使它们稍后再试. 否则, 我们请求旗标, 并 且再次照常测试空闲空间.

linux 手动睡眠的更多相关文章

  1. linux 手动源码安装lnmp(亲测)

    linux 手动源码安装lnmp笔记(亲测)<pre>先安装这2个yum install gccyum install g++</pre><pre>先在linux ...

  2. Linux 内核睡眠的几种方式

    译至:http://geeki.wordpress.com/2010/10/30/ways-of-sleeping-in-linux-kernel/ 在Linux中睡眠有2-3种不同的方法. 睡眠的第 ...

  3. linux 手动挂载硬盘没有移到回收站解决方法

    linux 手动挂载硬盘没有移到回收站解决方法 修改挂载硬盘的文件夹权限为当前用户即可 或者 修改读写权限 chmod 777 mount-disk-path

  4. Linux手动释放缓存的方法

    Linux释放内存的命令:syncecho 1 > /proc/sys/vm/drop_caches drop_caches的值可以是0-3之间的数字,代表不同的含义:0:不释放(系统默认值)1 ...

  5. linux 内核睡眠与唤醒

    休眠(被阻塞)的进程处于一个特殊的不可执行状态.进程休眠由多种原因,但肯定都是为了等待一些事件.事件可能是一 段时间从文件I/O读取更多数据,或者是某个硬件事件.一个进程还由可能在尝试获取一个已被占用 ...

  6. Linux手动搭建LAMP环境

    当你看到标题里的“手动搭建”,你是不是会想,难不成还有“自动搭建”?当然......不是,这里的“手动搭建”是指按部就班的搭建Apache.MySQL.PHP环境,是相对于集成软件包而言的.所以你是不 ...

  7. Linux手动添加swap分区

    转自:https://blog.csdn.net/whatday/article/details/51024571 为什么需要swap 根据Redhat公司的建议,Linux系统swap分区最适合的大 ...

  8. Linux手动安装新版本Python教程(CentOS)

    一.说明 1.1 linux为什么不升级python版本 2008年python3就发布了,到2020年1月1日python2.7就停止更新了,为什么主流的linux迟迟不去除python2自带pyt ...

  9. linux内核睡眠状态解析

    1. 系统睡眠状态 睡眠状态是整个系统的全局低功耗状态,在这种状态下,用户空间的代码不能被执行并且整个系统的活动明显被降低 1.1 被支持的睡眠状态 取决于所运行平台的能力和配置选项,Linux内核能 ...

随机推荐

  1. 跨境网上收款 找PayPal没错(php如何实现paypal支付)

    开发前准备 在我的博客中 有介绍如何获取ID 和 secret : 好了 在上一篇博客中详细介绍了也不少: 跨境网上收款 找PayPal没错(如何获取ID 和 secret) http://blog. ...

  2. Linux的登录和退出

    Linux是一个多用户的操作系统,用户要使用该系统,首先必须登录系统,使用完系统后,必须退出系统. 本章主要讨论登录和退出系统的方法: 用户登录系统时,为了使系统能够识别自己,必须输入用户名和密码,经 ...

  3. ajax嵌套陷阱

    ajax嵌套陷阱 $('.ajaxupd').click(function () { $('.shadow1').show(); $('.update').show(); var tds=$(this ...

  4. github中markdown语言的使用规则

    开始使用github就接触了markdown,确实如它的宗旨所言"易读易写",语法简洁明了,功能比纯文本更强,是一种非常适用于网络的书写语言.并且一大优点是兼容HTML,只要不在m ...

  5. 基于OPNET的路由协议仿真教程(AODV、OLSR 、DSR等)

    前言: 目前由于项目需要,学习了基于opnet的网络仿真方法,发现该软件的学习资料少之又少,所以将自己搜集到的学习资料进行整理,希望能帮助后来的人. 主要参考资料:OPNET网络仿真(清华陈敏版) 仿 ...

  6. 基于OSS+DataLakeAnalytics+QuickBI的Serverless的查询分析和可视化BI

    基于OSS的数据查询分析和可视化BI报表 数据存储在OSS后,有多种查询分析的方法,包括阿里云MaxCompute.DataLakeAnalytics产品等Severless查询分析服务,也可以自建S ...

  7. hdu1525 博弈

    /* n%m==0 n状态 n>=2*m 先手决定谁来面对当前的状态,并且可以知道状态,所以先手必胜. */ #include<stdio.h> int min(int x,int ...

  8. mogodb 修改字段属性

    修改为decimal类型 db.shopgoods.find({'Pricing.Detail':{$type:2}}).forEach(function(x){x.Pricing.Detail=Nu ...

  9. toString和valueOf使得对象访问时显示一个特定格式的字符串,但是可以进行数字运算

    作用 toString()的作用是返回一个反映这个对象的字符串; valueOf()的作用是返回它相应的原始值; 异同点 共同点:在 JavaScript 中,toString()方法和valueOf ...

  10. Docker Swarm:经济高效的容器调度

    本文探讨了几种容器调度策略,并以内存约束为例,讨论了如何利用Docker Swarm,通过资源约束实现容器的合理调度.其中,对容器资源的约束,包括硬约束和软约束,硬约束是指内存资源的实际限制条件,而软 ...