linux輪詢操作


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/unbutun/article/details/4722448

輪詢函數
輪詢的概念和作用
使用非阻塞I/O的應用程序通常會使用select()和poll(),poll和select用於查詢設備的狀態,以便用戶程序獲知是
否能對設備進行非阻塞的訪問,它們都需要設備驅動程序中的poll函數支持。
Select()和poll()系統調用最終會引發設備驅動設備中的poll()函數被執行。poll()函數為最終執行體)
Linux下select調用的過程:
1.用戶層應用程序調用select(),底層調用poll())
2.核心層調用sys_select() --> do_select()
最終調用文件描述符fd對應的struct file類型變量的struct file_operations *f_op的poll函數。
當然2.25.45中還引入了epoll(),即拓展的poll();
select()和poll()系統調用的本質一樣,前者在BSD UNIX中引入的,后者在System V
中引入的。
1、首先說什么是文件描述符,它有什么作用?
文件描述符是一個簡單的整數,用以標明每一個被進程所打開的文件和socket。第一個打開的文件是0,第二個是
1,依此類推。Unix 操作系統通常給每個進程能打開的文件數量強加一個限制。更甚的是,unix 通常有一個系統
級的限制。
大多數情況下,1024 個文件描述符足夠了。非常忙的cache可能需要4096或更多。當然,你可以自己配置文件描述
符限制,在配置文件描述符限制時,我推薦設置系統級限制的數量為每個進程限制的2 倍。
應用程序中的輪詢編程
應用程序中最廣泛用到的是BSD UNIX中引入的select()系統調用,其原型如下:
int select(int numfds,fd_set *readfme,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
select的第一個參數是文件描述符集中要被檢測的數目,這個值必須至少比待檢測的最大文件描述符大1;參數readfds
指定了被讀監控的文件描述符集;參數writefds指定了被寫監控的文件描述符集;而參數exceptfds指定了被例外
條件監控的文件描述符集。
Timeout參數是一個指向struct timeval類型的指針,它可以使select()在等待timeout時間后若沒有文件描述符准備
好則返回,struct timeval數據結構的定義如下:
struct timeval
{
int tv_sec;//表示幾秒
int tv_usec;//表示幾微秒
};
timeout取不同的值,該調用就表現不同的性質:
1.timeout為0,調用立即返回;
2.timeout為NULL,select()調用就阻塞,直到知道有文件描述符就緒;(當有文件描述符就緒時,會向這個函數
發送信號,以喚醒此函數。)
3.timeout為正整數,就是一般的定時器。
select的返回值有如下情況:
1.正常情況下返回就緒的文件描述符個數;
2.經過了timeout時長后仍無設備准備好,返回值為0;
3.如果select被某個信號中斷,它將返回-1並設置errno為EINTR。
4.如果出錯,返回-1並設置相應的errno。
select()函數的 接口主要是建立在一種叫“fd_set”類型的基礎上。這個類型是一組文件描述符(fd)的集合。
因為fd_set類型的長度在不同平台上不同,因此應該用一組標准的宏定義來處理這個類變量:
首先我們來了解fd_set這個結構的定義
#define FD_ZERO(fdsetp) __FD_ZERO(fdsetp)
(注意了,下面定義的常量是很有用的,因為在了解fd_set這個結構中會使用到,當然,這些值並不是規定死的,
在不同的程序中有不同的值,不過這些值只是取少數的幾個規定值)
#define __FDSET_LONGS   (__FD_SETSIZE/__NFDBITS)//計算得到的值為32
#define __FD_SETSIZE    1024
#define __NFDBITS       (8 * sizeof(unsigned long))  //8*4=32
typedef struct {
  37        unsigned long fds_bits [__FDSET_LONGS];
  38} __kernel_fd_set;
typedef __kernel_fd_set         fd_set;
以上就是對文件描述符集fd_set的定義,如果你能夠有下圖,那么我想,你更高興地去學習文件描述符:

0

0

0

0

0

1

0

...

0

0

...

0

1

0

0

...

0

0

0

0

0 3132 63
下面操作用來設置、清除、判斷文件描述符集合。
FD_ZERO(fd_set *set);//將文件描述符集fd_set中的值置0,如此以來對應所有位都被設置為0;
下面是對此函數的具體操作:

static __inline__ void __FD_ZERO(__kernel_fd_set *p)
  88{
  89        unsigned long *tmp = p->fds_bits;
  90        int i;
  91
  92        if (__builtin_constant_p(__FDSET_LONGS)) {
  93                switch (__FDSET_LONGS) {//__FDSET_LONGS的取值由系統決定
  94                      case 16:
  95                        tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
  96                        tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;
  97                        tmp[ 8] = 0; tmp[ 9] = 0; tmp[10] = 0; tmp[11] = 0;
  98                        tmp[12] = 0; tmp[13] = 0; tmp[14] = 0; tmp[15] = 0;
  99                        return;
 100
 101                      case 8:
 102                        tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
 103                        tmp[ 4] = 0; tmp[ 5] = 0; tmp[ 6] = 0; tmp[ 7] = 0;
 104                        return;
 105
 106                      case 4:
 107                        tmp[ 0] = 0; tmp[ 1] = 0; tmp[ 2] = 0; tmp[ 3] = 0;
 108                        return;
 109                }
 110        }
 111        i = __FDSET_LONGS;
 112        while (i) {
 113                i--;
 114                *tmp = 0;
 115                tmp++;
 116        }//此循環是為了使得上面的判斷操作變得更安全。
 117}
上面有內建函數 __builtin_constant_p ,這個函數用於判斷一個值是否為編譯時常數,假如參數__FDSET_LONGS的
值是常數,函數返回 1,否則返回 0。

FD_SET(int fd,fd_set *set);//將一個文件描述符加入文件描述集中 。
即將fd在文件描述符中所對應的位置1,*set為所操作的文件描述符集對象。
2static __inline__ void __FD_SET(unsigned long __fd, __kernel_fd_set *__fdsetp)
  53{
  54        unsigned long __tmp = __fd / __NFDBITS;
  55        unsigned long __rem = __fd % __NFDBITS;
  56        __fdsetp->fds_bits[__tmp] |= (1UL<<__rem);//將無符號長整型的1左移_rem位
  57}



FD_CLR(int fd,fd_set *set)//將一個文件描述符從文件描述符集中清除。

static __inline__ void __FD_CLR(unsigned long __fd, __kernel_fd_set *__fdsetp)
  61{
  62        unsigned long __tmp = __fd / __NFDBITS;
  63        unsigned long __rem = __fd % __NFDBITS;
  64        __fdsetp->fds_bits[__tmp] &= ~(1UL<<__rem);
  65}



FD_ISSET(int fd,fd_set *set)//判斷文件描述符是否被置位。

9static __inline__ int __FD_ISSET(unsigned long __fd, const __kernel_fd_set *__p)
  70{
  71        unsigned long __tmp = __fd / __NFDBITS;
  72        unsigned long __rem = __fd % __NFDBITS;
  73        return (__p->fds_bits[__tmp] & (1UL<<__rem)) != 0;
  74}

設備驅動中的輪詢操作:

unsigned int (*poll)(struct file *filp,struct poll_table *wait);
第一個參數為file結構體指針,第二個參數為輪詢表指針。這個函數應該進行以下兩項工作。
1、對可能引起設備文件狀態變化的等待隊列調用poll_wait()函數,將對應的等待隊列頭添加到poll_table;
2、返回表示是否對設備進行無阻塞讀、寫訪問的掩碼。
關鍵的用於向poll_table注冊等待隊列的poll_wait(*)函數的原型如下:

void poll_wait(struct file *filp,wait_queue_heat_t *queue,poll_table *wait);
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
  38{
  39        if (p && wait_address)
  40                p->qproc(filp, wait_address, p);//這個函數需要用戶自己編寫來實現。
  41}
下面是對輪詢表的定義:
  33typedef struct poll_table_struct {
  34        poll_queue_proc qproc;
  35} poll_table;
  31typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
由上面的代碼可以知道poll_table 結構只是對一個函數的封裝,更有趣的是,這個函數建立了實際的數據結構. 那個數據結構, 對於 poll和 select, 是一個內存頁的鏈表, 其中包含 poll_table_entry 結構. 每個 poll_table_entry 持有被傳遞給 poll_wait 的 struct file 和 wait_queue_head_t 指針, 以及一個關聯的等待隊列入口.
下面是poll_table_entry結構體的源代碼:
 48struct poll_table_entry {
49 struct file * filp;
50 wait_queue_t wait;
51 wait_queue_head_t * wait_address;
52};

poll_wait()函數的名稱非常容易讓人產生誤會,以為它和wait_event()等一樣,會阻塞地等待某件事情的發生,
這個函數不會引起阻塞.poll_wait()函數所作的 工作是把當前進程添加到wait參數指定的等待列表(poll_table)中。
驅動程序poll()函數應該返回設備資源的可獲取狀態,即POLLIN、POLLOUT、POLLPRI、POLLERR、POLLNVAL等宏的位“或“結果。
每個宏的含義都表明設備的一種狀態,如POLLIN意味着設備可以無阻塞地讀,POLLOUT意味着設備可以無阻塞地寫。
下面對poll()函數的可獲得狀態的取值進行解析:
POLLIN
如果設備可被不阻塞地讀, 這個位必須設置.
POLLRDNORM
這個位必須設置, 如果"正常"數據可用來讀. 一個可讀的設備返回( POLLIN|POLLRDNORM ).
POLLRDBAND
這個位指示帶外數據可用來從設備中讀取. 當前只用在 Linux 內核的一個地方( DECnet 代碼 )並且通常對設備驅動不可用.
POLLPRI
高優先級數據(帶外)可不阻塞地讀取. 這個位使 select 報告在文件上遇到一個異常情況, 因為 selct 報告帶外數據作為一個異常情況.
POLLHUP
當讀這個設備的進程見到文件尾, 驅動必須設置 POLLUP(hang-up). 一個調用 select 的進程被告知設備是可讀的, 如同 selcet 功能所規定的.
POLLERR
一個錯誤情況已在設備上發生. 當調用 poll, 設備被報告位可讀可寫, 因為讀寫都返回一個錯誤碼而不阻塞.
POLLOUT
這個位在返回值中設置, 如果設備可被寫入而不阻塞.
POLLWRNORM
這個位和 POLLOUT 有相同的含義, 並且有時它確實是相同的數. 一個可寫的設備返回( POLLOUT|POLLWRNORM).
POLLWRBAND
    如同 POLLRDBAND , 這個位意思是帶有零優先級的數據可寫入設備. 只有 poll 的數據報實現    使用這個位,
因為一個數據報看傳送帶外數據.應當重復一下 POLLRDBAND 和 POLLWRBAND 僅僅對關聯到 socket 的文件描述符有意義:
 通常設備驅動不使用這些標志.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM