1 阻塞和非阻塞IO
说明:
阻塞IO,当资源不可用时,进程就阻塞住.非阻塞IO,当资源不可用时,进程不被阻塞.
系统通过等待队列实现阻塞IO,等待队列的调度与系统调度有关.
非阻塞IO,需要用户不断查询状态.用户侧通过select,FD_SET等宏来实现,驱动侧需要实现poll函数.
变量
wait_queue_head_t head
wait_queue_t wait
函数
DECLARE_WAIT_QUEUE_HEAD(name)
DECLARE_WAITQUEUE(name,tsk)
init_waitqueue_head(&head)
wait_event(&head,condition)
wake_up(&head)
add_wait_queue(&head,&wait)
remove_wait_queue(&head,&wait)
wait_event_interruptible
wait_event_timeout
wait_event_interruptible_timeout
wake_up_interruptible
用法:
阻塞IO
1、 xx_device{
cdev cdev;
...
wait_queue_head_t queue;//在设备定义中条件等待队列头.
}
2、处理阻塞的地方
DECLEAR_WAITQUEUE(wait,current);
add_wait_queue(dev->queue,&wait)
...
if(!available){//资源不可用
__set_current_state(TASK_INTERRUPTIBLE);//设置浅睡眠状态
if(filep->flags & O_NOBLOCK){//非阻塞方式打开
ret= -EAGAIN;
goto out;
}
schedule();
if(signal_pending(current)){//被其他信号唤醒
ret=-ERESTARTSYS;
goto out;
}
}
处理正常事务...
out:remove_wait_queue(dev->queue,&wait);//移除等待队列
__set_current_state(TASK_RUNNING);//设置进程状态
return ret;
别的函数
当允许访问资源时
wake_up_interruptible(&dev->queue);
非阻塞IO
1、用户侧
fd_set rd,wt;
FD_ZERO(&rd);FD_ZERO(&wt)
FD_SET(fd,&rd);FD_SET(fd,&wt);
....
select(fd+1,fd_set *rd,fd_set*wt,fd_set*exp,struct timeval *time);
if(FD_ISSET(fd,&rd))
do read work
if(FD_ISSET(fd,&wr))
do write work
2、驱动侧
xx_poll(struct file *filep,poll_table *wait)
{
dev=filep->private_data;
poll_wait(dev->rd_queue,wait);
poll_wait(dev->wr_queue,wait);
if(可读)
mask |=POLLIN|POLLRDNORMAL;
if(可写)
mask |=POLLOUT|POLLWRNORMAL;
return mask;
}
struct file_operations xx_fops={
...
.poll=xx_poll;
}
2 异步通知
说明:
用户侧通过设置回调函数,当资源可用时,通过回调函数的形式,异步通知用户侧.相当于系统对用户侧的中断.
变量:
fasync_struct
typedef void(*sighandler_t)(int);
函数:
用户侧
signal(undigned long no,sighandler_t handler);//注册回调函数
fcntl(fd,cmd,data)
驱动侧
fasync_helper
kill_fasync
用法:
用户侧
xx_handler(int)
{
...
}
signal(fd,handler)
fcntl(fd,F_SETOWN,getpid());//设置own,让驱动侧知道向哪里发送消息
flag=fcntl(fd,F_GETFL);
fcntl(fd,F_SETFL,flag|FASYNC);设置驱动侧fasync标志
驱动侧
设备结构体中定义fasync_struct
xx_dev{
cdev cdev;
...
struct fasync_struct *fasync_queue;
}
实现xx_fasync函数
xx_fasync(int fd,struct file *filep,int mode)
{
fasync_helper(fd,filep,mode,&dev->fasync_queue);//调用fasync_helper函数
}
在资源可用时调用kill_fasync函数
if(available)
kill_fasync(&dev->fasync_queue,sig,POLL_XX);
在release中,注销函数
xx_fasync(-1,filep,0);
3 中断
说明:
中断用于打断当前程序.在linux中为取得效率的平衡,采用下半部处理方式,处理中断.下半部处理方式有tasklet,work,softirq.中断程序中上半部处理需要紧急处理部分,下半部处理不需要紧急处理部分.上下部处理根据情况来选择,不一定一定要.
tasklet和softirq工作于原子上下文,工作队列工作于进程上下文,所以工作队列可以阻塞,而tasklet和softirq不能有阻塞.
变量:
tasklet_t tsklet
work_struct wk
DECLARE_TASKLET(tsklet,tsklet_fun,data)//联系结构与下半部处理函数
函数:
register_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char * name,void *dev_id)
free_irq(unsigned int irq,void * dev_id);
disable_irq(int irq);//需要等待中断处理完
enable_irq(int irq);
disable_irq_nosync(int irq);//不需要等待中断处理完
tasklet_schedule(xx_tsk);
schedule_work(xx_wk);
INIT_WORK(wk,xx_work_fun,data);//联系工作队列中的结构与处理函数
用法
xx_init()
{
request_irq(irq,xx_irqhandler,flags,”xx_irq”,NULL);
if you want use work in bh
INIT_WORK(&xx_wk,work_fun,NULL);
}
xx_irqhandler(int irq,void dev_id,struct pt_regs regs)
{
…
schedule_work(&xx_wk);
or
tasklet_schedule(&xx_tsk);
return IRQ_HANDLED;
}
xx_exit()
{
…
free_irq(irq,NULL);
}
如果想用tasklet下半部
void xx_tasklet_fun(unsigned int)
{
}
DECLARE_TASKLET(xx_tsk,xx_tasklet_fun,NULL)
扩展:
local_irq_eanble,local_irq_disable//允许,禁止本地cpu中断
共享中断:
说明:
多个设备,共享一个硬件中断线的情况.共享中断需要将flags设置成IRQF_SHARED,系统会遍历所有共享中断的处理函数,直到返回IRQ_HANDLED.在上半部,应当读取中断状态,如果不是属于本身的中断应当立即返回IRQ_NONE;
4时间定时器:
说明:
时间定时器最终依赖硬件定时器来工作,当时钟中断发生时,系统会检测各个时间定时器,到期的时间定时器函数作为软中断在底半部执行.
变量:
timer_list timer
HZ 每秒钟中断次数,一般100
jiffies 当前中断次数.一秒钟有hz个jiffies.
函数:
init_timer(&timer)
add_timer(&timer)
del_timer(&timer)
mod_timer(&timer,unsigned long expries)
DEFINE_TIEMER(name,fun,expires)
TIMER_INITIALIZER(name,fun,dara)
msecs_to_jiffies 将毫秒转换成jiffies
用法:
sturct xx_dev={
struct cdev cdev;
…
struct timer_list xx_timer;
}
struct xx_dev dev;
xx_init()
{
init_timer(&dev.xx_timer);
dev.xx_timer.function=do_timer;
dev.xx_timer.data=..
dev.xx_timer.expires=jiffies+时间间隔;
}
xx_exit()
{
del_timer(&dev.xx_timer);
}
do_timer(unsigned long arg)
{
mod_timer(&dev.xx_timer,新时间)
或者
dev.xx_timer.expires=新时间
add_timer(&dev.xx_timer);
}
扩展:delay_work
说明:
对于周期性任务,还可以用delay_work来完成.
变量:
struct delay_work work;
函数:
schedule_delay_work(struct delay_work *,unsigned long dealy)
用法:
struct delay_work work;
xx_fun1()
{
schedule_delay_work(&work,delay);
}
xx_do_work(unsigned long time)
{
schedule_delay_work(&work,delay);
}
5延迟:
说明:
延迟有忙延迟,休眠延迟.忙延迟占用cpu时间,休眠延迟不占用cpu时间.
函数:
ndelay
udelay
mdelay
msleep 不可被打断
msleep_interruptiable 可被打断
ssleep
timer_before(a,b) a在b之前
timer_after(a,b) a在b之后