linux poll機制使用(一)

#1、poll機制的做用html

1.poll機制的做用

在前面的使用中斷的的方式來讀取按鍵值(linux 中斷管理(四))。使用這種方式讀取按鍵,若是按鍵沒有按下的時候,應用程序會一直處於睡眠的狀態。若是想要即便按鍵沒有按下,在必定的時間後也能返回,要實現這種功能,可使用poll機制。(select IO複用epoll也能夠實現這種功能,這裏只寫poll機制) #2、poll機制的應用編程 ##1.應用層函數接口 ###1).API:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);node

###2).Paramet:linux

fds nfds timeout
參數類型:<br>    struct pollfd {<br>    int fd; /* 文件描述符 / <br>    short events; / 等待的發生事件類型 / <br>    short revents; / 實際返回的事件類型 */ <br>    };<br>參數描述:<br>    fds是一個結構體指針,也就是poll函數能夠同時等待一個或者多個文件描述符的事件 參數類型:<br>    nfds_t,其實就是int型<br>參數描述:<br>    用來講明poll同時監聽fds的個數 參數類型:<br>    int<br>參數描述:<br>    等於-1:永久等待<br>    等於0:當即返回<br>    大於0:等待超時時間,以毫秒爲單位
###3).Return:
返回值 描述
<0 錯誤返回
=0 超時返回
>0 返回結構體中 revents 域不爲 0 的文件描述符個數
##2.應用程序

應用程序主要使用poll的方式讀取按鍵的值,而且設置5000ms超時等待編程

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>


/* poll機制測試 
 */
int main(int argc, char **argv)
{
	int fd;
	unsigned char key_val;
	int ret;
	/* 定義一個 struct pollfd 結構體數組*/
	struct pollfd fds[1];
	/* 打開一個設備文件 */
	fd = open("/dev/my_button", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	/**************** 初始化 struct pollfd 結構體 *************/
	/* 初始化文件描述符 */
	fds[0].fd     = fd;
	/* 初始化監聽的事件事件 */
	fds[0].events = POLLIN;
	while (1)
	{
		/* 調用poll函數,超時時間是5000ms */
		ret = poll(fds, 1, 5000);
		
		if (ret == 0){
			/* 超時返回 */
			printf("time out\n");
		}else if(ret<0){
			/* 出錯返回 */
		printf("poll error\n"); 
		}else{     
			/* 有數據可讀,讀取數據 */
			/* 這裏爲了簡單就不對返回的事件revents,作判斷和重置了 */
			read(fd, &key_val, 1);
			printf("key_val = %d\n", key_val);
		}
	}
	
	return 0;
}

#3、驅動程序數組

驅動程序的編寫主要在file_operationspoll成員添加一個函數接口button_poll。下面的程序是在linux中斷管理(四)更改的(不過本章節只使用了一個按鍵)。更改的代碼以下:函數

#include <linux/device.h> 
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/poll.h>


/* 申請一個等待隊列頭 */
DECLARE_WAIT_QUEUE_HEAD (button_waitq);
/* 事件觸發標誌 */
int ev_press;


/* 用一個結構體來描述一個按鍵,抽象的或許不是很好 */
struct but_irq_desc{
    /* 中斷名稱 */
    char *button_nane;
    /* 按鍵的狀態,1鬆開,0按下 */
    unsigned char key_state;
    /* 按鍵所接的GPIO口 */
    unsigned long pin;
    /* 按鍵的中斷號 */
    unsigned int irq;   
};


struct but_irq_desc but_irq_descs={
    .button_nane="button1",
    .key_state = 1,
    .pin = S3C2410_GPF0,
    .irq=IRQ_EINT0,
};

static struct cdev *button_cdev;
static struct class *button_class;


struct file_operations *button_fops;

/* 中斷處理函數 */
static irqreturn_t button_irq(int irq, void * dev_id)
{
    /* 獲取這個按鍵的結構體 */
    struct but_irq_desc *btndesc = (struct but_irq_desc *)dev_id;
    unsigned int pinval;
    /* 讀取按鍵的電平 */
    pinval = s3c2410_gpio_getpin(btndesc->pin);
    /* 若是是高電平*/
    if(pinval){
        /* 按鍵鬆開 */
        btndesc->key_state = 1;
    }else{
        btndesc->key_state = 0;
        /* 按鍵按下 */
    }
    /* 喚醒該等待隊列裏的進程 */
    wake_up_interruptible(&button_waitq);
    /* 將標誌置1 */
    ev_press = 1;
    return IRQ_HANDLED;
}

int button_open (struct inode * inode, struct file *file){
    /* 註冊驅動,中斷爲上升沿和降低沿觸發 */
	request_irq(but_irq_descs.irq, button_irq,\
		IRQF_TRIGGER_RISING| IRQF_TRIGGER_FALLING,\
		but_irq_descs.button_nane,(void*)(&but_irq_descs));
    return 0;
}
int button_release (struct inode *inode, struct file * file){
    /* 釋放中斷 */
    free_irq(but_irq_descs.irq,(void*)(&but_irq_descs));
    return 0;
}

/* 驅動讀函數 */
ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t * offset){
    unsigned char key_val;
    if(size!=1){
        return -EINVAL;
    }
    /* 等待按鍵按下,若是按鍵按下,則這個進程會被喚醒(之後有時間系一章等待隊列的源碼分析) */
    /* 若是ev_press等於1的時候則這個進程不會被掛起,等於0的時候這個進程纔會被掛起 */
    wait_event_interruptible(button_waitq, ev_press);
    key_val = but_irq_descs.key_state;
    /* 將按鍵值返回給應用層 */
    copy_to_user(buf, &key_val, 1);
    /* 將標誌置0將進程掛起,等待下一次喚醒 */
    ev_press = 0;
    return 1;
}





/*********************本章中增長的函數****************************/
static unsigned int button_poll(struct file *file, poll_table *wait)
{
	unsigned int res = 0;
	poll_wait(file, &button_waitq, wait);
	/************************ poll_wait函數定義以下 **********************/
	/* 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);
     *         //本質調用的是__pollwait(filp, wait_address, p);這個函數
	 * }
	 */
	/***********************************************************************/
	
	/* p->qproc 是一個函數指針,指向 __pollwait函數。
	 * __pollwait 作的事情也就是將當前的進程添加到 button_waitq 等待隊列中。
	 * 這裏只是添加到等待隊列而已,並不會當即休眠。
	 * (下一章分析poll機制源碼的時候會詳細分析)
	 */
	 
	/* 若是當前有按鍵按下,則返回POLLIN 和 POLLRDNORM事件,
	 * 不然返回0
	 * 返回非0則當前的進程不會休眠,返回0當前的進程會休眠
	 */
	if (ev_press)
		res = POLLIN | POLLRDNORM;
	/* 返回res */
	return res;
}
/*********************本章中增長的函數****************************/

static dev_t dev_id;

/* 模塊入口函數 */
static int __init my_button_init(void){
    

    /* 分配一個file_operations 結構體 */
    button_fops = kmalloc(sizeof(struct file_operations), GFP_KERNEL);
    /* 初始化接口函數 */
    button_fops->open = button_open;
    button_fops->release = button_release;
    button_fops->read = button_read;
    button_fops->poll = button_poll;
    /* 動態分配一個設備號 */
    alloc_chrdev_region(&dev_id, 0, 1, "my_button");
    /* 分配一個cdev結構體 */
    button_cdev = cdev_alloc();
    /* 將cdev結構體和 fops結構體綁定*/
    cdev_init(button_cdev, button_fops);
    /* 將驅動註冊到內核中 */
    cdev_add(button_cdev, dev_id,1);
    /* 建立一個class */
    button_class = class_create(THIS_MODULE, "my_button");
    /* 根據class內容建立一個設備節點 my_button*/
    class_device_create(button_class, NULL, dev_id, NULL,"my_button");
    return 0;
}



/* 模塊出口函數 */
static void __exit my_button_exit(void){
    
    /* 銷燬設別節點 */
    class_device_destroy(button_class, dev_id);
    /* 銷燬設備節點 */
    class_destroy(button_class);
    /* 釋放cdev結構體空間 */
    cdev_del(button_cdev);
    /* 註銷設備號 */
    unregister_chrdev_region(dev_id,1);
    /* 釋放fops空間 */
    kfree(button_fops);
    
}

/* 聲明模塊入口 */
module_init(my_button_init);
/* 聲明模塊出口 */
module_exit(my_button_exit);
/* 遵循GPL協議 */
MODULE_LICENSE("GPL");

#四:實驗現象源碼分析

1.按照前面的幾章的步驟,編譯驅動程序和應用程序。而後將編譯好的內核模塊和應用程序copy到文件系統。而後使用insmod xxx.ko插入內核模塊,執行應用程序。 2.觀察實驗現象(以下圖):測試

  • 按鍵按下時打印:key_val=0
  • 按鍵鬆開時打印:key_val=1
  • 若是5000毫秒後,沒有按鍵按下也沒有按鍵鬆開則打印time out
相關文章
相關標籤/搜索