操作延时
一,长延时
1, 忙等待--通过监听jiffies值得变化
延时1s
unsigned j=jiffies+1;
while(time_before(jiffies , j)){
cpu_relax();//nothing to do;
}
分析:
1》 非抢占设备上:在循环延时等待期间会整个锁住处理器;调度器从来不会抢占运行在内核空间的进程;
2》 抢占设备上: 如果没有拥有锁的话,处理器还可以运行其他进程;但是忙等待任然有些浪费,特别是不能禁止中断,如果禁止中断,jiffies 值不会变化,机器就会永远的等待,除非按下关机键切断电源;
3》在低负荷设备上运行良好:每次延时1s,每次系统调用都能立即执行,两次系统调用之间的时间极短;
4》在高负荷设备上运行分两种情况:
a,非抢占设备:如上述缺点,在延时等待期间会整个锁住处理器,所以每个系统调用的延时为1s,但是每次系统调用都不会立即执行,在两次系统调用之间有较长的时候,而且随负荷增加越来越明显,这是因为多任务处理器时间是共享的,在两次系统调用之后,调度器会把处理器时间分配给其他进程;
b,抢占设备:如上述特点,由于在延时期间可能被中断所以延时时长可能增加,两次系统调用之间的时间不是进程调度的唯一选择所以两次系统调用之间的时间可能会缩短;
2,在忙等待的基础上让出处理器;
unsigned long j=jiffies+1;
while(time_before(jiffies,j)){
schedule();
}
分析:
理论上性能好点,但是也没有实质性的优化;
1》在低负荷设备上运行:由于schedule()仅仅让出处理器,但是等待进程还在运行队列中,所以在没有其他进程的时候,延时循环进程刚让出处理器紧接着又被调度,所以实际上在延时等待的时候还是一直使用处理器,只要运行就会耗电等消耗资源;
2》在高负荷设备上:
a,非抢占设备:这个过程和上述忙等待中高负荷--非抢占设备运行过程一致;
b,抢占设备: 这个和上述忙等待高负荷--抢占设备运行过程基本一直,但是这个在延时等待过程中增加了进程调度的时间,当延时时间到期时,由于进程可能不会被立即调度执行,所以延时时间可能会延长,而且延长的时间随设备符合增加越来越明显;
综上两种延时方案(监听jiffies忙等待,让出处理器)整体性能都不太好;所以内核为驱动程序提供了第三种延时方案;
3,超时等待
a,unsigned long wait_event_interruptible_timeout(wait_queue_head_t wait ,condition , long jiffies);
b,unsigned long wait_event_timeout(wait_queue_head_t wait , condition , long jiffies);
返回值 等待时间到期或延期都返回0,等待过程被中断返回剩余时长,用无符号unsigned long 来表示剩余时长(即始终中断次数);
上述api都是在某个队列上等待某个事件;有时需要一种特殊情况,就是不在任何队列上等待任何事件的延时,内核提供了这样的特殊情况即就是直接使用上述api等底层实现原理(schedule_timeout(unsigned jiffies)),在调度之前一定要修改进程状态,否则进程一直在运行队列所以和上述让出处理器方案一样:
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(jiffies);
分析:超时等待过程进程不在运行队列,所以不会占用处理器,所以不受抢占与非抢占影响,基本也不受符合高低的影响,但是有一点,就是在等待过程中延时时间已到期进程可能不会立即被调度,所以延时时间可能延长延长时间就是等待被调度的时间,但是这都不是问题,因为在实际使用中设备基本都强调的是至少延时多长时间;
二,短延时
有时要求延时时长非常短,内核提供了下面几个api来满足短延时;
a,void mdelay(usinged long millisecs);
b,void udelay(usigned long usecs);
c,void ndelay(usinged long nsecs);
重点需要注意的是上述单个函数都是忙等待;
d,void msleep(unsigned int millisecs);//延时的毫秒数;
e,unsigned long msleep_interruptible(unsigned int millisecs);//可中断延时的毫秒数;
f,void ssleep(unsigned seconds);//延时的秒数;