在linux驱动学习的过程中,会遇到同步互斥、异步通信、poll机制、阻塞和非阻塞的这些概念,对于这些概念在什么时候引入来讲述比较好呢?下面只讲述poll机制和异步通信。
对于按键驱动,如何去返回按键值成为一个比较有意思事情。首先可以查询按键的接口返回是否按下的状态值,也可以利用中断去获取按键值。对于不断的查询和中断去获取按键值,明显可以知道中断方式所耗费的资源是比较小的。但是上面讲的实现只是驱动程序如何去返回这个按键值给应用程序,对于应用程序来说,去获取按键值只能不断的去查询了。这样依然使得CPU占用比较大。
那么有什么方法可以让CPU占用率下降呢?
嗯,想想,假设应用程序设计成几秒钟起来读一下,其他时间休息(休眠),这样可以减低了?也可以当有数据的时候由驱动来告诉应用程序来读一下,这样不就能节省了?
所以在这样首先引入一个poll机制,poll机制本质是对一个打开的文件进行监听,如果有数据就返回有可读数据,如果没有就休息(休眠)若干秒再次读取。
有了这个poll机制,那么如何去完善这个按键的驱动和应用程序。在学驱动前见过file_operation,file_operation里面有个poll的函数指针,在一个简单的对应的关系可以知道应用程序调用poll就会调用到驱动的poll函数。
那么对应于驱动的poll函数需要完成下面:
1、使用pol_wait把进程挂到等待队列上去。突然,有人就会说为什么要放入等待队列,什么是等待队列?
进程常常需要去等待一些事件发生,以便确定将如何运行。那么等待队列实现了在事件上的条件等待。等待特定事件的进程放入合适的等待队列中,并放弃控制。因此,等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。
2、确定相关的打开的文件是否有数据可读。有的话就返回1,否则0
到了这里对于驱动的poll机制就写了,接下来只要完成应用程序对于poll一个调用就行了。
好第一种方式如何去写,大概有个眉头,那么就进去把代码实现出去。代码实现如下:
驱动部分:
static DECLARE_WAIT_QUEUE_HEAD(button_wait); //分配等待队列
static unsigned button_poll(struct file *file, poll_table *wait) //驱动poll函数实现
{
unsigned res = 0;
poll_wait(file, &button_wait, wait);
if(condition)
res = POLLIN | POLLRDNORM;
return res;
}
应用程序部分:
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
while(1)
{
if(!(poll(fds,1,5000)))
{
printf("time out\n");
}
else
{
read(fd,&val,1);
printf("val is 0x%x\n",val);
}
}
对于第二种方式,由驱动来告诉应用程序有数据读就是异步通知;在讲这个异步通知先插讲下信号,当一个程序先另一个程序发送一个信号,如果此程序能够接受和有对应的处理,那么就能去执行。那么异步通知就可以采用这种信号的方式来通知应用程序,当按键按下有数据可以读,驱动就会发出一个信号去通知应用程序。
那么如何这个信号怎么来的,怎么去发,发给谁,这就需要去弄明白了。
1、一个进程都是拥有一个进程ID,那么发给谁就知道了,发给应用程序的进程ID就行了。
2、在应用程序可以去注册一个信号,那么发什么信号也是知道了。
3、在驱动可以使用函数kill_fasync来发送。
在应用程序需要注册一个信号,还有完成对应信号的处理函数,等待信号就行了。
对于驱动程序需要添加fasync函数,那么驱动程序发送信号需要应用程序ID,如何去获取这个ID又是需要去解决的问题,对于内核中提供了fasync_helper函数来帮助去设置。
到了这里就可以写一个有异步通知功能的驱动程序。
下面是驱动部分代码:
struct fasync_struct *fasync; //定义一个fasync_struct结构体
static int button_fasync(int fd, struct file *file, int on)
{
/* No locking - fasync_helper does its own locking */
return fasync_helper(fd, file, on, &fasync);
}
然后再有数据到来后使用:
/* 发送信号给应用程序 */
kill_fasync(&fasync, SIGPOLL,POLL_IN);
对于APP:
/* 信号处理函数 */
void do_signal(int signum)
{
unsigned char val=0;
read(fd,&val,1);
printf("key value = 0x%x\n",val);
}
int main(int argc, char **argv)
{
int flag;
/* 申请信号SIGIO */
signal(SIGIO,do_signal);
fd = open("/dev/myenit",O_RDWR);
if(fd < 0)
{
printf("can't open the button_dev\n");
}
fcntl(fd,F_SETOWN,getpid());
flag = fcntl(fd,F_GETFL);
/* 每当fb的标志FASYNC被修改就能调用驱动的fasync函数 */
fcntl(fd,F_SETFL,flag | FASYNC);
while(1)
{
sleep(1000);
}
return 0;
}
对于按键驱动,如何去返回按键值成为一个比较有意思事情。首先可以查询按键的接口返回是否按下的状态值,也可以利用中断去获取按键值。对于不断的查询和中断去获取按键值,明显可以知道中断方式所耗费的资源是比较小的。但是上面讲的实现只是驱动程序如何去返回这个按键值给应用程序,对于应用程序来说,去获取按键值只能不断的去查询了。这样依然使得CPU占用比较大。
那么有什么方法可以让CPU占用率下降呢?
嗯,想想,假设应用程序设计成几秒钟起来读一下,其他时间休息(休眠),这样可以减低了?也可以当有数据的时候由驱动来告诉应用程序来读一下,这样不就能节省了?
所以在这样首先引入一个poll机制,poll机制本质是对一个打开的文件进行监听,如果有数据就返回有可读数据,如果没有就休息(休眠)若干秒再次读取。
有了这个poll机制,那么如何去完善这个按键的驱动和应用程序。在学驱动前见过file_operation,file_operation里面有个poll的函数指针,在一个简单的对应的关系可以知道应用程序调用poll就会调用到驱动的poll函数。
那么对应于驱动的poll函数需要完成下面:
1、使用pol_wait把进程挂到等待队列上去。突然,有人就会说为什么要放入等待队列,什么是等待队列?
进程常常需要去等待一些事件发生,以便确定将如何运行。那么等待队列实现了在事件上的条件等待。等待特定事件的进程放入合适的等待队列中,并放弃控制。因此,等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。
2、确定相关的打开的文件是否有数据可读。有的话就返回1,否则0
到了这里对于驱动的poll机制就写了,接下来只要完成应用程序对于poll一个调用就行了。
好第一种方式如何去写,大概有个眉头,那么就进去把代码实现出去。代码实现如下:
驱动部分:
static DECLARE_WAIT_QUEUE_HEAD(button_wait); //分配等待队列
static unsigned button_poll(struct file *file, poll_table *wait) //驱动poll函数实现
{
unsigned res = 0;
poll_wait(file, &button_wait, wait);
if(condition)
res = POLLIN | POLLRDNORM;
return res;
}
应用程序部分:
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;
while(1)
{
if(!(poll(fds,1,5000)))
{
printf("time out\n");
}
else
{
read(fd,&val,1);
printf("val is 0x%x\n",val);
}
}
对于第二种方式,由驱动来告诉应用程序有数据读就是异步通知;在讲这个异步通知先插讲下信号,当一个程序先另一个程序发送一个信号,如果此程序能够接受和有对应的处理,那么就能去执行。那么异步通知就可以采用这种信号的方式来通知应用程序,当按键按下有数据可以读,驱动就会发出一个信号去通知应用程序。
那么如何这个信号怎么来的,怎么去发,发给谁,这就需要去弄明白了。
1、一个进程都是拥有一个进程ID,那么发给谁就知道了,发给应用程序的进程ID就行了。
2、在应用程序可以去注册一个信号,那么发什么信号也是知道了。
3、在驱动可以使用函数kill_fasync来发送。
在应用程序需要注册一个信号,还有完成对应信号的处理函数,等待信号就行了。
对于驱动程序需要添加fasync函数,那么驱动程序发送信号需要应用程序ID,如何去获取这个ID又是需要去解决的问题,对于内核中提供了fasync_helper函数来帮助去设置。
到了这里就可以写一个有异步通知功能的驱动程序。
下面是驱动部分代码:
struct fasync_struct *fasync; //定义一个fasync_struct结构体
static int button_fasync(int fd, struct file *file, int on)
{
/* No locking - fasync_helper does its own locking */
return fasync_helper(fd, file, on, &fasync);
}
然后再有数据到来后使用:
/* 发送信号给应用程序 */
kill_fasync(&fasync, SIGPOLL,POLL_IN);
对于APP:
/* 信号处理函数 */
void do_signal(int signum)
{
unsigned char val=0;
read(fd,&val,1);
printf("key value = 0x%x\n",val);
}
int main(int argc, char **argv)
{
int flag;
/* 申请信号SIGIO */
signal(SIGIO,do_signal);
fd = open("/dev/myenit",O_RDWR);
if(fd < 0)
{
printf("can't open the button_dev\n");
}
fcntl(fd,F_SETOWN,getpid());
flag = fcntl(fd,F_GETFL);
/* 每当fb的标志FASYNC被修改就能调用驱动的fasync函数 */
fcntl(fd,F_SETFL,flag | FASYNC);
while(1)
{
sleep(1000);
}
return 0;
}