1.poll原理
-
poll用来做什么,就是用于在应用层select时候,驱动检查有没有数据可以读的函数。在很多函数里面都要实现。目的就是告诉应用层数据到了。
-
当select时,是否直接返回,还得取决于传入的fd的属性,如果属性是阻塞型,会使用驱动的poll函数做检查,有数据就会返回相应的掩码,如果没有,那么select就在会阻塞,当驱动层有数据到来时,会再一次的调用poll函数做检查,如果有数据了,那么立马会唤醒阻塞的进程或者线程。那么select就会返回。如果不阻塞,当poll检查没有数据时,会立即返回错误的掩码。
-
至于进程或者线程阻塞不是发生在驱动,而是发生在vfs层,因此在驱动的poll函数调用的poll_wait函数是没有阻塞进程的,只是将其加入一个队列而已。
-
对于在select是这样操作的,那么read函数是怎么操作的,如果没有数据可读,那么read函数也是分为阻塞和非阻塞的情况。在阻塞的情况下,会使用
wait_event_interruptible函数来等待条件发生。当条件发生了,驱动内的read函数会继续执行。把数据从内核层copy到用户层。非阻塞会立即返回错误。 -
需要说明的是,poll_wait函数并不阻塞,程序中poll_wait(filp, &outq,
wait)这句话的意思并不是说一直等待outq信号量可获得,真正的阻塞动作是上层的select/poll函数中完成的。select/poll会在一个循环中对每个需要监听的设备调用它们自己的poll支持函数以使得当前进程被加入各个设备的等待列表。若当前没有任何被监听的设备就绪,则内核进行调度(调用schedule)让出cpu进入阻塞状态,schedule返回时将再次循环检测是否有操作可以进行,如此反复;否则,若有任意一个设备就绪,select/poll都立即返回。2.select函数
intselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
- (1)intmaxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错。说明:对于这个原理的解释可以看上边fd_set的详细解释,fd_set是以位图的形式来存储这些文件描述符。maxfdp也就是定义了位图中有效地位的个数。
- (2)fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0得值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
- (3)fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
- (4)fd_set*errorfds同上面两个参数的意图,用来监视文件错误异常文件。
- (5)structtimeval*
timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第三,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即
select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述
3.代码实例
static unsigned char queue_flag = 0; // 队列的判断条件
wait_queue_head_t read_queue; // 等待队列定义
static unsigned char read_flag = 0; // 是否有有效数据
。。。。。
static ssize_t cdev_read(struct file* filp, char __user* buf, size_t count, loff_t* f_pos)
{
printk("wait event interruptible before\n");
wait_event_interruptible(read_queue, queue_flag);
printk("wait event interruptible after\n");
if(copy_to_user(buf, buffer, count))
{
return -EFAULT;
}
read_flag = 0;
return count;
}
static ssize_t cdev_write(struct file* filp, const char __user* buf, size_t count, loff_t* f_pos)
{
if(copy_from_user(buffer + *f_pos, buf, count))
{
return -EFAULT;
}
queue_flag = 1;
read_flag = 1;
wake_up(&read_queue);
return count;
}
static unsigned int cdev_poll(struct file* filp, poll_table* pt)
{
unsigned int mask = POLLIN | POLLRDNORM;
printk("poll wait before\n");
poll_wait(filp, &read_queue, pt);
printk("poll wait after\n");
/* 根据实际情况,标记事件类型 */
if(read_flag == 1)
{
printk("have write\n");
return mask;
}
/* 如果mask为0,那么证明没有请求事件发生;如果非零说明有时间发生 */
printk("no write\n");
return 0;
}
应用代码实例:
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<linux/rtc.h>
#include<linux/ioctl.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int fd;
void* readthread(void* arg)
{
char data[256];
fd_set rfds;
fd_set wfds;
int ret = 0;
while(1)
{
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
select(fd + 1, &rfds, &wfds, NULL, NULL);
if(FD_ISSET(fd, &rfds))
{
ret = read(fd, data, 3);
if(ret < 0)
{
printf("read error\n");
return (void*)-1;
}
printf("data: %c %c %c\n", data[0], data[1], data[2]);
}
}
return (void*)0;
}
int main(int argc, char *argv[])
{
char data[256];
int ret;
pthread_t tid;
printf("Hello World!\n");
fd = open("/dev/chrdev", O_RDWR);
if(fd < 0)
{
printf("open failed\n");
return -1;
}
pthread_create(&tid, NULL, readthread, NULL);
while(1)
{
ret = write(fd, "abc", 3);
if(ret < 0)
{
printf("write error\n");
close(fd);
return -1;
}
sleep(20);
}
close(fd);
return 0;
}
运行结果: