日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

發布時間:2024/9/21 linux 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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;?????????/*?seconds?*/??
  • ????????__kernel_suseconds_t????tv_usec;????????/*?microseconds?*/??
  • };??
  • 在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;??
  • ????????/*?Allocate?small?arguments?on?the?stack?to?save?memory?and?be?
  • ???????????faster?-?use?long?to?make?sure?the?buffer?is?aligned?properly?
  • ???????????on?64?bit?archs?to?avoid?unaligned?access?*/??
  • ????????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;??
  • ??
  • ????????/*?Optimise?the?no-wait?case?*/??
  • ????????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++)?{??
  • ????????????????????????????????/*?
  • ?????????????????????????????????*?Fish?for?events.?If?we?found?one,?record?it?
  • ?????????????????????????????????*?and?kill?the?poll_table,?so?we?don't?
  • ?????????????????????????????????*?needlessly?register?any?other?waiters?after?
  • ?????????????????????????????????*?this.?They'll?get?immediately?deregistered?
  • ?????????????????????????????????*?when?we?break?out?and?return.?
  • ?????????????????????????????????*/??
  • ????????????????????????????????if?(do_pollfd(pfd,?pt))?{??
  • ????????????????????????????????????????count++;??
  • ????????????????????????????????????????pt?=?NULL;??
  • ????????????????????????????????}??
  • ????????????????????????}??
  • ????????????????}??
  • ????????????????/*?
  • ?????????????????*?All?waiters?have?already?been?registered,?so?don't?provide?
  • ?????????????????*?a?poll_table?to?them?on?the?next?loop?iteration.?
  • ?????????????????*/??
  • ????????????????pt?=?NULL;??
  • ????????????????if?(!count)?{??
  • ????????????????????????count?=?wait->error;??
  • ????????????????????????if?(signal_pending(current))??
  • ????????????????????????????????count?=?-EINTR;??
  • ????????????????}??
  • ????????????????if?(count?||?timed_out)??
  • ????????????????????????break;??
  • ??
  • ????????????????/*?
  • ?????????????????*?If?this?is?the?first?loop?and?we?have?a?timeout?
  • ?????????????????*?given,?then?we?convert?to?ktime_t?and?set?the?to?
  • ?????????????????*?pointer?to?the?expiry?value.?
  • ?????????????????*/??
  • ????????????????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驱动中的非阻塞访问方式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。