在用户程序中,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()的更多相关文章

  1. IO多路复用(select、poll、epoll)介绍及select、epoll的实现

    IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...

  2. Epoll,Poll,Select模型比较

    http://blog.csdn.net/liangyuannao/article/details/7776057 先说Select: 1.Socket数量限制:该模式可操作的Socket数由FD_S ...

  3. /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 ...

  4. 轮询、select、 epoll

    网卡设备对应一个中断号, 当网卡收到网络端的消息的时候会向CPU发起中断请求, 然后CPU处理该请求. 通过驱动程序 进而操作系统得到通知, 系统然后通知epoll, epoll通知用户代码.  一. ...

  5. [转]谈谈select, iocp, epoll,kqueue及各种网络I/O复用机制

    参考原文:再谈select, iocp, epoll,kqueue及各种I/O复用机制 一.I/O模型概述 介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking ...

  6. Select与Epoll比较

    一.问题引出 联系区别 问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一个描述符上面,另外的描述符虽然有数据但是不能读出来,这样实时性不能满足要求,大概的解 ...

  7. 【网络】再谈select, iocp, epoll,kqueue及各种I/O复用机制 && Reactor与Proactor的概念

    首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal drive ...

  8. 网络编程基础——学习阻塞,非阻塞(select和epoll)

    <h3 class="xyn" helvetica="" neue',="" helvetica,="" aria ...

  9. 多路复用select和epoll的区别(转)

    先说下本文框架,先是问题引出,然后概括两个机制的区别和联系,最后介绍每个接口的用法 一.问题引出 联系区别 问题的引出,当需要读两个以上的I/O的时候,如果使用阻塞式的I/O,那么可能长时间的阻塞在一 ...

随机推荐

  1. lucene正向索引(续)——每次commit会形成一个新的段,段"_1"的域和词向量信息可能存在"_0.fdt"和"_0.fdx”中

    DocStoreOffset DocStoreSegment DocStoreIsCompoundFile 对于域(Stored Field)和词向量(Term Vector)的存储可以有不同的方式, ...

  2. 计算机基础——Java笔记一

            电子管-晶体管 摩尔定律 18个月变一次       贝尔实验室 C语言是基础.芯片领域软件领域 机器语言 ——汇编语言——高级语言 (面向过程,面向对象)   基本的逻辑怎么用代码实 ...

  3. Django 测试开发4 Django 模板和分页器

    Django结合前端框架Bootstrap来开发web页面.pip install django-bootstrap3 在setting.py添加‘bootstrap3’. 继承模板. 在base页面 ...

  4. O(n) O(log n) blist: an asymptotically faster list-like type for Python

    https://pypi.org/project/blist/ blist: an asymptotically faster list-like type for Python — blist 1. ...

  5. PorterDuffXfermode之Mode.SRC_IN

    package com.loaderman.customviewdemo.view; import android.content.Context; import android.graphics.B ...

  6. mysql无法导入函数和存储过程解决方法

    1. mysql> SET GLOBAL log_bin_trust_function_creators = 1; 2. 系统启动时 --log-bin-trust-function-creat ...

  7. form 提交页面不刷新实现

    // no redirect <!DOCTYPE html> <html> <head> <meta http-equiv="Content-typ ...

  8. 重新学习微信小程序

    基础学习: 传送门:http://www.jianshu.com/p/1cec15a81722 这个简书博客介绍的很详细,今天思思重新学习了一下. 一路到最后只遇到一个坑,还是自己不仔细.这里记录下: ...

  9. 第十八章 并发登录人数控制——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录:要么踢出前者登录(强制退出).比如spring securi ...

  10. Spring Aop(十六)——编程式的自定义Advisor

    转发:https://www.iteye.com/blog/elim-2399437 https://www.iteye.com/blogs/subjects/springaop 编程式的自定义Adv ...