一,IO多路复用
1.IO多路复用模型
2.poll的应用
1.poll函数:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
【头文件包含】:#include <poll.h>
【参数】:
参数1:表示多个文件描述符的集合,用于描述fd的信息
struct pollfd {
int fd; /* 文件描述符*/
short events; /* 希望监控fd的什么事件 */
short revents; /* 结果描述,表示当前的fd是否有读,写,出错 */
};
POLLIN 读
POLLOUT 写
POLLERR 出错
参数2 :被监控的fd的个数
参数3:监控的时间
正 ------>表示监控多少微秒
负 ------>表示无限时间去监控
0 ------>表示不等待,非阻塞
【返回值】:
大于0 ------>表示fd中有数据
负数 ------>表示出错
0 ------>表示时间到
2.poll应用编程
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#define KEY_ENTER 28
struct key_event{
int code; //表示按键的类型
int value; //表示按键的按下还是抬起
};
static int fd;
static struct key_event event;
int main(int argc, char const *argv[])
{
char in_buf[128];
int ret;
int fd = open("/dev/key0",O_RDWR);
if(fd < 0)
{
perror("open");
exit(-1);
}
//实现IO多路复用
//监控多个设备
struct pollfd pfd[2];
pfd[0].fd = fd; //监控按键设备
pfd[0].events = POLLIN; //读
pfd[1].fd = 0; //监控标准输入
pfd[1].events = POLLIN; //读
while(1)
{
ret = poll(pfd,2,-1);
if(ret > 0)
{
printf("ret = %d\n",ret);
//表示两个fd中至少有一个发生了数据可读
if(pfd[0].revents & POLLIN)
{
//可以去读数据
read(pfd[0].fd,&event,sizeof(struct key_event));
if(event.code == KEY_ENTER)
{
if(event.value)
{
printf("APP_KEY pressed\n");
}
else
{
printf("APP_KEY up\n");
}
}
}
if(pfd[1].revents & POLLIN)
{
fgets(in_buf,128,stdin);
printf("in_buf = %s\n",in_buf);
}
}
else
{
perror("poll");
exit(-1);
}
}
close(pfd[0].fd);
return 0;
}
2.poll驱动接口的实现
//描述按键的信息
struct key_desc{
unsigned int dev_major;
struct class* cls;
struct device* dev;
int irqno;
void *reg_base;
struct key_event event;
wait_queue_head_t wq_head;
int key_state; //表示是否有数据
struct fasync_struct * fasync;
struct tasklet_struct mytasklet;
};
struct key_desc* key_dev;
const struct file_operations key_fops = {
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_close,
.poll = key_dev_poll, //增加poll接口
.fasync = key_dev_fasync,
};
//实现驱动里的poll接口
unsigned int key_dev_poll (struct file *filp, struct poll_table_struct *pts)
{
printk("----%s----\n",__FUNCTION__);
//返回一个mask值
unsigned int mask;
//调用poll_wait将当前等待队列注册到系统中
poll_wait(filp, &key_dev->wq_head,pts);
// 1,没有数据的时候返回一个0
if(!key_dev->key_state)
{
mask = 0;
}
// 2,当有数据的时候返回一个POLLIN
if(key_dev->key_state)
{
mask |= POLLIN;
}
return mask;
}
二,异步信号通知
1.应用-------处理信号,主要是读写数据
- 1,设置信号处理方法
signal(SIGIO,catch_signale);
- 2,将当前进程设置成SIGIO的属主进程
fcntl(fd,F_SETOWN,getpid());
- 3,把IO模式设置成异步模式
int flags = fcntl(fd,F_GETFL);
fcntl(fd,F_GETFL,flags | FASYNC);
具体代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#define KEY_ENTER 28
struct key_event{
int code; //表示按键的类型
int value; //表示按键的按下还是抬起
};
static int fd;
static struct key_event event;
void catch_signale(int signo)
{
printf("we got signal SIGN\n");
//读取数据
if(signo == SIGIO)
{
read(fd,&event,sizeof(struct key_event));
if(event.code == KEY_ENTER)
{
if(event.value)
{
printf("APP_KEY pressed\n");
}
else
{
printf("APP_KEY up\n");
}
}
}
}
int main(int argc, char const *argv[])
{
char in_buf[128];
int ret;
int fd = open("/dev/key0",O_RDWR);
if(fd < 0)
{
perror("open");
exit(-1);
}
//1,设置信号处理方法
signal(SIGIO,catch_signale);
//2,将当前进程设置成SIGIO的属主进程
fcntl(fd,F_SETOWN,getpid());
//3,把IO模式设置成异步模式
int flags = fcntl(fd,F_GETFL);
fcntl(fd,F_GETFL,flags | FASYNC);
while(1)
{
//可以做其他事情
printf("I am waiting.\n");
sleep(1);
}
close(fd);
return 0;
}
2.驱动--------发送信号
- 需要和进程关联-------->记录信号该发送给谁,即要在驱动中实现一个fasync接口
//描述按键的信息
struct key_desc{
unsigned int dev_major;
struct class* cls;
struct device* dev;
int irqno;
void *reg_base;
struct key_event event;
wait_queue_head_t wq_head;
int key_state; //表示是否有数据
struct fasync_struct * fasync;
struct tasklet_struct mytasklet;
};
struct key_desc* key_dev;
int key_dev_fasync (int fd, struct file *filp, int on)
{
//只需要调用一个函数记录信号该发送给谁
return fasync_helper(fd, filp,on, &key_dev->fasync);
}
const struct file_operations key_fops = {
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_close,
.poll = key_dev_poll,
.fasync = key_dev_fasync,
};
- 在某个特定的时候去发送信号----------->有数据来的时候,即触发中断的函数里
irqreturn_t key_irq_handler (int irq, void * dev_id)
{
printk("------%s----\n",__FUNCTION__);
int value = readl(key_dev->reg_base) & (1<<2);
if(value)
{
//按键抬起
printk("key up\n");
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 0;
}
else
{
//按键按下
printk("key press\n");
key_dev->event.code = KEY_ENTER;
key_dev->event.value = 1;
}
//发送信号
kill_fasync(&key_dev->fasync, SIGIO, POLLIN);
return IRQ_HANDLED;
}
继续参考下一篇博客:Linux驱动开发之按键驱动(3)--------- 中断下半部的实现方法