蛻變成蝶~Linux設備驅動中的阻塞和非阻塞I/O

  今天意外收到一個消息,真是驚呆我了,博客軒給我發了信息,說是俺的博客文章有特點能夠出本書,,這簡直讓我受寵若驚,俺只是個大三的技術宅,寫的博客也是本身所學的一些看法和在網上看到我一些博文以及帖子裏綜合起來寫的,,總之這又給了額外的動力,讓本身繼續前進,,但願和你們可以分享一些本身的經驗,,在最須要奮鬥的年級以及在技術的領域踽踽獨行的過程當中有共同的夥伴繼續前進~html

  今天寫的是Linux設備驅動中的阻塞和非阻塞I/0,何謂阻塞與非阻塞I/O?簡單來講就是對I/O操做的兩種不一樣的方式,驅動程序能夠靈活的支持用戶空間對設備的這兩種訪問方式。數據結構

1、基本概念:socket

  • 阻塞操做     : 是指在執行設備操做時,若不能得到資源,則掛起進程直到知足操做條件後再進行操做。被掛起的進程進入休眠, 被從調度器移走,直到條件知足。
  • 非阻塞操做  :在不能進行設備操做時,並不掛起,它或者放棄,或者不停地查詢,直到能夠進行操做。非阻塞應用程序一般使用select系統調用查詢是否能夠對設備進行無阻塞的訪問最終會引起設備驅動中   poll函數執行。

2、輪詢操做函數

阻塞的讀取一個字符:ui

char buf;
fd = open("/dev/ttyS1",O_RDWR);
.....
res = read(fd,&buf,1); //當串口上有輸入時才返回,沒有輸入則進程掛起睡眠
if(res == 1)
{
  printf("%c/n",buf);
}

非阻塞的讀一個字符:指針

char buf;
fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK);//O_NONBLOCK 非阻塞標識
.....
while(read(fd,&buf,1)!=1);//串口上沒有輸入則返回,因此循環讀取
printf("%c/n",buf);

  阻塞操做經常用等待隊列來實現,而非阻塞操做用輪詢的方式來實現。非阻塞I/O的操做在應用層一般會用到select()和poll()系統調用查詢是否可對設備進行無阻塞訪問。select()和poll()系統調用最終會引起設備驅動中的poll()函數被調用。這裏對隊列就很少介紹了,你們能夠看看數據結構裏面的知識點。htm

  應用層的select()原型爲:blog

int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptionfds,

struct timeval *timeout);  numfds 的值爲須要檢查的號碼最高的文件描述符加1,若select()在等待timeout時間後,若沒有文件描述符準備好則返回。

  應用程序爲:隊列

#inlcude------
main()
{
  int fd,num;
  char rd_ch[BUFFER_LEN];
  fd_set rfds,wfds;  //讀寫文件描述符集
  //以非阻塞方式打開/dev/globalfifo設備文件
  fd=open("/dev/globalfifo",O_RDWR|O_NONBLOCK);
  if(fd != -1)
  {
   //FIFO 清零
   if(ioctl(fd,FIFO_CLEAR,0) < 0)
   {
     printf("ioctl cmd 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);

    }
  }
}

  下面說說設備驅動中的poll()函數,函數原型以下:進程

static unsigned int poll(struct file *file, struct socket *sock,poll_table *wait) //第一個參數是file結構體指針,第三個參數是輪詢表指針,這個函數應該進行兩項工做
  • 對可能引發設備文件狀態變化的等待隊列調用poll_wait()函數,將對應的等待隊列頭添加到poll_table
  • 返回表示是否能對設備進行無阻塞讀,寫訪問的掩碼

  這裏還要提到poll_wait()函數,不少人會覺得是和wait_event()同樣的函數,會阻塞的等待某件事情的發生,其實這個函數並不會引發阻塞,它的工做是把當前的進程增添到wait參數指定的等待列表poll_table中去,poll_wait()函數原型以下:

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
從中能夠看出是將等待隊列頭wait_address添加到p所指向的結構體中(poll_table)

  驅動函數中的poll()函數典型模板以下:

static unsigned int xxx_poll(struct file *filp,struct socket *sock,
 poll_table *wait)
{
unsigned int mask = 0;
struct xxx_dev *dev = filp->private_data;//得到設備結構體指針
...
poll_wait(filp,&dev->r_wait,wait);//加讀等待隊列頭到poll_table
poll_wait(filp,&dev->w_wait,wait);//加寫等待隊列頭到poll_table
...
if(...)//可讀
mask |= POLLIN | POLLRDNORM;
if(...)//可寫
mask |= POLLOUT | POLLRDNORM;
...
return mask;
 
}

  

3、支持輪詢操做的globalfifo驅動

  在globalfifo的poll()函數中,首先將設備結構體重的r_wait和w_wait等待隊列頭加到等待隊列表,globalfifo設備驅動的poll()函數以下:

static unsigned int gloablfif0_poll(struct file *filp,poll_table *wait)
{
     unsigned int mask = 0;
    struct globalfifo_dev *dev = filp->private_data;

    down(&dev->sem);

    poll_wait(filp,&dev->r_wait , wait)  ;
    poll_wait(filp,&dev->r_wait , wait)  ;

    if(dev->current_len != 0)
    {
          mask |= POLLIN | POLLRDNORM;  
    }

    if(dev->current_len != GLOBALFIFO_SIZE)
    {
        mask |= POLLOUT | POLLWRNORM;
    }

    up(&dev->sem);
    return mask;
}

  

4、總結

阻塞與非阻塞操做:

  • 定義並初始化等待對列頭;
  • 定義並初始化等待隊列;
  • 把等待隊列添加到等待隊列頭
  • 設置進程狀態(TASK_INTERRUPTIBLE(能夠被信號打斷)和TASK_UNINTERRUPTIBLE(不能被信號打斷))
  • 調用其它進程

poll機制:

  • 把等待隊列頭加到poll_table
  • 返回表示是否能對設備進行無阻塞讀,寫訪問的掩碼

 

  版權全部,轉載請註明轉載地址:http://www.cnblogs.com/lihuidashen/p/4442573.html

相關文章
相關標籤/搜索