1.框架圖
被稱為當(dāng)前時(shí)代最好用的io多路復(fù)用方式;
核心操作:一棵樹(紅黑樹)、一張表(內(nèi)核鏈表)以及三個(gè)接口;
?思想:(fd代表文件描述符)
????????epoll要把檢測(cè)的事件fd掛載到內(nèi)核空間紅黑樹上,遍歷紅黑樹,調(diào)用每個(gè)fd對(duì)應(yīng)的操作方法,找到發(fā)生事件的fd,如果沒有發(fā)生事件的fd,進(jìn)程休眠,如果事件發(fā)生,將發(fā)生事件的fd拷貝一份放到內(nèi)核鏈表,每個(gè)節(jié)點(diǎn)對(duì)應(yīng)一個(gè)fd,最后把鏈表的節(jié)點(diǎn)信息傳遞到用戶空間的數(shù)組中,用戶空間無需判斷事件的發(fā)生,需要判斷事件類型(讀寫等);
?
2.代碼
---pro1.c---應(yīng)用程序(epoll方式)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/epoll.h>
int main(int argc, const char *argv[])
{
int fd1,fd2,epfd;
char buf[128] = {0};
struct epoll_event event; //用于操作epoll
struct epoll_event events[10]; //用戶空間存放發(fā)生事件的數(shù)組
//創(chuàng)建epoll句柄,紅黑樹根節(jié)點(diǎn)
epfd = epoll_create(1);
if(epfd < 0)
{
printf("epoll_create fail\n");
exit(-1);
}
//打開設(shè)備文件
fd1 = open("/dev/input/mouse0", O_RDWR);
if (fd1 < 0)
{
printf("鼠標(biāo)事件文件失敗\n");
exit(-1);
}
fd2 = open("/dev/myled0", O_RDWR);
if (fd2 < 0)
{
printf("自定義事件文件失敗\n");
exit(-1);
}
//添加準(zhǔn)備就緒事件到epoll
event.events = EPOLLIN; //讀事件
event.data.fd = fd1;
if((epoll_ctl(epfd,EPOLL_CTL_ADD,fd1,&event)) < 0)
{
printf("epoll_ctl fd1 fail\n");
}
event.events = EPOLLIN; //讀事件
event.data.fd = fd2;
if((epoll_ctl(epfd,EPOLL_CTL_ADD,fd2,&event)) < 0)
{
printf("epoll_ctl fd2 fail\n");
}
//監(jiān)聽時(shí)間是否發(fā)生
while(1)
{
//成功接收返回時(shí)間的個(gè)數(shù),放入events數(shù)組中
int ret = epoll_wait(epfd,events,10,-1);
if(ret < 0)
{
printf("epoll_wait fail\n");
exit(-1);
}
int i;
//循環(huán)遍歷數(shù)組,做事件的處理
for(i=0; i<ret; i++)
{
if(events[i].events & EPOLLIN) //發(fā)生事件是讀事件
{
read(events[i].data.fd,buf,sizeof(buf));
printf("buf:%s\n",buf);
memset(buf,0,sizeof(buf));
}
}
}
close(fd1);
close(fd2);
return 0;
}
---pro2.c---應(yīng)用程序(模擬自定義設(shè)備數(shù)據(jù)就緒)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
char buf[128] = "hello world";
int fd = open("/dev/myled0", O_RDWR);
if (fd < 0)
{
printf("打開設(shè)備文件失敗\n");
exit(-1);
}
write(fd, buf, sizeof(buf));
close(fd);
return 0;
}
---epoll.c---驅(qū)動(dòng)程序
?
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include<linux/poll.h>
char kbuf[128] = {0};
unsigned int major;
struct class *cls;
struct device *dev;
unsigned int condition = 0;
// 定義一個(gè)等待隊(duì)列頭
wait_queue_head_t wq_head;
// 封裝操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
int ret;
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy_to_ user err\n");
return -EIO;
}
condition = 0; // 下一次硬件數(shù)據(jù)沒有就緒
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
int ret;
// 從用戶拷貝數(shù)據(jù),模擬硬件數(shù)據(jù)
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy_from_user err\n");
return -EIO;
}
condition = 1;
wake_up_interruptible(&wq_head);
return 0;
}
//封裝POLL方法
__poll_t mycdev_poll(struct file *file, struct poll_table_struct *wait)
{
__poll_t mask = 0;
//向上提交等待隊(duì)列頭
poll_wait(file,&wq_head,wait);
//根據(jù)事件是否發(fā)生給一個(gè)合適的返回值
if(condition)
{
mask = POLLIN;
}
return mask;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.poll = mycdev_poll,
.write = mycdev_write,
.release = mycdev_close,
};
// 入口函數(shù)
static int __init mycdev_init(void)
{
//初始化等待隊(duì)列
init_waitqueue_head(&wq_head);
major = register_chrdev(0, "myled", &fops);
if (major < 0)
{
printk("字符設(shè)備驅(qū)動(dòng)注冊(cè)失敗\n");
return major;
}
printk("字符設(shè)備驅(qū)動(dòng)注冊(cè)成功:major=%d\n", major);
// 向上提交目錄
cls = class_create(THIS_MODULE, "MYLED");
if (IS_ERR(cls))
{
printk("向上提交目錄失敗\n");
return -PTR_ERR(cls);
}
printk("向上提交目錄成功\n");
// 向上提交設(shè)備節(jié)點(diǎn)信息
int i;
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev))
{
printk("向上提交設(shè)備節(jié)點(diǎn)信息失敗\n");
return -PTR_ERR(dev);
}
}
printk("向上提交設(shè)備節(jié)點(diǎn)信息成功\n");
return 0;
}
// 出口函數(shù)
static void __exit mycdev_exit(void)
{
// 銷毀設(shè)備節(jié)點(diǎn)信息
int i;
for (i = 0; i < 3; i++)
{
device_destroy(cls, MKDEV(major, i));
}
// 銷毀目錄信息
class_destroy(cls);
// 字符設(shè)備驅(qū)動(dòng)注銷
unregister_chrdev(major, "myled");
}
// 聲明
// 入口函數(shù)地址
module_init(mycdev_init);
// 出口函數(shù)地址
module_exit(mycdev_exit);
// 遵循的GPL協(xié)議
MODULE_LICENSE("GPL");
?
3.測(cè)試結(jié)果
?執(zhí)行pro2.c,自定義事件被監(jiān)聽到;
?在ubuntu上動(dòng)鼠標(biāo),鼠標(biāo)事件被監(jiān)聽;文章來源:http://www.zghlxwxcb.cn/news/detail-731813.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-731813.html
到了這里,關(guān)于驅(qū)動(dòng)開發(fā),IO多路復(fù)用實(shí)現(xiàn)過程,epoll方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!