本文转载自http://blog.chinaunix.net/uid-23769728-id-3084737.html

这里所谓的长延时,是指其实现时间延时的粒度可以在HZ这一水准上。《深入Linux设备驱动程序内核机制》第8章"时间管理"中提到了好几种实现延时功能的机制,包括长延时短延时等,对每一种延时机制的优劣都有理论上的分析。

本帖旨在从另一个角度探讨一下其中所提到的“长延时”的三个实现方式,这些延时方式都试图让出处理器,换句话说都是基于非忙等待的实现(因为长延时若是忙等待,将极大浪费处理器的资源)与书中的原理分析不同之处在于,我希望在本帖中以实际的代码验证的方式来使那种理论上的分析更加直观化:我们在一个内核模块,比如demodev.ko的初始化函数中调用这些延时函数,然后通过ps命令来查看延时期间insmod进程的状态。

首先我们看第一种实现(为了有足够的时间让我们去查看进程的状态,我将这种“长延时”延时间隔设定30S,这样足够我们在此延时的窗口期间观察insmod进程的状态):

void delay_30s(void){
unsigned long j = jiffies + * HZ;
while(time_before(jiffies, j))
schedule();
}

这种实现采用了schedule的方式,貌似会重新调度一个新进程。其实因为当前进程状态没有改变,所以调用delay_30s的当前进程仍然处在CPU的运行队列中,进程状态为TASK_RUNNING.30s延时的目的肯定能达到了,但是该进程随时可能被调度器所调度,若是处理器的运行队列中只有这一个进程,CPU无法进入idle状态,而后者可以被电源管理模块所利用。如果我们insmod demodev.ko,那么通过ps命令来查看该进程状态时,会发现如下的输出信息:

root@build-server:/home/dennis/book_2nd_edition/book_sourcecode# ps aux | grep insmod
root      6491  100  0.0   4420   576 pts/2    R+   21:55   0:28 insmod demodev.ko
root      6514  0.0  0.0  13448   872 pts/3    S+   21:56   0:00 grep --color=auto insmod

insmod进程的R+状态意味着当前进程处在后台一个运行的状态下。

再来看一个改进版:

void delay_30s(void){
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout( * HZ);
}

这次在schedule_timeout调用之前,先把进程状态改成TASK_INTERRUPTIBLE。schedule_timeout()函数内部会启用一个内核timer,同时会调用schedule()。一旦schedule()函数被调用,当前进程将从处理器的运行队列中移走,移走的current进程会被放到内核定时队列的一个结点中。当30S的时间到期时,定时器上的函数会唤醒current,delay_30s函数将从schedule_timeout函数中返回。如果我们insmod demodev.ko,那么通过ps命令来查看该进程状态时,会发现如下的输出信息:

root@build-server:/home/dennis/book_2nd_edition/book_sourcecode# ps aux | grep insmod
root      6300  0.0  0.0   4420   580 pts/2    S+   21:53   0:00 insmod demodev.ko
root      6306  0.0  0.0  13448   868 pts/3    S+   21:53   0:00 grep --color=auto insmod

insmod进程的S+状态意味着当前进程处在后台一个睡眠的状态下。如果你的调用上下文允许当前进程进入睡眠,那么这是一个非常完美的实现:睡眠的进程不占用处理器资源,同时延时的目的也达到了。

最后,我们来试图改进第一种的那个方案,代码如下:

void delay_30s(void){
unsigned long j = jiffies + * HZ;
while(time_before(jiffies, j)){
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}

如果你在demodev.ko的初始化函数中调用了上面这个函数,insmod demodev.ko将回不到shell状态下了,ctrl+c也不行,如果换成TASK_INTERRUPTIBLE的话还可以用CTRL+C来杀死当前进程。这里的问题在于在schedule()前调用set_current_state(TASK_UNINTERRUPTIBLE),将导致当前进程从处理器运行队列中移开,一旦移开,current进程将处于流浪状态,谁会去收留它呢?它将永远失去被处理器调度的机会,不过此时若用ps aux来看一下insmod进程状态的话,会发现它处于S+状态,因为在schedule函数之前那个set_current_state的调用。

最后我想提一下那个schedule_timeout函数,如果调用该函数前没有用改变进程的状态,那么这个函数基本上是瞬间返回(其实取决于当前处理器运行队列及调度器行为),虽然它初始化并提交了一个定时器结点,但是它调用schedule时基本上是很快会被调度器再次调度,然后它有个del_singleshot_timer_sync(&timer)调用,后者将把之前提交的定时器结点从定时器管理队列中摘除,因为如果当前进程一旦结束,放到此前提交的定时器结点中的current将指向无意义的空间。

linux驱动之内核空间几种长延时的实现策略的优劣评估的更多相关文章

  1. Linux驱动的两种加载方式过程分析

    一.概念简述 在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用.静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新 ...

  2. Linux驱动的两种载入方式过程分析

    一.概念简述 在Linux下能够通过两种方式载入驱动程序:静态载入和动态载入. 静态载入就是把驱动程序直接编译进内核.系统启动后能够直接调用.静态载入的缺点是调试起来比較麻烦,每次改动一个地方都要又一 ...

  3. Linux 网络编程的5种IO模型:信号驱动IO模型

    Linux 网络编程的5种IO模型:信号驱动IO模型 背景 上一讲 Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 我们讲解了多路复用等方面的知识,以及有关例程. ...

  4. Linux驱动实践:你知道【字符设备驱动程序】的两种写法吗?

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...

  5. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

  6. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

  7. LINUX驱动、系统底层

    就业模拟测试题-LINUX驱动.系统底层工程师职位 本试卷从考试酷examcoo网站导出,文件格式为mht,请用WORD/WPS打开,并另存为doc/docx格式后再使用 试卷编号:143921试卷录 ...

  8. Linux驱动设计—— 中断与时钟

    中断和时钟技术可以提升驱动程序的效率 中断 中断在Linux中的实现 通常情况下,一个驱动程序只需要申请中断,并添加中断处理函数就可以了,中断的到达和中断函数的调用都是内核实现框架完成的.所以程序员只 ...

  9. linux驱动面试题整理

    1.字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的文件? 答:mknod命令结合设备的主设备号和次设备号,可创建一个设备文件. 评:这只是其中一种方式,也 ...

随机推荐

  1. 在CentOS下安装两个Tomcat

    在CentOS下安装两个Tomcat [版权声明:本文为博主原创文章,转载请说明出处.希望能和大家共同学习] 1.不同的tomcat启动和关闭监听不同的端口 2.不同的tomcat的启动文件start ...

  2. Cobbler 部署

    环境介绍 Cobbler 操作系统: Centos-7.2-x86_64 Cobbler服务器地址: 10.90.0.10 部署 Cobbler 安装 Centos epel 原 [root@node ...

  3. Android集成百度地图

    1. 百度地图api Android定位SDK Android地图SDK Android地图SDK<------

  4. link小图标以及表格的用法基础

    一.网页小图标的实现 实例: 实现方式: 效果: 二.表格基础 1.表格的组合标签 常用: table tr td caption ①table属性 border  边框 width  宽度 默认按照 ...

  5. spring boot 项目连接数据库查询数据过程

    spring boot 项目搭建 pom.xml <?xml version="1.0" encoding="UTF-8"?> <projec ...

  6. 记一次svg反爬学习

    网址:http://www.porters.vip/confusion/food.html 打开开发者工具后 页面源码并不是真实的数字,随便点一个d标签查看其样式 我们需要找到两个文件,food.cs ...

  7. Mybatis Plus中的lambdaQueryWrapper条件构造图介绍

  8. Servlet学习之Maven导入Servlet-api包与Tomcat冲突报500问题

    Maven导入Servlet-api包导致无法运行报500的问题 以下解决方式适用于跟我类似的配置环境,构建servlet程序后,启动Tomcat页面报"类com.tioxy.servlet ...

  9. zookeeper 源码编译

    环境:mac 1.github上下载 源码 项目地址:https://github.com/apache/zookeeper 2.安装 ant mac:brew update ->  brew ...

  10. luogu P4284 [SHOI2014]概率充电器 期望 概率 树形dp

    LINK:概率充电器 大概是一个比较水的题目 不过有一些坑点. 根据期望的线性性 可以直接计算每个元件的期望 累和即为答案. 考虑统计每一个元件的概率的话 那么对其有贡献就是儿子 父亲 以及自己. 自 ...