“小王,听说过如虎添翼吧..”我拍拍下王的头说。
“还如虎添翼你,为你前次提的几个东东,我是头上长包..”小王愤慨地瞪着我。
“啊,为啥这样呢,原本还特意拒绝了MM的约会,抽出时刻计划给你说点高档的东东,看来现在是不行了”我吃惊道,“这样吧,这次就给你讲些和前边有关的东西,也不失为如虎添翼不是?”。
“好,我也是这么计划的,便是没好意思说,今日讲些啥呢?”小王昏暗的眼光总算闪了闪。(为啥这么难过呢,如同跟什么会嚎叫的特像,哈哈)
那就言归正传,今日我们讲—Linux设备驱动程序之异步告诉与异步I/O.”
小王,前边不是讲了堵塞与非堵塞拜访,poll()函数等供给的较好的处理设备拜访的机制,那么经过这次有关异步告诉整套机制的合作,就更相辅相成,如虎添翼了。
啥叫异步告诉:很简略,一旦设备准备好,就自动告诉运用程序,这种情况下运用程序就不需求查询设备状况,这是不是特像硬件上常提的“中止的概念”。上边比较精确的说法其实应该叫做“信号驱动的异步I/O”,信号是在软件层次上对中止机制的一种模仿。
“小王,给你一个体现的机遇,说说这个和前边的几点不同和差异。。”
“嗯,我的了解是这样的哈,堵塞I/O意味着一向等候设备可拜访再拜访,非堵塞I/O意味着运用poll()来查询是否可拜访,而异步告诉则意味着设备告诉运用程序自身可拜访。”看着小王聪明的眼睛和明晰的思路,我也不由得给予一个鼓舞的浅笑啊。
说的好,我仅仅想着重一点:上面三种方法,其实自身是没有好坏的,应该依据不同的运用场景合理挑选算了。
提到信号,在运用程序中,为了捕获信号(还捕获呢, 不便是一个处理吗)能够运用signal()函数来设置对应的信号的处理函数。函数原型是
void (*signal(int signo,void (*func)(int))) (int) 这个看起来费力吧,不但你,我看着也费力,不要紧,给你来个比如:
void sigterm_handler(int signo)
{
char data[MAX_LEN];
int len;
len=read(STDIN_FILENO, &data,MAX_LEN);
data[len]=0;
printf("Input available:%s\n",data);
exit(0);
}
int main(void)
{
int oflags;
//发动信号驱动机制
signal(SIGIO, sigterm_handler);
fcntl(STDIN_FILENO, F_SETOWN, getpid());
oflags = fcntl(STDIN_FILENO, F_GETFL);
fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
//树立一个死循环,避免程序完毕
while(1);
return 0;
}
看了这段代码理解啥意思了吧,我也不多少了,我们持续往下走..为了一个用户在用户空间中能处理一个设备开释的信号,它有必要完结一下3份作业:
1)经过F_SETOWN操控指令设置设备文件的具有者为本进程,这样从设备驱动中宣布的信号才干被本进程收到。
2)经过F_SETFLIO操控指令设置设备文件支撑FASYNC,即异步告诉形式。
3)经过signal()链接信号和信号处理函数。
当然,假如你了解linux/Unix信号机制的话,你可能会问为啥没说sigaction函数,其实不要紧,效果差不多,想知道的话,自己看书Apue的P261.
有了信号的发送,那么就必定得有信号的开释了:
在设备驱动和运用程序的异步告诉交互中,仅仅在运用程序端捕获信号是不行的,由于信号没有的源头是在驱动端,因而要在恰当的机遇让设备驱动开释信号。
为了使设备支撑异步告诉机制,驱动程序中触及三个操作:
1)支撑F_SETOWN指令,能在这个操控指令处理中设置filp->f_owner为对应的进程ID。不过此项作业已由内核完结,设备驱动无须处理。
2)支撑F_SETFL指令的处理,每逢FASYNC标志改动时,驱动程序中fasync()函数将得以进行。因而,驱动程序有必要完成fasync()函数。
3)在设备资源可获得时,调用kill_fasync()函数激起相应的信号。
驱动程序中上面的三步是和运用程序是一一对应的。如下图:
设备驱动中异步告诉编程仍是比较简略的,首要便是一些数据结构,和两个函数:
数据结构:fasync_struct结构体
函数:1)处理FASYNC标志改变的函数int fasync_helper(int fd, struct file *filp, int mode ,struct fasync_struct **fa);
2) 开释信号用的函数void kill_fasync(struct fasync_struct **fa, int sig, int band);
和其他设备驱动相同,一般将fasync_struct放到设备结构体中。下边是典型模版:
struct xxx_dev
{
struct cdev cdev;
…
struct fasync_struct *async_queue; //异步结构体
}
而在驱动的fasync()函数中,只需求简略的将该参数的3个参数以及fasync_struct结构体指针的指针作为第四个参数传给fasync_helper函数即可.下边是典型模版:
static int xxx_fasync(int fd, struct file *filp, int mode)
{
struct xxx_dev *dev = filp->private_data;
return fasync_helper(fd,filp,mode,&dev->async_queue);
}
一旦设备资源能够获得时,应该调用kill_fasync()开释SIGIO信号,可读时第三个参数设置为POLL_IN,可写时第三个参数设置为POLL_OUT,下边是开释信号的典型模版:
static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_ops)
{
struct xxx_dev *dev = filp->private_data;
….
//发生异步信号
if(dev->async_queue)
{
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
..
}
最终,在文件封闭时,即在设备驱动的release函数中,应调用设备驱动的fasync()函数将文件从异步告诉的列表中删去,下边是设备驱动的开释函数的典型模版:
static int xxx_release(struct inode *inode, struct file *filp)
{
struct xxx_dev *dev = filp->private_data;
//将文件从异步告诉列表中删去
xxx_fasync(-1,filp,0);
…
return 0;
}
“等等,我知道你理解,你是想向我个比如,我知道你啥意思,不要紧,我下次补上 。”没等小王,我马上给她堵上“你啊,心里几根小九九,我还不知道啊..”