(十一)linux之poll輪詢

目錄

        • (一)poll輪詢的作用
        • (二)poll輪詢相關的接口
        • (三)poll使用流程
        • (四)實例代碼

(一)poll輪詢的作用

以阻塞的方式打開文件,那么對多個文件讀寫時,若某個文件未準備好,則系統會處于讀寫阻塞,并影響其他文件的讀寫,poll輪訓就是實現既可使用輸入輸出流又不想阻塞在任何一個設備的讀寫操作
調用poll函數返回時,會返回一個文件是否可讀寫的標志狀態,用戶程序根據不同的標志狀態來讀寫相應的文件,實現阻塞方式打開但是非阻塞方式讀寫的結果

(二)poll輪詢相關的接口

  1. 系統層接口:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd *fds: 輪訓描述符的集合數據結構
struct pollfd {int   fd;         /* file descriptor */輪訓的文件描述符short events;     /* requested events */對于輪訓的文件描述所檢測的事件類型short revents;    /* returned events */對于輪訓的文件描述所返回的事件類型
};
nfds_t nfds:輪訓描述符的個數
int timeout:輪詢時間

請求事件和返回事件類型:

POLLIN      There is data to read.
POLLOUT     Writing  is now possible, though a write larger that the available spacein a socket or pipe will still block (unless O_NONBLOCK is set).
POLLPRI:   There is urgent data to read
POLLERR:   Error condition (only returned in revents; ignored in events).POLLHUP:   Hang up (only returned in revents; ignored in events). 
POLLNVAL:  Invalid request: fd not open (only returned in revents; ignored in events).
POLLRDNORM  Equivalent to POLLIN.
POLLRDBAND  Priority band data can be read (generally unused on Linux).POLLWRNOR   Equivalent to POLLOUT.POLLWRBAND  Priority data may be written.
RETURN VALUE On success, a positive number is returned; this is  the  number  of  structures  which  havenonzero  revents  fields (in other words, those descriptors with events or errors reported).A value of 0 indicates that the call timed out and  no  file  descriptors  were  ready.   Onerror, -1 is returned, and errno is set appropriately.
  1. 內核層接口:
unsigned int (*poll) (struct file *, struct poll_table_struct *);//file_operations結構體里

設置輪訓的等待隊列poll_wait:

static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{if (p && p->_qproc && wait_address)p->_qproc(filp, wait_address, p);
}

喚醒等待隊列節點wake_up_interruptible或wake_up:

#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
/*** __wake_up - wake up threads blocked on a waitqueue.* @q: the waitqueue* @mode: which threads* @nr_exclusive: how many wake-one or wake-many threads to wake up* @key: is directly passed to the wakeup function** It may be assumed that this function implies a write memory barrier before* changing the task state if and only if any tasks are woken up.*/

(三)poll使用流程

1.內核層編寫poll相關的函數

unsigned int cdev_poll (struct file * fp, struct poll_table_struct * table)

2.在合適cdev_poll函數中調用poll_wait

 poll_wait(fp,&waithead,table);//不會進行阻塞,添加等待隊列到輪詢列表中

3.在合適的地方調用wake_up_interruptible喚醒poll等待隊列

wake_up_interruptible(&waithead)

(四)實例代碼

假設要求創建多個設備節點,每個設備節點對應一個設備,在系統調用多次read接口來讀取底層設備的狀態。例如4個按鍵 ----- 4個設備節點 ----分別讀取4個設備節點文件的數據獲取按鍵的狀態-----不確定哪一個按鍵被按下
分析:
若采用阻塞方式操作:四個讀寫操作會相互阻塞,如按鍵1未被按下,則讀取不到數據,既是按鍵4被按下,也會被按鍵1的操作阻塞,獲取不到數據
若采用非阻塞操作:既是讀取不到數據,也會直接返回,對于監測按鍵的狀態,按鍵隨時可能被按下,所以需要的是循環檢測,采用非阻塞就會導致一直在進行無用功
采用多路io輪詢操作:檢測是文件描述符是否發生變化,變化就去操作對應的文件,否則阻塞在指定的操作
chrdev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/poll.h>#define CDEVCOUNT 5
#define CDEVNAME "cdevdevice"
#define INODENAME "mycdev"
int count=0;
dev_t dev=0;
int keyflag;//等待隊列第二個參數
char keyvalue[4]={-1,-1,-1,-1};
struct cdev * cdev =NULL;
struct class * cdevclass =NULL;
wait_queue_head_t  waithead;struct Key
{unsigned int gpios;char * name;int num;unsigned int irq;
};struct Key key[]={{EXYNOS4_GPX3(2),"K1",0},{EXYNOS4_GPX3(3),"K2",1},{EXYNOS4_GPX3(4),"K3",2},{EXYNOS4_GPX3(5),"K4",3},
};irqreturn_t key_handler(int irq, void * dev)
{struct Key * tmp =(struct Key *)dev;int value[4];value[tmp->num]= gpio_get_value(tmp->gpios);gpio_set_value(EXYNOS4X12_GPM4(tmp->num),value[tmp->num]);keyflag = 1;keyvalue[tmp->num] = value[tmp->num];wake_up_interruptible(&waithead);return IRQ_HANDLED;
}int cdev_open (struct inode *node, struct file *file)
{printk("cdev_open is install\n");return 0;
}
ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{int ret =0;if((fp->f_flags & O_NONBLOCK)== O_NONBLOCK)//非阻塞{if(!keyflag)return -EAGAIN;}else//阻塞{wait_event_interruptible(waithead,keyflag);}keyflag =0;ret = copy_to_user(buf,keyvalue,4);if(ret <0)return -EFAULT;printk("cdev_read is install\n");return 0;
}
ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)
{printk("cdev_write is install\n");return 0;
}
unsigned int cdev_poll (struct file * fp, struct poll_table_struct * table)
{unsigned int bitmask =0;printk("this is poll_wait before\n");poll_wait(fp,&waithead,table);//不會進行阻塞,添加等待隊列到輪詢列表中if(keyflag)//必須有判斷標志bitmask = POLLIN;printk("this is poll_wait after\n");return bitmask;
}int cdev_release (struct inode *node, struct file *fp)
{printk("cdev_release is install\n");return 0;
}
struct file_operations fop={.open=cdev_open,.read=cdev_read,.write=cdev_write,.release=cdev_release,.poll = cdev_poll,
};void mycdev_add(void)
{//1.申請設備號--動態int ret =alloc_chrdev_region(&dev,0, CDEVCOUNT, CDEVNAME);if(ret)return ;//初始化cdev結構體cdev = cdev_alloc();if(!cdev){goto out;}cdev_init(cdev,&fop);//添加字符設備到系統中ret =cdev_add(cdev,dev, CDEVCOUNT);if(ret){goto out1;}//創建設備類cdevclass = class_create(THIS_MODULE, INODENAME);if(IS_ERR(cdevclass)){goto out2;}for (count=0;count<CDEVCOUNT;count++)device_create(cdevclass, NULL, dev+count, NULL, "mydevice%d",count);out:unregister_chrdev_region(dev,CDEVCOUNT);	return ;
out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return ;
out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return ;
}static int __init  dev_module_init(void)
{int ret=0,i=0;unsigned long flags= IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING|IRQF_SHARED;for(i=0;i<4;i++){key[i].irq = gpio_to_irq(key[i].gpios);ret =request_irq(key[i].irq, key_handler,flags,key[i].name,(void *) &key[i]);}init_waitqueue_head(&waithead);//創建等待隊列頭mycdev_add();printk("this is dev_module_init \n");return 0;
}static void __exit dev_module_cleanup(void)
{int i=0;for(i=0;i<4;i++){key[i].irq = gpio_to_irq(key[i].gpios);free_irq(key[i].irq,(void *)&key[i]);}for(count=0;count<CDEVCOUNT;count++){device_destroy(cdevclass, dev+count);}class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev, CDEVCOUNT);kfree(cdev);printk("this is dev_module_cleanup\n");
}module_init(dev_module_init);
module_exit(dev_module_cleanup);
MODULE_LICENSE("GPL");

chr_app.c

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>int ret=0;
struct pollfd fds[1];
char status[]={-1,-1,-1,-1};
char key[]={-1,-1,-1,-1};
int main(int argc, char *argv[])
{int i=0,fd= open(argv[1],O_RDWR/*|O_NONBLOCK*/);if(fd== -1){perror("open");return -1;}while(1){fds[0].fd = fd;fds[0].events = POLLIN;ret = poll(fds,1,5000);//只有錯誤、滿足條件、超時才會返回,不滿足條件在這死等if(ret == -1){perror("poll\n");return -1;}else if(ret == 0){printf("timeout\n");}else if(ret & POLLIN){printf("ret >0\n");if(read(fd,status,4)<0)/*疑問:把read屏蔽掉打印出錯*/{printf("按鍵狀態未改變\n");sleep(1);continue;}for(i=0;i<4;i++){if(status[i] != key[i] ){printf("key%d is %s\n",i,status[i]?"up":"down");status[i]=key[i];}}}}close(fd);return 0;
}

Makefile

CFLAG =-C
TARGET = chrdev
TARGET1 = chr_app
KERNEL = /mydriver/linux-3.5
obj-m += $(TARGET).oall:make $(CFLAG)  $(KERNEL) M=$(PWD)arm-linux-gcc -o $(TARGET1) $(TARGET1).c
clean:make $(CFLAG)  $(KERNEL) M=$(PWD) clean

本文章僅供學習交流用禁止用作商業用途,文中內容來水枂編輯,如需轉載請告知,謝謝合作

微信公眾號:zhjj0729

微博:文藝to青年

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531409.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531409.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531409.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

校驗碼(海明校驗,CRC冗余校驗,奇偶校驗)

循環冗余校驗碼 CRC碼利用生成多項式為k個數據位產生r個校驗位進行編碼,其編碼長度為nkr所以又稱 (n,k)碼. CRC碼廣泛應用于數據通信領域和磁介質存儲系統中. CRC理論非常復雜,一般書就給個例題,講講方法.現在簡單介紹下它的原理: 在k位信息碼后接r位校驗碼,對于一個給定的(n,k…

(十二)linux內核定時器

目錄&#xff08;一&#xff09;內核定時器介紹&#xff08;二&#xff09;內核定時器相關接口&#xff08;三&#xff09;使用步驟&#xff08;四&#xff09;實例代碼&#xff08;一&#xff09;內核定時器介紹 內核定時器并不是用來簡單的定時操作&#xff0c;而是在定時時…

java Proxy(代理機制)

我們知道Spring主要有兩大思想&#xff0c;一個是IoC&#xff0c;另一個就是AOP&#xff0c;對于IoC&#xff0c;依賴注入就不用多說了&#xff0c;而對于Spring的核心AOP來說&#xff0c;我們不但要知道怎么通過AOP來滿足的我們的功能&#xff0c;我們更需要學習的是其底層是怎…

(十三)linux中斷底半部分處理機制

這篇文章介紹一下linux中斷的底半部分的tasklet和workquene兩種處理機制&#xff0c;其中tasklet中不能有延時函數&#xff0c;workquene的處理函數可以加入延時操作 目錄&#xff08;一&#xff09;tasklet小任務處理機制&#xff08;1&#xff09;tasklet相關函數接口&#x…

Codeforces Round #326 (Div. 2) B. Pasha and Phone C. Duff and Weight Lifting

B. Pasha and PhonePasha has recently bought a new phone jPager and started adding his friends phone numbers there. Each phone number consists of exactly n digits. Also Pasha has a number k and two sequences of length n?/?k (n is divisible by k) a1,?a2,?…

vmware中裝的ubuntu上不了網

本文章針對橋接方式進行講解&#xff0c;如果需要另外兩種連接方式請參考文末給出的鏈接 &#xff08;一&#xff09;問題 主機和虛擬機可以相互ping通&#xff0c;但是卻不能ping網址 &#xff08;二&#xff09;解決辦法 vmware為我們提供了三種網絡工作模式&#xff0c;…

document.getElementById()與 $()區別

document.getElementById()返回的是DOM對象&#xff0c;而$()返回的是jQuery對象 什么是jQuery對象&#xff1f; ---就是通過jQuery包裝DOM對象后產生的對象。jQuery對象是jQuery獨有的&#xff0c;其可以使用jQuery里的方法。 比如&#xff1a; $("#test").html() 意…

關于gedit的編碼問題

今天由于gedit的編碼格式導致LCD顯示屏的問題&#xff0c;開始沒有想到后來才發現&#xff0c;在這記錄一下 #include <stdio.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h>…

c語言表白程序代碼

雙十一要到了&#xff0c;好激動啊&#xff01;&#xff01;&#xff01; 是時候準備出手了&#xff01; 花了一天的時間寫的表白代碼。 表示自己弱弱的..... 看了網上好多都是js寫的&#xff0c;感覺碉堡了&#xff01;js用的不熟&#xff0c;前端不好&#xff0c;java&#x…

tiny4412移植tslib庫

1、將tslib-1.4.tar.gz拷貝到虛擬機某個路徑進行解壓 2、進入解壓路徑tslib 3、執行#./autogen.sh 如果提示&#xff1a;./autogen.sh: 4: ./autogen.sh: autoreconf: not found 原因&#xff1a;沒有安裝automake工具, 解決辦法:需要安裝此工具&#xff1a; apt-get instal…

移植QT到tiny4412開發板

目錄&#xff08;一&#xff09; 環境準備&#xff08;二&#xff09; Qt源代碼下載&#xff08;三&#xff09; 移植tslib庫&#xff08;四&#xff09;操作流程1.解壓qt源碼包2.配置編譯環境3.生成Makefile4.編譯安裝5.安裝一些庫用來支持 qt6. 添加以下內容到開發板目錄下的…

c++面試常用知識(sizeof計算類的大小,虛擬繼承,重載,隱藏,覆蓋)

一. sizeof計算結構體 注&#xff1a;本機機器字長為64位 1.最普通的類和普通的繼承 #include<iostream> using namespace std;class Parent{ public:void fun(){cout<<"Parent fun"<<endl;} }; class Child : public Parent{ public:void fun(){…

嵌入式面試題(一)

目錄1 關鍵字volatile有什么含義&#xff1f;并給出三個不同的例子2. c和c中的struct有什么不同&#xff1f;3.進程和線程區別4.ARM流水線5.使用斷言6 .嵌入式系統的定義7 局部變量能否和全局變量重名&#xff1f;8 如何引用一個已經定義過的全局變量&#xff1f;9、全局變量可…

能ping通ip但無法ping通域名和localhost //ping: bad address 'www.baidu.com'

錯誤描述&#xff1a; ~ # ping localhost ping: bad address localhost原因&#xff0c;在/etc目錄下缺少hosts文件&#xff0c;將linux中的/etc hosts文件拷入即可 ~ # ping localhost PING localhost (127.0.0.1): 56 data bytes 64 bytes from 127.0.0.1: seq0 ttl64 tim…

eclipse導入web項目之后項目中出現小紅叉解決辦法

項目中有小紅叉我遇到的最常見的情況&#xff1a; 1、項目代碼本身有問題。&#xff08;這個就不說了&#xff0c;解決錯誤就OK&#xff09; 2、項目中的jar包丟失。&#xff08;有時候eclipse打開時會出現jar包丟失的情況&#xff0c;關閉eclipse重新打開或者重新引入jar包就O…

arm開發板通過網線連接筆記本電腦上外網

需要工具&#xff1a;arm開發板&#xff0c;網線&#xff0c;一臺雙網卡的win7筆記本電腦&#xff08;筆記本電腦一般都是雙網卡&#xff09; 一、筆記本電腦需要先連上外網&#xff0c;可以連上家里的WIFI&#xff0c;或者手機開熱點&#xff08;本人未測試過連接手機的熱點&…

windows下實現Git在局域網使用

1.首先在主機A上創建一個文件夾用于存放你要公開的版本庫。然后進入這個文件夾&#xff0c;右鍵->Git create repository here&#xff0c;彈出的窗口中勾選Make it Bare&#xff01;之后將這個文件夾完全共享&#xff08;共享都會吧&#xff1f;注意權限要讓使用這個文件夾…

解決linux下QtCreator無法輸入中文的情況

安裝了QtCreator(Qt5.3.1自帶版本)后無法輸入中文&#xff0c;確切的說是無法打開輸入法。以前使用iBus輸入法的時候沒有這個問題&#xff0c;現在使用sougou輸入法才有的這個問題。 可以查看此文 http://www.cnblogs.com/oloroso/p/5114041.html 原因 有問題就得找原因&…

lintcode 滑動窗口的最大值(雙端隊列)

題目鏈接&#xff1a;http://www.lintcode.com/zh-cn/problem/sliding-window-maximum/# 滑動窗口的最大值 給出一個可能包含重復的整數數組&#xff0c;和一個大小為 k 的滑動窗口, 從左到右在數組中滑動這個窗口&#xff0c;找到數組中每個窗口內的最大值。 樣例 給出數組 [1…

你的main函數規范嗎?

在學習c語言的時候&#xff0c;有一個函數一直被我們使用&#xff0c;那就是main函數&#xff0c;但是你知道標準里面是怎么規定它的寫法嗎&#xff1f; 平時看見的main函數有下面這幾種&#xff1a; 1.int main(void){ }2.int main(){ }3.int main(int argc, char *argv[])…