阻塞IO,异步通知,中断,时间定时器(Linux驱动2)

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之后

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值