四、poll()、select()和epoll()
在用户程序中,poll()和select()系统调用用于对设备进行无阻塞访问。poll()和select()最终会调用设备驱动中的poll()函数,在我所使用的Linux内核中,还有扩展的poll()函数epoll()
一、poll()函数
应用程序中的poll()函数原型为:
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);
函数参数以及返回值:
fds:用于描述监听的文件描述符集
nfds:fds的数量
timeout:监听超时时间
返回值:成功返回0;出错返回-1。
示例代码如下:
struct pollfd fdsa[]; fdsa[].fd = fd; /* 监听fd */
fdsa[].events = POLLIN; /* 监听输入事件,除 */ while() {
/* 5000ms内若有输入,返回大于0的数;否则返回0 */
ret = poll(&fdsa[], , );
if (!ret)
printf("time out\n");
else {
read(fd, buf, );
printf("buf = %d\n", buf[]);
}
}
现在,我们来看看poll()函数的调用过程:
SYSCALL_DEFINE3(poll, ...)
-> do_sys_poll(ufds, nfds, to);
-> poll_initwait(&table); // 初始化等待队列
-> do_poll(nfds, head, &table, end_time);
-> do_pollfd(pfd, pt, &can_busy_loop, busy_flag); // 处理进程的每一个fd的poll操作
-> f.file->f_op->poll(f.file, pwait); // 执行驱动程序的poll()函数
二、select()函数
应用程序中的select()函数原型为:
#include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout); void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
select()函数中参数nfs表示监听所有的fd的最大值 + 1;readfds、writefds和exceptfds分别是被监听的读、写和异常的文件描述符集;timeout表示监听超时时间,结构体如下:
struct timeval {
__kernel_time_t tv_sec; /* 秒 */
__kernel_suseconds_t tv_usec; /* 微秒 */
};
FD_SET()、FD_ZERO()、FD_CLR()、FD_ISSET()分别用于加入fd、清除fd集合、清除fd、判断fd是否被加入集合中
示例代码如下:
fd_set rfds; FD_ZERO(&rfds);
FD_SET(fd, &rfds); tv.tv_sec = ;
tv.tv_usec = ; // 设置等待时间5s ret = select(fd + , &rfds, NULL, NULL, &tv); if (ret > ) {
if(FD_ISSET(fd, &rfds)) /* 测试是否有数据 */ {
read(fd, buf, );
printf("buf = %d\n", buf[]);
}
}
select()函数的调用过程:
SYSCALL_DEFINE5(select, ...)
-> core_sys_select(n, inp, outp, exp, to);
-> do_select(n, &fds, end_time); //
-> poll_initwait(&table); // 初始化等待队列
-> mask = (*f_op->poll)(f.file, wait); // 执行驱动程序的poll()函数
当poll()和select()的文件数量庞大、I/O流量频繁时,poll()和select()的性能表现较差,我们宜使用epoll(),epoll()不会随着fd的数目增长而降低效率
三、epoll()函数
epoll()函数原型为:
#include <sys/epoll.h> /* 创建epoll文件描述符 */
int epoll_create(int size); /* 添加、修改或删除需要监听的文件描述符及其事件 */
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); /* 等待被监听的描述符的I/O事件 */
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
代码中maxevents表示每次能处理的事件数
代码中的struct epoll_event声明为:
struct epoll_event {
uint32_t events; /* epoll事件 */
epoll_data_t data; /* epoll数据 */
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
示例代码如下:
int efd, nfds, i;
struct epoll_event event; efd = epoll_create();
if (efd == -) {
return -;
} event.data.fd = fd;
/*
* EPOLLIN: 表示对应的文件描述符可以读
* EPOLLOUT: 表示对应的文件描述符可以写
* EPOLLET: 表示对应的文件描述符有事件发生
*/
event.events = EPOLLIN | EPOLLET; /* 除EPOLL_CTL_ADD之外还有EPOLL_CTL_DEL(删除)和EPOLL_CTL_MOD(修改) */
s = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); while () {
nfds = epoll_wait(epfd, event, , ); for (i = ; i < nfds; ++i) {
if (event[i].events & EPOLLIN) /* 有数据可读 */ {
read(event[i].data.fd, buf, );
printf("buf = %d\n", buf[]);
}
}
}
epoll()系列函数的调用过程:
/* epoll_create() */
SYSCALL_DEFINE1(epoll_create, int, size)
-> sys_epoll_create1();
-> evetpoll_init(); /* epoll_ctl() */
SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
struct epoll_event __user *, event)
-> case EPOLL_CTL_ADD:
-> ep_insert(ep, &epds, tfile, fd);
-> tfile->f_op->poll(tfile, &epq.pt); /* 调用驱动的poll()函数 */ /* epoll_wait() */
SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
int, maxevents, int, timeout)
-> ep_poll(ep, events, maxevents, timeout);
-> 判断timeout
四、poll()、select()和epoll()的区别

五、驱动程序的poll()函数
poll()函数需要#include <linux/poll.h>
为了更方便演示poll()函数,我在代码中加入了一个全局变量ev_press,如果有按键按下置1;然后重新置0
static struct file_operations key_fops = {
.owner = THIS_MODULE,
.read = key_read,
.poll = key_poll, /* 加入poll()函数 */
.open = key_open,
.release = key_release,
};
poll()函数示例如下:
static unsigned int key_poll(struct file *filp, struct poll_table_struct *table)
{
struct key_device *dev = filp->private_data; unsigned int mask = ; poll_wait(filp, &dev->r_head, table); if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
}
代码中的poll_wait()并不会像wait_event()系列函数一样阻塞地等待事件发生,poll_wait()并不会引起阻塞。它只是把当前进程加入到poll_table_struct等待列表
key源代码:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/poll.h> #include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h> #include <mach/gpio.h> #define KEY_MAJOR 255 struct pin_desc {
int gpio;
int val;
char *name;
}; struct key_device {
struct cdev cdev;
wait_queue_head_t r_head;
wait_queue_head_t w_head;
}; static struct pin_desc desc[] = {
{ EXYNOS4_GPX3(), 0x01, "KEY0" },
{ EXYNOS4_GPX3(), 0x02, "KEY1" },
{ EXYNOS4_GPX3(), 0x03, "KEY2" },
{ EXYNOS4_GPX3(), 0x04, "KEY3" },
}; static int g_major = KEY_MAJOR;
module_param(g_major, int, S_IRUGO); static struct key_device* dev;
static struct class* scls;
static struct device* sdev;
static unsigned char key_val;
static volatile int ev_press = ; static irqreturn_t key_interrupt(int irq, void *dev_id)
{
struct pin_desc *pindesc = (struct pin_desc *)dev_id;
unsigned int tmp; tmp = gpio_get_value(pindesc->gpio); /* active low */
printk(KERN_DEBUG "KEY %d: %08x\n", pindesc->val, tmp); if (tmp)
key_val = pindesc->val;
else
key_val = pindesc->val | 0x80; set_current_state(TASK_RUNNING); ev_press = ; return IRQ_HANDLED;
} static ssize_t key_read(struct file *filp, char __user *buf, size_t len, loff_t * loff)
{
struct key_device *dev = filp->private_data; // 声明等待队列
DECLARE_WAITQUEUE(rwait, current);
add_wait_queue(&dev->r_head, &rwait); // 休眠
__set_current_state(TASK_INTERRUPTIBLE);
schedule(); // 有数据
copy_to_user(buf, &key_val, ); remove_wait_queue(&dev->r_head, &rwait);
set_current_state(TASK_RUNNING); ev_press = ; return ;
} static unsigned int key_poll(struct file *filp, struct poll_table_struct *table)
{
struct key_device *dev = filp->private_data; unsigned int mask = ; poll_wait(filp, &dev->r_head, table); if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static int key_open(struct inode *nodep, struct file *filp)
{
struct key_device *dev = container_of(nodep->i_cdev, struct key_device, cdev);
// 放入私有数据中
filp->private_data = dev; int irq;
int i, err = ; for (i = ; i < ARRAY_SIZE(desc); i++) {
if (!desc[i].gpio)
continue; irq = gpio_to_irq(desc[i].gpio);
err = request_irq(irq, key_interrupt, IRQ_TYPE_EDGE_BOTH,
desc[i].name, (void *)&desc[i]);
if (err)
break;
} if (err) {
i--;
for (; i >= ; i--) {
if (!desc[i].gpio)
continue; irq = gpio_to_irq(desc[i].gpio);
free_irq(irq, (void *)&desc[i]);
}
return -EBUSY;
} init_waitqueue_head(&dev->r_head); return ;
} static int key_release(struct inode *nodep, struct file *filp)
{
// 释放中断
int irq, i; for (i = ; i < ARRAY_SIZE(desc); i++) {
if (!desc[i].gpio)
continue; irq = gpio_to_irq(desc[i].gpio);
free_irq(irq, (void *)&desc[i]);
} return ;
} static struct file_operations key_fops = {
.owner = THIS_MODULE,
.read = key_read,
.poll = key_poll,
.open = key_open,
.release = key_release,
}; static int keys_init(void)
{
int ret;
int devt;
if (g_major) {
devt = MKDEV(g_major, );
ret = register_chrdev_region(devt, , "key");
}
else {
ret = alloc_chrdev_region(&devt, , , "key");
g_major = MAJOR(devt);
} if (ret)
return ret; dev = kzalloc(sizeof(struct key_device), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
goto fail_alloc;
} cdev_init(&dev->cdev, &key_fops);
ret = cdev_add(&dev->cdev, devt, );
if (ret)
return ret; scls = class_create(THIS_MODULE, "key");
sdev = device_create(scls, NULL, devt, NULL, "key"); return ; fail_alloc:
unregister_chrdev_region(devt, ); return ret;
} static void keys_exit(void)
{
dev_t devt = MKDEV(g_major, ); device_destroy(scls, devt);
class_destroy(scls); cdev_del(&(dev->cdev));
kfree(dev); unregister_chrdev_region(devt, );
} module_init(keys_init);
module_exit(keys_exit); MODULE_LICENSE("GPL");
Makefile:
KERN_DIR = /work/tiny4412/tools/linux-3.5 all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += key.o
测试文件:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <string.h> int main(int argc, char** argv)
{
int fd, ret;
fd = open("/dev/key", O_RDWR);
if (fd < ) {
printf("can't open /dev/key\n");
return -;
} unsigned char key_val; fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds); struct timeval tv;
tv.tv_sec = ;
tv.tv_usec = ; while () {
ret = select(fd + , &rfds, NULL, NULL, &tv); if(ret == ) /* 超时 */ {
printf("select time out!\n");
break;
}
else if (ret > ) {
if (FD_ISSET(fd, &rfds)) {
read(fd, &key_val, );
printf("key_val = 0x%x\n", key_val);
}
}
} close(fd); return ;
}
下一章 五、并发控制
四、poll()、select()和epoll()的更多相关文章
- IO多路复用(select、poll、epoll)介绍及select、epoll的实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- Epoll,Poll,Select模型比较
http://blog.csdn.net/liangyuannao/article/details/7776057 先说Select: 1.Socket数量限制:该模式可操作的Socket数由FD_S ...
- /dev/poll, kqueue(2), event ports, POSIX select(2), Windows select(), poll(2), and epoll(4)
/dev/poll, kqueue(2), event ports, POSIX select(2), Windows select(), poll(2), and epoll(4) libevent ...
- 轮询、select、 epoll
网卡设备对应一个中断号, 当网卡收到网络端的消息的时候会向CPU发起中断请求, 然后CPU处理该请求. 通过驱动程序 进而操作系统得到通知, 系统然后通知epoll, epoll通知用户代码. 一. ...
- [转]谈谈select, iocp, epoll,kqueue及各种网络I/O复用机制
参考原文:再谈select, iocp, epoll,kqueue及各种I/O复用机制 一.I/O模型概述 介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking ...
- Select与Epoll比较
一.问题引出 联系区别 问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一个描述符上面,另外的描述符虽然有数据但是不能读出来,这样实时性不能满足要求,大概的解 ...
- 【网络】再谈select, iocp, epoll,kqueue及各种I/O复用机制 && Reactor与Proactor的概念
首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal drive ...
- 网络编程基础——学习阻塞,非阻塞(select和epoll)
<h3 class="xyn" helvetica="" neue',="" helvetica,="" aria ...
- 多路复用select和epoll的区别(转)
先说下本文框架,先是问题引出,然后概括两个机制的区别和联系,最后介绍每个接口的用法 一.问题引出 联系区别 问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一 ...
随机推荐
- Flask上下文源码分析(二)
前面第一篇主要记录了Flask框架,从http请求发起,到返回响应,发生在server和app直接的过程. 里面有说到,Flask框架有设计了两种上下文,即应用上下文和请求上下文 官方文档里是说先理解 ...
- Python 自学笔记(八)
import math def A(a,b): print("第一个参数的值为"+str(a)) print("第一个参数的值为"+str(b)) a = 1 ...
- insmod某个内核模块时提示“Failed to find the folder holding the modules”如何处理?
答: 创建/lib/modules/$(uname -r)目录,命令如下: mkdir /lib/modules/$(uname -r)
- rand随机函数
1.rand() rand()函数是使用线性同余法做的,它并不是真的随机数,因为其周期特别长,所以在一定范围内可以看成随机的. rand()函数不需要参数,它将会返回0到RAND_MAX之间的任意的整 ...
- PHP 美化输出数组
var_export — 输出或返回一个变量的字符串表示 此函数返回关于传递给该函数的变量的结构信息,它和 var_dump() 类似,不同的是其返回的表示是合法的 PHP 代码. 您可以通过将函数的 ...
- 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_16-认证接口开发-Api接口定义
4.3 认证服务 4.3.1 需求分析 认证服务需要实现的功能如下: 1.登录接口 前端post提交账号.密码等,用户身份校验通过,生成令牌,并将令牌存储到redis. 将令牌写入cookie. 2. ...
- InfluxDB+Grafana大数据监控系列之基础环境部署(一)
一.单节点环境部署 机器节点信息及 InfluxDB.Grafana 版本选择: 节点 Linux版本 部署服务 10.223.1.198 Centos 6.8 InfluxDB 1.7.7 10.2 ...
- (四)UML之顺序图(时序图)
一.概念 顺序图是交互图的一种形式,它显示对象沿生命线发展,对象之间随时间的交互表示为从源生命线指向目标生命线的消息.顺序图能很好地显示那些对象与其它那些对象通信,什么消息触发了这些通信,顺序图不能很 ...
- PHP实现简单留言板
最近学习了下PHP基础,这里做一个简单的留言板,算是对PHP和MySQL的使用做一个整体的练习吧,不遇到问题总感觉学不到东西. 截图如下: 总结: 1>数据库的简单操作,数据库的增删改查: 2. ...
- MongoDB集群之分片技术应用 —— 学习笔记
课程链接:https://www.imooc.com/learn/501 一.什么是分片? 分片:将数据进行2拆分,将数据水平的分散到不同的服务器上. 二.为什么要分片? 架构上:读写均衡.去中心化 ...