Chinaunix首页 | 论坛 | 博客
  • 博客访问: 521589
  • 博文数量: 51
  • 博客积分: 345
  • 博客等级: 民兵
  • 技术积分: 534
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-21 12:02
个人简介

文章分类

全部博文(51)

文章存档

2023年(2)

2022年(1)

2021年(7)

2020年(10)

2019年(2)

2016年(20)

2015年(5)

2014年(1)

2011年(3)

我的朋友

分类: LINUX

2015-09-01 13:50:55

    驱动会存在一个等待队列,专门为监听某一事件存在,该等待队列存放需要被唤醒的进程;
poll函数会注册一个wait_queue_t 结构到对应的队列上,当条件满足时,调用wake_up,阻塞的队列就会被唤醒进行调度;

    wait queue是Linux内核里的基本功能,以队列为基础数据结构,与进程的调度有密切关系;能够实现核心的异步通知机制; 等待对队列也可以用来同步对资源的访问,Linux信号量机制也是由等待队列来实现的。 
poll(system V)select(BSD)用来查询设备的状态,以便用户程序获知是否对设备进行非阻塞访问;都需要驱动程序的poll函数支持。

     重要的API,void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)

最终调用 __pollwait
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
                    poll_table *p)
{
     struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);     //poll_wqueues 管理结构
     struct poll_table_entry *entry = poll_get_entry(pwq);     //在管理结构上申请一个表项
     if (!entry)
          return;
     get_file(filp);
     entry->filp = filp;
     entry->wait_address = wait_address;     //等待队列
     entry->key = p->_key;
     init_waitqueue_func_entry(&entry->wait, pollwake);     //唤醒后执行函数pollwake
     entry->wait.private = pwq;          //私有数据指向管理结构
     add_wait_queue(wait_address, &entry->wait);    //添加entry->wait到wait_address等待队列
}

加入到等待队列;

poll_wait函数并不阻塞,真正阻塞是在上层的select/poll函数中完成。select/poll会在一个循环中对每一个需要监听的设备调用它们自己的poll支持函数以使得当前进程被加入到各个设备的等待列表;若当前没有任何被监听的设备就绪,则内核进行调度,让出CPU进入阻塞状态,schedule返回时将再次循环检查是否可以进行操作,如此反复,若有任意一个就绪,select/poll返回;


阻塞是指在执行操作时,若获取不到资源,则进程挂起直到满足条件再进行操作;
非阻塞进程是在不能进行设备操作时,并不挂起;


*********************************************************************
fasync异步通知
异步通知类似于中断机制,设备驱动发送一个信号给内核,告知内核有数据可读,在条件不满足之前不会造成阻塞; poll和阻塞性IO在条件不满足时会造成阻塞;

用户态:
signal(SIGIO, &input_handler); /* dummy sample; sigaction() is better */
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
做三件事情:
1、指定SIGIO的处理函数;
2、指定一个进程作为文件的属主(filp->owner),告知内核信号应该发给哪个进程;
3、设备文件中添加FASYNC标记,驱动中就会调用对应的fasync函数
struct file_operations {
struct module *owner;
     loff_t (*llseek) (struct file *, loff_t, int);
     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
......

         int (*fasync) (int, struct file *, int);
......
}


内核态:
信号处理流程,及设置文件属主的操作内核处理是通用的;

与poll类似,存在一个管理结构 struct fasync_struct,同样以队列形式来管理;当条件满足时,内核会调用kill_fasync( )函数,依次向异步队列结构对象发送SIGIO信号;
struct fasync_struct {
     spinlock_t          fa_lock;
     int               magic;
     int               fa_fd;
     struct fasync_struct     *fa_next; /* singly linked list */
     struct file          *fa_file;
     struct rcu_head          fa_rcu;
};

fasync_helper():
添加异步对象;  fapp由驱动设备管理,每个驱动设备对应一个fapp,该设备所有异步对象挂在其链上;
/*
* fasync_helper() is used by almost all character device drivers
* to set up the fasync queue, and for regular files by the file
* lease code. It returns negative on error, 0 if it did no changes
* and positive if it added/deleted the entry.
*/
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
     if (!on)    //on为0时,表示删除异步对象
          return fasync_remove_entry(filp, fapp);
     return fasync_add_entry(fd, filp, fapp);    //on为1时,表示添加异步对象 
}

kill_fasync():
发送信号到内核,唤醒上层进程;依次向异步队列结构对象发送SIGIO信号;
kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN);
POLL_IN 表示设备可读;POLL_OUT:表示设备可写

异步通知相关的数据结构和struct wait_queue是一致的,这两种情况都涉及等待同一个事件,区别是这个struct file被用来替代struct task_struct。

阅读(1450) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~