生活随笔
收集整理的這篇文章主要介紹了
Linux设备驱动开发-linux驱动中的非阻塞访问方式
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前面已經詳細分析過了阻塞訪問方式,下面就來繼續分析一下非阻塞的訪問方式。
什么是非阻塞的訪問方式呢?非阻塞操作的進程在不能進行設備操作時,并不掛起,他或者是放棄當前的進程執行,或者是不停地進行查詢,知道進程可以進行操作為止。實際上就是常說的輪詢的方式進行設備的訪問。
select()和poll()調用的本質是一樣的,在性能上也不存在明顯的差異,只是select監視的文件描述符數量有限,下面分別看下這兩個系統調用。
首先看下select()系統調用,其函數原型為:
[cpp]?view plaincopy
int?select(int?nfds,?fd_set?*readfds,?fd_set?*writefds,?? ??????????????????fd_set?*exceptfds,?struct?timeval?*timeout);??
readfds、writefds、exceptfds是被select()監視的讀、寫、異常處理的文件描述符集合,nfds是需要檢查的號碼最高的文件描述符加1,timeout是一個超時值得設定,當在timeout的時間里沒有文件描述符準備好則返回。struct timeval結構體如下:
[cpp]?view plaincopy
struct?timeval?{?? ????????__kernel_time_t?????????tv_sec;??????????? ????????__kernel_suseconds_t????tv_usec;?????????? };??
在select()函數后面還提供了一些API函數,如:
[cpp]?view plaincopy
void?FD_CLR(int?fd,?fd_set?*set);?????????????????????????????????? int??FD_ISSET(int?fd,?fd_set?*set);???????????????????????????????? void?FD_SET(int?fd,?fd_set?*set);????????????????????????????????? void?FD_ZERO(fd_set?*set);????????????????????????????????????????
select()的接口主要是建立在叫fd_set類型的基礎上,它是一組文件描述符的集合,來看下面的定義:
[cpp]?view plaincopy
typedef?struct?{?? ????????unsigned?long?fds_bits?[__FDSET_LONGS];?? }?__kernel_fd_set;??
而__FDSET_LONGS是
[cpp]?view plaincopy
#define?__FDSET_LONGS???(__FD_SETSIZE/__NFDBITS)??
[cpp]?view plaincopy
#define?__FD_SETSIZE????1024?? #define?__NFDBITS???????(8?*?sizeof(unsigned?long))??
這里計算得__FDSET_LONGS的值是32,不同平臺上這個值不一定相同。
上面大概介紹了一下selece(),下面來看poll()。
poll()系統調用的原型是:
[cpp]?view plaincopy
int?poll(struct?pollfd?*fds,?nfds_t?nfds,?int?timeout);??
其中pollfd結構體如下:
[cpp]?view plaincopy
struct?pollfd?{?? ????????int?fd;?????????????????????????????????????? ????????short?events;????????????????????????????? ????????short?revents;???????????????????????????? };??
起執行過程如下:
首先將用戶傳入的pollfd數組拷貝到內核空間,然后查詢每個文件描述符對應的設備的狀態,最后將獲得的數據傳送到用戶空間并執行釋放內存和剝離等待隊列等工作。
設計上無論是select()還是poll()系統調用,其最終都會引發設備驅動中的poll()被執行,那么下面就來看下設備驅動中的poll()函數:
首先來看下在設備驅動中的poll()函數的原型:
[cpp]?view plaincopy
unsigned?int?(*poll)?(struct?file?*,?struct?poll_table_struct?*);??
其在file_operations結構體中。參數file是file結構體指針,第二個參數是輪詢表指針。這個函數主要完成的工作有:
對可能引起設備文件狀態變化的等待隊列調用poll_wait()函數,將對應的等待隊列頭添加到poll_table。最后返回表示是否能對設備進行無阻塞讀、寫訪問的掩碼。
[cpp]?view plaincopy
typedef?void?(*poll_queue_proc)(struct?file?*,?wait_queue_head_t?*,?struct?poll_table_struct?*);?? ?????????? typedef?struct?poll_table_struct?{?? ????????poll_queue_proc?qproc;?? ????????unsigned?long?key;?? }?poll_table;??
poll_queue_proc是一個函數指針,其在整個過程中都不變化,而key根據不同的fd的檢測要求而變化。
[cpp]?view plaincopy
static?inline?void?poll_wait(struct?file?*?filp,?wait_queue_head_t?*?wait_address,?poll_table?*p)?? {?? ????????if?(p?&&?wait_address)?? ????????????????p->qproc(filp,?wait_address,?p);?? }??
wait_address是驅動程序需要提供的等待隊列頭。
下面把poll()系統調用涉及的主要代碼在這里貼出來,大家可以深入解析一下:
[cpp]?view plaincopy
SYSCALL_DEFINE3(poll,?struct?pollfd?__user?*,?ufds,?unsigned?int,?nfds,?? ????????????????long,?timeout_msecs)?? {?? ????????struct?timespec?end_time,?*to?=?NULL;?? ????????int?ret;?? ?? ????????if?(timeout_msecs?>=?0)?{?? ????????????????to?=?&end_time;?? ????????????????poll_select_set_timeout(to,?timeout_msecs?/?MSEC_PER_SEC,?? ????????????????????????NSEC_PER_MSEC?*?(timeout_msecs?%?MSEC_PER_SEC));?? ????????}?? ?? ????????ret?=?do_sys_poll(ufds,?nfds,?to);?? ?? ????????if?(ret?==?-EINTR)?{?? ????????????????struct?restart_block?*restart_block;?? ?? ????????????????restart_block?=?¤t_thread_info()->restart_block;?? ????????????????restart_block->fn?=?do_restart_poll;?? ????????????????restart_block->poll.ufds?=?ufds;?? ????????????????restart_block->poll.nfds?=?nfds;?? ?? ????????????????if?(timeout_msecs?>=?0)?{?? ????????????????????????restart_block->poll.tv_sec?=?end_time.tv_sec;?? ????????????????????????restart_block->poll.tv_nsec?=?end_time.tv_nsec;?? ????????????????????????restart_block->poll.has_timeout?=?1;?? ????????????????}?else?? ????????????????????????restart_block->poll.has_timeout?=?0;?? ?? ????????????????ret?=?-ERESTART_RESTARTBLOCK;?? ????????}?? ????????return?ret;?? }??
[cpp]?view plaincopy
int?do_sys_poll(struct?pollfd?__user?*ufds,?unsigned?int?nfds,?? ????????????????struct?timespec?*end_time)?? {?? ????????struct?poll_wqueues?table;?? ????????int?err?=?-EFAULT,?fdcount,?len,?size;?? ????????? ? ?? ????????long?stack_pps[POLL_STACK_ALLOC/sizeof(long)];?? ????????struct?poll_list?*const?head?=?(struct?poll_list?*)stack_pps;?? ????????struct?poll_list?*walk?=?head;?? ????????unsigned?long?todo?=?nfds;?? ?? ????????if?(nfds?>?current->signal->rlim[RLIMIT_NOFILE].rlim_cur)?? ????????????????return?-EINVAL;?? ?? ????????len?=?min_t(unsigned?int,?nfds,?N_STACK_PPS);?? ????????for?(;;)?{?? ????????????????walk->next?=?NULL;?? ????????????????walk->len?=?len;?? ????????????????if?(!len)?? ????????????????????????break;?? ?? ????????????????if?(copy_from_user(walk->entries,?ufds?+?nfds-todo,?? ????????????????????????????????????????sizeof(struct?pollfd)?*?walk->len))?? ????????????????????????goto?out_fds;?? ?? ????????????????todo?-=?walk->len;?? ????????????????if?(!todo)?? ????????????????????????break;?? ?? ????????????????len?=?min(todo,?POLLFD_PER_PAGE);?? ????????????????size?=?sizeof(struct?poll_list)?+?sizeof(struct?pollfd)?*?len;?? ????????????????walk?=?walk->next?=?kmalloc(size,?GFP_KERNEL);?? ????????????????if?(!walk)?{?? ????????????????????????err?=?-ENOMEM;?? ????????????????????????goto?out_fds;?? ????????????????}?? ????????}?? ?? ????????poll_initwait(&table);?? ????????fdcount?=?do_poll(nfds,?head,?&table,?end_time);?? ????????poll_freewait(&table);?? ?? ????????for?(walk?=?head;?walk;?walk?=?walk->next)?{?? ????????????????struct?pollfd?*fds?=?walk->entries;?? ????????????????int?j;?? ?? ????????????????for?(j?=?0;?j?<?walk->len;?j++,?ufds++)?? ????????????????????????if?(__put_user(fds[j].revents,?&ufds->revents))?? ????????????????????????????????goto?out_fds;?? ????????}?? ?? ????????err?=?fdcount;?? out_fds:?? ????????walk?=?head->next;?? ????????while?(walk)?{?? ????????????????struct?poll_list?*pos?=?walk;?? ????????????????walk?=?walk->next;?? ????????????????kfree(pos);?? ????????}?? ?? ????????return?err;?? }??
[cpp]?view plaincopy
static?int?do_poll(unsigned?int?nfds,??struct?poll_list?*list,?? ???????????????????struct?poll_wqueues?*wait,?struct?timespec?*end_time)?? {?? ????????poll_table*?pt?=?&wait->pt;?? ????????ktime_t?expire,?*to?=?NULL;?? ????????int?timed_out?=?0,?count?=?0;?? ????????unsigned?long?slack?=?0;?? ?? ?????????? ????????if?(end_time?&&?!end_time->tv_sec?&&?!end_time->tv_nsec)?{?? ????????????????pt?=?NULL;?? ????????????????timed_out?=?1;?? ????????}?? ?? ????????if?(end_time?&&?!timed_out)?? ????????????????slack?=?estimate_accuracy(end_time);?? ?? ????????for?(;;)?{?? ????????????????struct?poll_list?*walk;?? ?? ????????????????for?(walk?=?list;?walk?!=?NULL;?walk?=?walk->next)?{?? ????????????????????????struct?pollfd?*?pfd,?*?pfd_end;?? ?? ????????????????????????pfd?=?walk->entries;?? ????????????????????????pfd_end?=?pfd?+?walk->len;?? ????????????????????????for?(;?pfd?!=?pfd_end;?pfd++)?{?? ????????????????????????????????? ? ? ? ? ? ?? ????????????????????????????????if?(do_pollfd(pfd,?pt))?{?? ????????????????????????????????????????count++;?? ????????????????????????????????????????pt?=?NULL;?? ????????????????????????????????}?? ????????????????????????}?? ????????????????}?? ????????????????? ? ? ?? ????????????????pt?=?NULL;?? ????????????????if?(!count)?{?? ????????????????????????count?=?wait->error;?? ????????????????????????if?(signal_pending(current))?? ????????????????????????????????count?=?-EINTR;?? ????????????????}?? ????????????????if?(count?||?timed_out)?? ????????????????????????break;?? ?? ????????????????? ? ? ? ?? ????????????????if?(end_time?&&?!to)?{?? ????????????????????????expire?=?timespec_to_ktime(*end_time);?? ????????????????????????to?=?&expire;?? ????????????????}?? ?? ??????????????????if?(!poll_schedule_timeout(wait,?TASK_INTERRUPTIBLE,?to,?slack))?? ????????????????????????timed_out?=?1;?? ????????}?? ????????return?count;?? }??
下面還是根據前面所舉的globalmem例子寫一個poll函數
[cpp]?view plaincopy
static?unsigned?int?globalmem_poll(struct?file?*filp,poll_table?*wait)?? {?? ????unsigned?int?mask?=?0;?? ????struct?globalmem_dev?*dev?=?filp->private_data;?? ?? ????down(&dev->sem);?? ?? ????poll_wait(filp,&dev->r_wait,wait);?????????????????????? ????poll_wait(filp,&dev->w_wait,wait);????????????????????? ?????? ????if(dev->current_len?!=?0)?? ????????mask?|=?POLLIN?|?POLLRDNORM;???????????? ????if(dev->current_len?!=?GLOBALFIFO_SIZE)?????????? ????????mask?|=?POLLOUT?|?POLLWRNORM;????????? ?? ????up(&dev->sem);?? ????return?mask;?? }??
最后將其付給poll函數指針:
[cpp]?view plaincopy
static?const?struct?file_operations?globalmem_fops?=?{?? ????.poll?=?globalmem_poll,?? }??
而關于上面的標識符<poll.h>中有定義。
下面再編寫一個應用程序用于監控globalmem的可讀可寫狀態,代碼如下(未加頭文件):
[cpp]?view plaincopy
#define?FIFO_CLEAR?0x1;?? #define?BUFFER_LEN?20;?? ?? mian()?? {?? ????int?fd,num;?? ????char?rd_ch[BUFFER_LEN];?? ????fd_set?rfds,wfds;?? ?? ????fd?=?open("/dev/globalmem",O_RDONLY?|?O_NONBLOCK);?? ????if(fd?!=?-1){?? ????????if(ioctl(fd,FIFO_CLEAR,0)<0)?? ????????????printf("ioctl?command?failed\n");?? ?? ????while(1){?? ????????FD_ZERO(&rfds);?? ????????FD_ZERO(&wfds);?? ????????FD_SET(fd,&rfds);?? ????????FD_SET(fd,&wfds);?? ?? ????????select(fd+1,&rfds,&wfds,NULL,NULL);?? ????????if(FD_ISSET(fd,&rfds))?? ????????????printf("poll?monitor:can?be?read\n");?? ????????if(FD_ISSET(fd,&wfds))?? ????????????printf("poll?monitor:can?be?written\n");?????? ????????}?? ????}else{?? ????????printf("Device?open?failure\n");?????? ????}?? } ?
總結
以上是生活随笔為你收集整理的Linux设备驱动开发-linux驱动中的非阻塞访问方式的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。