linux驱动开发
IO模型
阻塞IO
应用程序调用 IO 操作后,会一直阻塞等待数据的读取或写入完成。
直到数据准备就绪或写入完成,应用程序才会继续执行。
非阻塞IO
应用程序调用 IO 操作后,如果数据还没有准备好或无法立即写入,函数会立即返回而不是阻塞等待。应用程序可以继续执行其他任务,之后再次检查数据的准备情况。
IO多路复用
使用 select、poll 或 epoll 等机制,应用程序可以同时监听多个 IO 事件。当任何一个 IO 事件就绪时,应用程序会被通知,可以进行读取或写入操作。这种模型允许同时处理多个 IO 操作,提高了系统的并发性能。
相关API
1.定义等待队列头
wait_queue_head_t wq_head;
2.初始化等待队列头
init_waitqueue_head(&wq_head);
3.wait_event(wq_head, condition)
等待某个条件满足:
当条件condition为假时,进程将被置于休眠状态,并添加到等待队列中,等待条件满足时被唤醒。
当条件condition为真时,进程将继续执行。
4.wait_event_interruptible(wq_head, condition)
功能:检查condition的真假,如果为真,则函数执行结束,如果为假,将进程切换到可中断休眠状态
参数:wq_head:等待队列头
condition:标志变量
5.wake_up(&wq_head)
功能:将不可中断休眠态的进程唤醒,当执行这个函数时如果condition为假,则进程被唤醒后会再次休眠
参数:等待队列头地址
6.wake_up_interruptible(&wq_head)
功能:将 可中断休眠态的进程唤醒当执行这个函数时如果condition为假,则进程被唤醒后会再次休眠
参数:等待队列头地址
7. __poll_t (*poll) (struct file *file, struct poll_table_struct *wait)
{
//将等待队列头向上提交
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
功能:向上提交等待队列头
参数:
filp: 文件结构体指针
wait_address:要提交的等待队列头首地址
p: 将队列头向上提交的通道
}
if(condition)
return POLLIN;//POLLIN表示发生的事件为读事件POLLOUT表示写事件
else
return 0;
}
epoll
epoll是Linux系统中的一种高效的I/O多路复用机制,用于管理大量的文件描述符并监视它们的状态变化。它相对于传统的select和poll机制,具有更高的性能和扩展性。
epoll的核心是一个事件驱动的I/O模型,通过将文件描述符注册到内核维护的事件表中,然后通过等待事件的发生来实现非阻塞地进行I/O操作。当文件描述符上发生感兴趣的事件时,内核会通知应用程序进行相应的处理。
相关API:
1.int epoll_create(int size);
功能:创建一个新的epoll
参数:
size:大于0的整数
返回值:成功返回用于操作epoll的文件描述符,失败返回错误码
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:进行epoll管理
参数:
epfd:epoll_create生成的文件描述符
op:管理的类型选项
EPOLL_CTL_ADD:用于在epoll上添加文件描述符
EPOLL_CTL_MOD:用于修改文件描述符事件类型
EPOLL_CTL_DEL:从epoll上移除指定的文件描述符
fd:要操作的文件描述符
event:设置文件描述符属性的变量
struct epoll_event {
uint32_t events; /* Epoll events */
//EPOLLIN:读
//EPOLLOUT:写
epoll_data_t data; /* User data variable */
};
typedef union epoll_data {
void *ptr;
int fd;《=======使用这个
uint32_t u32;
uint64_t u64;
} epoll_data_t;
返回值: 成功返回0,失败返回错误码
3.int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能:阻塞等待事件发生
参数:
epfd:epoll_create生成的文件描述符
events: 存放发生的事件的文件描述符数组的首地址
maxevents:监听的文件描述符最大个数
timeout:设置超时事件 毫秒级 -1表示不关注超时 >0超时事件
返回值:
>0:发生事件的文件描述符个数
==0:超时时间到达
<0:失败
举例
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
int main(int argc,const char * argv[])
{
char buf[128]="";
int fd1,fd2;//设置要监听的文件描述符
int epoll_fd,ret,i;
struct epoll_event event1;//设备监听
struct epoll_event event2;//鼠标监听
struct epoll_event events[MAX_EVENTS];
fd1=open("/dev/mycdev0",O_RDWR);
if(fd1<0){
printf("设备文件打开失败\n");
return -1;
}
fd2=open("/dev/input/mouse0",O_RDWR);
if(fd2<0){
printf("鼠标文件打开失败\n");
return -1;
}
// 创建 epoll 实例
epoll_fd=epoll_create(5);
if(epoll_fd<0){
printf("epoll创建失败\n");
return -1;
}
// 添加感兴趣的文件描述符到 epoll 实例中
event1.events = EPOLLIN;
event1.data.fd = fd1;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd1, &event1) == -1) {
perror("1 epoll_ctl failed\n");
return -1;
}
event2.events = EPOLLIN;
event2.data.fd = fd2;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd2, &event2) == -1) {
perror("2 epoll_ctl failed\n");
return -1;
}
while(1){
// 等待事件发生
ret = epoll_wait(epoll_fd,events,MAX_EVENTS,-1);
if(ret < 0){
printf("epoll_wait failed\n");
return -1;
}
// 处理就绪的事件
for(i=0;i<ret;i++){
if(events[i].events & EPOLLIN){
memset(buf,0,sizeof(buf));
read(events[i].data.fd,buf,sizeof(buf));
printf("buf=%s\n",buf);
}
}
}
close(fd1);
return 0;
}