Linux设备驱动开发-linux驱动中的非阻塞访问方式

前面已经详细分析过了阻塞访问方式,下面就来继续分析一下非阻塞的访问方式。

什么是非阻塞的访问方式呢?非阻塞操作的进程在不能进行设备操作时,并不挂起,他或者是放弃当前的进程执行,或者是不停地进行查询,知道进程可以进行操作为止。实际上就是常说的轮询的方式进行设备的访问。

select()和poll()调用的本质是一样的,在性能上也不存在明显的差异,只是select监视的文件描述符数量有限,下面分别看下这两个系统调用。

首先看下select()系统调用,其函数原型为:

[cpp]  view plain copy
  1. int select(int nfds, fd_set *readfds, fd_set *writefds,  
  2.                   fd_set *exceptfds, struct timeval *timeout);  
readfds、writefds、exceptfds是被select()监视的读、写、异常处理的文件描述符集合,nfds是需要检查的号码最高的文件描述符加1,timeout是一个超时值得设定,当在timeout的时间里没有文件描述符准备好则返回。struct timeval结构体如下:

[cpp]  view plain copy
  1. struct timeval {  
  2.         __kernel_time_t         tv_sec;         /* seconds */  
  3.         __kernel_suseconds_t    tv_usec;        /* microseconds */  
  4. };  
在select()函数后面还提供了一些API函数,如:

[cpp]  view plain copy
  1. void FD_CLR(int fd, fd_set *set);                                //将一个文件描述符从文件描述符集中清除  
  2. int  FD_ISSET(int fd, fd_set *set);                              //判断文件描述符是否被置位  
  3. void FD_SET(int fd, fd_set *set);                               //将一个文件描述符加到文件描述符集中  
  4. void FD_ZERO(fd_set *set);                                      //清除一个文件描述符集  

select()的接口主要是建立在叫fd_set类型的基础上,它是一组文件描述符的集合,来看下面的定义:

[cpp]  view plain copy
  1. typedef struct {  
  2.         unsigned long fds_bits [__FDSET_LONGS];  
  3. } __kernel_fd_set;  
而__FDSET_LONGS是

[cpp]  view plain copy
  1. #define __FDSET_LONGS   (__FD_SETSIZE/__NFDBITS)  
[cpp]  view plain copy
  1. #define __FD_SETSIZE    1024  
  2. #define __NFDBITS       (8 * sizeof(unsigned long))  
这里计算得__FDSET_LONGS的值是32,不同平台上这个值不一定相同。

上面大概介绍了一下selece(),下面来看poll()。

poll()系统调用的原型是:

[cpp]  view plain copy
  1. int poll(struct pollfd *fds, nfds_t nfds, int timeout);  
其中pollfd结构体如下:

[cpp]  view plain copy
  1. struct pollfd {  
  2.         int fd;                                    //文件描述符  
  3.         short events;                           //等待的事件  
  4.         short revents;                          //实际发生的事件  
  5. };  

起执行过程如下:

首先将用户传入的pollfd数组拷贝到内核空间,然后查询每个文件描述符对应的设备的状态,最后将获得的数据传送到用户空间并执行释放内存和剥离等待队列等工作。

设计上无论是select()还是poll()系统调用,其最终都会引发设备驱动中的poll()被执行,那么下面就来看下设备驱动中的poll()函数:

首先来看下在设备驱动中的poll()函数的原型:

[cpp]  view plain copy
  1. unsigned int (*poll) (struct file *, struct poll_table_struct *);  
其在file_operations结构体中。参数file是file结构体指针,第二个参数是轮询表指针。这个函数主要完成的工作有:

对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table。最后返回表示是否能对设备进行无阻塞读、写访问的掩码。

[cpp]  view plain copy
  1. typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);  
  2.           
  3. typedef struct poll_table_struct {  
  4.         poll_queue_proc qproc;  
  5.         unsigned long key;  
  6. } poll_table;  
poll_queue_proc是一个函数指针,其在整个过程中都不变化,而key根据不同的fd的检测要求而变化。

[cpp]  view plain copy
  1. static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)  
  2. {  
  3.         if (p && wait_address)  
  4.                 p->qproc(filp, wait_address, p);  
  5. }  
wait_address是驱动程序需要提供的等待队列头。

下面把poll()系统调用涉及的主要代码在这里贴出来,大家可以深入解析一下:

[cpp]  view plain copy
  1. SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,  
  2.                 long, timeout_msecs)  
  3. {  
  4.         struct timespec end_time, *to = NULL;  
  5.         int ret;  
  6.   
  7.         if (timeout_msecs >= 0) {  
  8.                 to = &end_time;  
  9.                 poll_select_set_timeout(to, timeout_msecs / MSEC_PER_SEC,  
  10.                         NSEC_PER_MSEC * (timeout_msecs % MSEC_PER_SEC));  
  11.         }  
  12.   
  13.         ret = do_sys_poll(ufds, nfds, to);  
  14.   
  15.         if (ret == -EINTR) {  
  16.                 struct restart_block *restart_block;  
  17.   
  18.                 restart_block = ¤t_thread_info()->restart_block;  
  19.                 restart_block->fn = do_restart_poll;  
  20.                 restart_block->poll.ufds = ufds;  
  21.                 restart_block->poll.nfds = nfds;  
  22.   
  23.                 if (timeout_msecs >= 0) {  
  24.                         restart_block->poll.tv_sec = end_time.tv_sec;  
  25.                         restart_block->poll.tv_nsec = end_time.tv_nsec;  
  26.                         restart_block->poll.has_timeout = 1;  
  27.                 } else  
  28.                         restart_block->poll.has_timeout = 0;  
  29.   
  30.                 ret = -ERESTART_RESTARTBLOCK;  
  31.         }  
  32.         return ret;  
  33. }  
[cpp]  view plain copy
  1. int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,  
  2.                 struct timespec *end_time)  
  3. {  
  4.         struct poll_wqueues table;  
  5.         int err = -EFAULT, fdcount, len, size;  
  6.         /* Allocate small arguments on the stack to save memory and be 
  7.            faster - use long to make sure the buffer is aligned properly 
  8.            on 64 bit archs to avoid unaligned access */  
  9.         long stack_pps[POLL_STACK_ALLOC/sizeof(long)];  
  10.         struct poll_list *const head = (struct poll_list *)stack_pps;  
  11.         struct poll_list *walk = head;  
  12.         unsigned long todo = nfds;  
  13.   
  14.         if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)  
  15.                 return -EINVAL;  
  16.   
  17.         len = min_t(unsigned int, nfds, N_STACK_PPS);  
  18.         for (;;) {  
  19.                 walk->next = NULL;  
  20.                 walk->len = len;  
  21.                 if (!len)  
  22.                         break;  
  23.   
  24.                 if (copy_from_user(walk->entries, ufds + nfds-todo,  
  25.                                         sizeof(struct pollfd) * walk->len))  
  26.                         goto out_fds;  
  27.   
  28.                 todo -= walk->len;  
  29.                 if (!todo)  
  30.                         break;  
  31.   
  32.                 len = min(todo, POLLFD_PER_PAGE);  
  33.                 size = sizeof(struct poll_list) + sizeof(struct pollfd) * len;  
  34.                 walk = walk->next = kmalloc(size, GFP_KERNEL);  
  35.                 if (!walk) {  
  36.                         err = -ENOMEM;  
  37.                         goto out_fds;  
  38.                 }  
  39.         }  
  40.   
  41.         poll_initwait(&table);  
  42.         fdcount = do_poll(nfds, head, &table, end_time);  
  43.         poll_freewait(&table);  
  44.   
  45.         for (walk = head; walk; walk = walk->next) {  
  46.                 struct pollfd *fds = walk->entries;  
  47.                 int j;  
  48.   
  49.                 for (j = 0; j < walk->len; j++, ufds++)  
  50.                         if (__put_user(fds[j].revents, &ufds->revents))  
  51.                                 goto out_fds;  
  52.         }  
  53.   
  54.         err = fdcount;  
  55. out_fds:  
  56.         walk = head->next;  
  57.         while (walk) {  
  58.                 struct poll_list *pos = walk;  
  59.                 walk = walk->next;  
  60.                 kfree(pos);  
  61.         }  
  62.   
  63.         return err;  
  64. }  
[cpp]  view plain copy
  1. static int do_poll(unsigned int nfds,  struct poll_list *list,  
  2.                    struct poll_wqueues *wait, struct timespec *end_time)  
  3. {  
  4.         poll_table* pt = &wait->pt;  
  5.         ktime_t expire, *to = NULL;  
  6.         int timed_out = 0, count = 0;  
  7.         unsigned long slack = 0;  
  8.   
  9.         /* Optimise the no-wait case */  
  10.         if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {  
  11.                 pt = NULL;  
  12.                 timed_out = 1;  
  13.         }  
  14.   
  15.         if (end_time && !timed_out)  
  16.                 slack = estimate_accuracy(end_time);  
  17.   
  18.         for (;;) {  
  19.                 struct poll_list *walk;  
  20.   
  21.                 for (walk = list; walk != NULL; walk = walk->next) {  
  22.                         struct pollfd * pfd, * pfd_end;  
  23.   
  24.                         pfd = walk->entries;  
  25.                         pfd_end = pfd + walk->len;  
  26.                         for (; pfd != pfd_end; pfd++) {  
  27.                                 /* 
  28.                                  * Fish for events. If we found one, record it 
  29.                                  * and kill the poll_table, so we don't 
  30.                                  * needlessly register any other waiters after 
  31.                                  * this. They'll get immediately deregistered 
  32.                                  * when we break out and return. 
  33.                                  */  
  34.                                 if (do_pollfd(pfd, pt)) {  
  35.                                         count++;  
  36.                                         pt = NULL;  
  37.                                 }  
  38.                         }  
  39.                 }  
  40.                 /* 
  41.                  * All waiters have already been registered, so don't provide 
  42.                  * a poll_table to them on the next loop iteration. 
  43.                  */  
  44.                 pt = NULL;  
  45.                 if (!count) {  
  46.                         count = wait->error;  
  47.                         if (signal_pending(current))  
  48.                                 count = -EINTR;  
  49.                 }  
  50.                 if (count || timed_out)  
  51.                         break;  
  52.   
  53.                 /* 
  54.                  * If this is the first loop and we have a timeout 
  55.                  * given, then we convert to ktime_t and set the to 
  56.                  * pointer to the expiry value. 
  57.                  */  
  58.                 if (end_time && !to) {  
  59.                         expire = timespec_to_ktime(*end_time);  
  60.                         to = &expire;  
  61.                 }  
  62.   
  63.                   if (!poll_schedule_timeout(wait, TASK_INTERRUPTIBLE, to, slack))  
  64.                         timed_out = 1;  
  65.         }  
  66.         return count;  
  67. }  
下面还是根据前面所举的globalmem例子写一个poll函数
[cpp]  view plain copy
  1. static unsigned int globalmem_poll(struct file *filp,poll_table *wait)  
  2. {  
  3.     unsigned int mask = 0;  
  4.     struct globalmem_dev *dev = filp->private_data;  
  5.   
  6.     down(&dev->sem);  
  7.   
  8.     poll_wait(filp,&dev->r_wait,wait);                    //加读等待队列头  
  9.     poll_wait(filp,&dev->w_wait,wait);                   //加写等待队列头  
  10.       
  11.     if(dev->current_len != 0)  
  12.         mask |= POLLIN | POLLRDNORM;          //表示数据可获得  
  13.     if(dev->current_len != GLOBALFIFO_SIZE)          
  14.         mask |= POLLOUT | POLLWRNORM;       //表示数据可写入  
  15.   
  16.     up(&dev->sem);  
  17.     return mask;  
  18. }  
最后将其付给poll函数指针:

[cpp]  view plain copy
  1. static const struct file_operations globalmem_fops = {  
  2.     .poll = globalmem_poll,  
  3. }  
而关于上面的标识符<poll.h>中有定义。

下面再编写一个应用程序用于监控globalmem的可读可写状态,代码如下(未加头文件):

[cpp]  view plain copy
  1. #define FIFO_CLEAR 0x1;  
  2. #define BUFFER_LEN 20;  
  3.   
  4. mian()  
  5. {  
  6.     int fd,num;  
  7.     char rd_ch[BUFFER_LEN];  
  8.     fd_set rfds,wfds;  
  9.   
  10.     fd = open("/dev/globalmem",O_RDONLY | O_NONBLOCK);  
  11.     if(fd != -1){  
  12.         if(ioctl(fd,FIFO_CLEAR,0)<0)  
  13.             printf("ioctl command failed\n");  
  14.   
  15.     while(1){  
  16.         FD_ZERO(&rfds);  
  17.         FD_ZERO(&wfds);  
  18.         FD_SET(fd,&rfds);  
  19.         FD_SET(fd,&wfds);  
  20.   
  21.         select(fd+1,&rfds,&wfds,NULL,NULL);  
  22.         if(FD_ISSET(fd,&rfds))  
  23.             printf("poll monitor:can be read\n");  
  24.         if(FD_ISSET(fd,&wfds))  
  25.             printf("poll monitor:can be written\n");      
  26.         }  
  27.     }else{  
  28.         printf("Device open failure\n");      
  29.     }  
  30. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值