本文转载自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. Invalid RNPermission 'ios.permission.xxx'. should be one of: ( )

    原因可能是配置配置问题, 我碰到的是Android上完美运行,iOS报错,原因是前期用的Android开发,iOS的配置项没有配完整 按照官方配置一遍 https://github.com/react ...

  2. http连接,缓存,cookie,重定向,代理

    早期的HTTP协议使用短连接,收到响应后就立即关闭连接,效率很低:   HTTP/1.1默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率:   服务器会发送“Connection:     ...

  3. 记一次css字体反爬

    前段时间在看css反爬的时候,发现很多网站都做了css反爬,比如,设置字体反爬的(58同城租房版块,实习僧招聘https://www.shixiseng.com/等)设置雪碧图反爬的(自如租房http ...

  4. Java基础-语法基础

    一.Java中的关键字和保留字 关键字:某种语言赋予了特殊含义的单词 保留字:没有赋予特殊含义,但是准备日后要使用的单词 二.Java中的标识符 其实就是在从程序中自定义的名词.比如类名.变量名,函数 ...

  5. Python pass语句

    Python pass语句:空语句,主要用于保持程序结构的完整性 或者 函数想要添加某种功能,但是还没有想好具体应该怎么写. 在 for 循环中使用 pass: lst = [7,8,9,4] for ...

  6. Excel绘制动态图表 之 极品offset、多种控件动态动图

    1.案例1:辅助区域动态图 动态按钮“投资金额”的制作: "数据"菜单下"数据工作”组中的“数据验证”,选择"序列". 2. OFFSET ——函数中 ...

  7. PHP array_shift() 函数

    实例 删除数组中的第一个元素(red),并返回被删除的元素: <?php $a=array("a"=>"red","b"=> ...

  8. PHP printf() 函数

    实例 输出格式化的字符串: <?php高佣联盟 www.cgewang.com$number = 9;$str = "Beijing";printf("There ...

  9. Blob分析之ball_seq.hdev

    * ball_seq.hdev: Inspection of Ball Bonding * 关闭更新dev_update_off ()*图像集合ImageNames := 'die/' + ['die ...

  10. 记录一次线上实施snmp

    公司要实施一个部级的项目,我们公司的提供的产品要对接下客户的一个平台监控平台,该监控平台使用snmp,我们公司的产品不支持snmp,所以由我负责在现网实施snmp,记录这次现网 一.生成编译规则 1. ...