linux輪詢操作

來源:互聯網
上載者:User

本文來自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位 31位32位  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 的檔案描述符有意義:
 通常裝置驅動不使用這些標誌.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.