役割、ポーリングメカニズム
アクション1.pollのメカニズム
キーの値を読み取るために、割り込みを使用して、前述のように(Linuxの管理割り込み(IV) )。読み込み、このボタンを使用し、ボタンが押されていない場合は、アプリケーションがスリープ状態になっています。あなたは、ボタンが押されていない場合でも、この機能を達成するために、一定期間後に返すことができますしたい場合は、使用することができます
poll机制
。(select IO复用
そしてepoll
また、ここでは、この機能、書き込み専用のポーリングメカニズムを実行することができます)
第二に、アプリケーション・プログラミング・ポーリングのためのメカニズム
1.アプリケーションインタフェースのレイヤ機能
1).API:
int型世論調査(構造体たpollfd * FDS、nfds_t nfds個、int型のタイムアウト)。
2).Paramet:
FDS | nfds個 | タイムアウト |
---|---|---|
パラメータタイプ: たpollfdストラクト{ int型FD; / *ファイルディスクリプタ/ ショートイベント; /待機するイベント型の発生/ ショートにrevents; /イベントタイプは、実際には* /返さ ;} : パラメータ説明 FDS構造は、ポインタ、すなわち、ありますイベントながら、poll関数は、1つ以上のファイルディスクリプタを待つことができます |
パラメータタイプ: nfds_t、実際には、int型の パラメータ: FDSを監視しながら、世論調査の数を説明するための |
パラメータタイプ: int型の パラメータ: 永遠の待機:-1に等しい 0に等しい:すぐに戻ります 0より大きい:タイムアウト時間をミリ秒単位で |
3).Return:
返却値 | 説明 |
---|---|
<0 | エラーリターン |
= 0 | 復帰へのタイムアウト |
> 0 | バックにreventsドメイン構造は、そのファイルディスクリプタ0の数ではありません |
2.アプリケーション
ポーリングアプリケーションの主な用途は、キーの値を読み込み、タイムアウト待ち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;
}
第三に、ドライバー
主にドライバの準備で
file_operations
のpoll
メンバー関数インタフェースを追加しますbutton_poll
。次のプログラムは、である(IV)管理割り込み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");
4:実験結果
1.前の章、コンパイラドライバやアプリケーションの手順に従ってください。その後、コンパイル済みのカーネルモジュールとアプリケーションは、ファイルシステムをコピーします。そして、
insmod xxx.ko
カーネルモジュール、アプリケーションの実行に挿入されました。
2(以下)が観察された実験結果:
- 印刷キーが押された場合:
key_val=0
- あなたは印刷するボタンを離すと:
key_val=1
- 5000ミリ秒場合は、印刷を押さないと何もキーリリースボタンはありません
time out