Linux设备驱动轮询操作--多路复用IO

应用程序中的轮询编程(多路复用IO)

应用层提供的轮询(多路复用IO)的接口有三个,分别是poll、select、epoll,这三个函数对应了驱动file_operations结构体中的函数成员:

__poll_t (*poll) (struct file *, struct poll_table_struct *);

poll函数的典型模板;

static unsigned int xxx_poll(struct file *filp, poll_table *wait)
{
    unsigned int mask = 0;
    struct xxx_dev *dev = filp->private_data;    /*   获得设备结构体指针  */

    ...
    poll_wait(filp, &dev->r_wait, wait);           /*   加入读等待队列   */
    poll_wait(filp, &dev->w_wait, wait);           /*   加入写等待队列   */

    if (...)                                       /*   可读   */
        mask |= POLLIN | POLLRDNORM;               /*   标示数据可获得(对用户可读)  */

    if (...)                                       /*   可写   */
        mask |= POLLOUT | POLLWRNORM;              /*   标示数据可写入  */
    ...
    return mask;
}

1、select

函数原型:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) 
  • nfds - 所要监视的这三类文件描述集合中,最大文件描述符加 1。

  • readfds - 监视的读文件描述符集合

  • writefds - 监视的写文件描述集合

  • exceptfds - 异常处理的文件描述符集合

  • timeout - 超时时间,在超时时间结束时,即使监视的文件描述没有反应,进程也会被唤醒。

readfds 可以设置为NULL,表示不关心任何文件的读变化,另外两个同理。

超时时间的结构体原型:

struct timeval { 
    long        tv_sec;                  /*  秒    */ 
    long        tv_usec;                /*  微妙    */   
}; 

系统提供的操作fd_set类型变量的函数:

void FD_ZERO(fd_set *set)   //清除文件描述符集合
void FD_SET(int fd, fd_set *set)   //将一个文件描述符加入文件描述符集合中
void FD_CLR(int fd, fd_set *set)  //将一个文件描述符从文件描述符集合中清除
int  FD_ISSET(int fd, fd_set *set) //判断文件描述符是否被置位
void main(void) 
{ 
    int ret, fd;                   /* 要监视的文件描述符     */ 
    fd_set readfds;               /* 读操作文件描述符集     */ 
    struct timeval timeout;      /* 超时结构体         */ 

    fd = open("dev_xxx", O_RDWR | O_NONBLOCK);   /* 非阻塞式访问 */ 

    FD_ZERO(&readfds);          /* 清除 readfds       */ 
    FD_SET(fd, &readfds);      /* 将 fd 添加到 readfds 里面 */ 
        
    /* 构造超时时间 */ 
    timeout.tv_sec = 0; 
    timeout.tv_usec = 500000;   /* 500ms   */ 
        
    ret = select(fd + 1, &readfds, NULL, NULL, &timeout); 
    switch (ret) { 
        case 0:             /* 超时     */ 
            printf("timeout!\r\n"); 
            break; 
        case -1:            /* 错误     */ 
            printf("error!\r\n"); 
            break; 
        default:      /* 可以读取数据 */ 
            if(FD_ISSET(fd, &readfds)) { /* 判断是否为 fd 文件描述符 */ 
                /* 使用 read 函数读取数据 */ 
            } 
            break; 
    }    
} 

2、poll

select功能类似。不同地方在于select在单个线程中一般能够监视的最大文件描述符数量为1024,而poll没有这个限制。

函数原型:

int poll(struct pollfd  *fds, nfds_t nfds, int  timeout) 
  • fds - 要监视的文件描述符以及要监视的事件,fd后面加s表示一个数组。

    结构体原型:

    struct pollfd { 
      int      fd;            /*  文件描述符    */ 
      short events;        /*  请求的事件    */ 
          short revents;          /*  返回的事件    */ 
    }; 
    
    //events可以是以下事件类型:
    //    POLLIN      有数据可以读取。 
    //    POLLPRI     有紧急的数据需要读取。 
    //    POLLOUT    可以写数据。 
    //    POLLERR    指定的文件描述符发生错误。 
    //    POLLHUP    指定的文件描述符挂起。 
    //    POLLNVAL    无效的请求。 
    //    POLLRDNORM  等同于 POLLIN 
    
  • nfds - 监视的文件描述符的数量,也就是fds数组的大小。

  • timeout - 超时时间

#include <poll.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    struct pollfd fds;
    int timeout_ms = 5000;
    int ret;
    char buf[20];

    fds.fd = 0;           //监视标准输入  (0文件描述符代表标准输入)           
    fds.events = POLLIN;

    ret = poll(&fds, 1, timeout_ms);      

    if (ret == 1 && (fds.events & POLLIN))     
    {
        read(0, buf, sizeof(buf));                  
        printf("get : %s\n", buf);
    }

    printf("AABBB\r\n");

    return 0;
}

3、epoll

当多路复用IO文件数量庞大,IO流量频繁的时候,而select和poll函数都是会遍历所监视的所有文件描述符,select和poll性能都会下降,而epoll的性能不会随着fd的增长而降低效率。

函数接口:

int epoll_create(int size); 
//创建epoll句柄,创建完本身会占据一个fd,所以适用完后必须调用close关闭
//size - 从 Linux2.6.8 开始此参数已经没有意义了,随便填写一个大于 0 的值就可以。 
//返回值: -1表示创建失败
int epoll_ctl(int epfd, int op,  int fd, struct epoll_event *event); 
//添加要监视的文件描述符和事件
//epfd - epoll句柄
//op - 对句柄的操作,可以是以下值:
//    EPOLL_CTL_ADD  向 epfd 添加文件参数 fd 表示的描述符。 
//	EPOLL_CTL_MOD  修改参数 fd 的 event 事件。 
//	EPOLL_CTL_DEL   从 epfd 中删除 fd 描述符。 
//fd - 要监视的文件描述符 
//event - 监视的事件,可选以下值:
//    EPOLLIN    有数据可以读取。 
//	  EPOLLOUT    可以写数据。
//    EPOLLPRI    有紧急的数据需要读取。 
//	  EPOLLERR  指定的文件描述符发生错误。 
//	  EPOLLHUP  指定的文件描述符挂起。 
//	  EPOLLET  设置 epoll 为边沿触发,默认触发模式为水平触发。 
//	  EPOLLONESHOT 一次性的监视,当监视完成以后还需要再次监视某个 fd,那么就需要将fd 重新添加到 epoll 里面。 

驱动中的轮询编程

1、poll()函数原型

unsigned int(*poll)(struct file * filp, struct poll_table* wait);

2、poll_wait()

该函数不会引起阻塞,作用将参数queue对应的等待队列上休眠的进程唤醒

void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table * wait);

3、poll()函数模板

 static unsigned int xxx_poll(struct file *filp, poll_table *wait)
 {
  	unsigned int mask = 0;
  	struct xxx_dev *dev = filp->private_data;      /*  获得设备结构体指针 */

  	...
  	poll_wait(filp, &dev->r_wait, wait);           /*  加入读等待队列  */
  	poll_wait(filp, &dev->w_wait, wait);           /*  加入写等待队列  */

  	if (...)                                       /*  可读  */
      mask |= POLLIN | POLLRDNORM;               /*  标示数据可获得(对用户可读) */

  	if (...)                                       /*  可写  */
      mask |= POLLOUT | POLLWRNORM;              /*  标示数据可写入 */
  		...
  	return mask;
}
  • 函数返回的为下列宏的组合
POLLIN      有数据可以读取。 
POLLPRI      有紧急的数据需要读取。 
POLLOUT    可以写数据。 
POLLERR    指定的文件描述符发生错误。 
POLLHUP    指定的文件描述符挂起。 
POLLNVAL    无效的请求。 
POLLRDNORM  等同于 POLLIN,普通数据可读 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲盖弥彰1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值