Linux驱动开发 | 多路复用+中断

本文深入探讨了多路复用的概念及其在Linux环境下的应用,通过对比阻塞和非阻塞IO模型,解释了多路复用如何提高IO操作效率。详细介绍了poll函数的使用方法,包括其参数、返回值及在应用程序中的具体实现,展示了如何同时监控标准输入流和设备输入流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用阻塞模型虽然可以避免等待数据过程中,CPU对进程的消耗,但是仅仅是为了等待这一个结果,就让进程进入休眠,对于还要进行其他IO操作的进程而言太“奢侈”。所以引入多路复用的概念,解决这个问题。

非阻塞:立即返回结果,如果想得到期望的结果,要不停的调用这个方法(轮询),非常耗费资源

阻塞:没有得到真正的数据前,不返回结果。此时,进程进入阻塞(休眠)态,直到有数据唤醒进程,这个过程不耗资源。

多路复用:和原理和阻塞类似,不过多路复用是同时进行多个IO读写操作。这样就减少了在等待时间上的浪费,提高效率。

多路复用要对文件描述符进行操作,在应用层上进行实现。关于多路复用在应用层上的使用,之前我有总结过一份笔记。这里使用poll函数完成;应用中使用poll对设备文件进行了监控,那么设备驱动就必须实现poll接口。所以...

  1. poll函数实现对标准输入流(按键)与设备输入流(按键)的监控
  2. 在设备驱动中实现poll的接口

Linux环境下使用`man 2 poll`查看poll函数的介绍,程序包含头文件poll.h

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数1: 表示多个文件描述符集合
    struct pollfd描述的是文件描述符到信息
    struct pollfd {
       int   fd;  //文件描述符
       short events;   //希望监控fd的什么事件:读,写,出错
                    POLLIN 读,
                    POLLOUT 写,
                    POLLERR出错
       short revents;    //结果描述,表示当前的fd是否有读,写,出错
                    //用于判断,是内核自动赋值
                    POLLIN 读,
                    POLLOUT 写,
                    POLLERR出错
    };
参数2:被监控到fd的个数
参数3: 监控的时间:
            正: 表示监控多少ms
            负数: 无限的时间去监控
            0: 等待0ms,类似于非阻赛
返回值: 负数:出错
        大于0,表示fd中有数据
        等于0: 时间到 

应用程序

//key_test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>




struct key_event{
	int code; // 按键的类型
	int value; // 状态
};


#define KEY_ENTER		28

int main(int argc, char *argv[])
{
	int ret;
	char in_buf[128];	//接收标准输入数据
	struct key_event event;	//接收按键设备数据
	
	int fd = open("/dev/key0", O_RDWR);
	
	if(fd < 0)
	{
		perror("open");
		exit(1);
	}
	//描述2个文件描述符,标准输入0 和 按键设备结点fd
	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); // 多路复用 poll 同时监控按键与标准输入
			printf("ret = %d\n", ret);
			
			if(ret > 0)
			{//表示有数据
				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 enter pressed\n");
						}else
						{
							printf("APP__ key enter 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;

}



设备驱动文件

1.在file_operations中 poll指向key_drv_poll函数

const struct file_operations key_fops = {
	.open = key_drv_open,
	.read = key_drv_read,
	.write = key_drv_write,
	.release = key_drv_close,
	.poll = key_drv_poll,

};

2.在key_drv_poll函数中实现接口

unsigned int key_drv_poll(struct file *filp, struct poll_table_struct *pts)
{
	
	// 返回一个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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hinzer

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

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

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

打赏作者

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

抵扣说明:

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

余额充值