栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

【Linux】内核驱动篇四--IO模型

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Linux】内核驱动篇四--IO模型


内核驱动篇四--IO模型
  • 一、五种IO模型------读写外设数据的方式
  • 二、阻塞与非阻塞
  • 三、多路复用
    • 3.1 应用层:三套接口select、poll、epoll
    • 3.2 驱动层:实现poll函数
  • 四、信号驱动
    • 4.1 应用层:信号注册+fcntl
    • 4.2 驱动层:实现fasync函数


一、五种IO模型------读写外设数据的方式
  1. 阻塞: 不能操作就睡觉
  2. 非阻塞:不能操作就返回错误
  3. 多路复用:委托中介监控
  4. 信号驱动:让内核如果能操作时发信号,在信号处理函数中操作
  5. 异步IO:向内核注册操作请求,内核完成操作后发通知信号

二、阻塞与非阻塞

应用层:

  1. open函数的条件O_NONBLOCK指示read、write时是否阻塞
  2. open以后可以由fcntl函数来改变是否阻塞:
flags = fcntl(fd,F_GETFL,0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

驱动层:通过等待队列

wait_queue_head_t //等待队列头数据类型

init_waitqueue_head(wait_queue_head_t *pwq) //初始化等待队列头
    
wait_event_interruptible(wq,condition)   浅度等待,信号可以唤醒

        
wait_event(wq,condition) //深度睡眠

wake_up_interruptible(wait_queue_head_t *pwq)  //浅度唤醒
        
wake_up(wait_queue_head_t *pwq)  //深度唤醒
    
    



三、多路复用

描述符:

  1. 文件描述符:设备文件、管道文件
  2. socket描述符
3.1 应用层:三套接口select、poll、epoll

select:位运算实现 监控的描述符数量有限(32位机1024,64位机2048) 效率差

poll:链表实现,监控的描述符数量不限 效率差

epoll:效率最高,监控的描述符数量不限

三者使用驱动设备都会调用驱动代码内的poll函数

select使用模型:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
//功能:将fd 从集合中清除掉

int  FD_ISSET(int fd, fd_set *set);
//功能:判断fd 是否存在于集合中
 
void FD_SET(int fd, fd_set *set);
//功能:将fd 添加到集合中

void FD_ZERO(fd_set *set);
//功能:将集合清零

//使用模型:

while(1)
{
    
    
	
    
    ret = select(maxfd+1,&rfds,&wfds,NULL,NULL);
    if(ret < 0)
    {
        if(errno == EINTR)//错误时信号引起的
        {
        	continue;   
        }
        else
        {
            break;
        }
    }
    else if(ret == 0)
    {//超时
        //.....
    }
    else
    { //> 0 ret为可被操作的描述符个数
        if(FD_ISSET(fd1,&rfds))
        {//读数据
            //....
        }
        if(FD_ISSET(fd2,&rfds))
        {//读数据
            //....
        }
        ///.....
        if(FD_ISSET(fd1,&wfds))
        {//写数据
            //....
        }
    }
}
   
3.2 驱动层:实现poll函数

poll_waith函数:

void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p);

poll函数制造与使用模板:

unsigned int xxx_poll(struct file *filp, poll_table *wait) //函数名初始化给struct file_operations的成员.poll
{
    unsigned int mask = 0;
    
    return mask;
}

四、信号驱动 4.1 应用层:信号注册+fcntl

使用模板:

signal(SIGIO, input_handler); //注册信号处理函数

fcntl(fd, F_SETOWN, getpid());//将描述符设置给对应进程,好由描述符获知PID

oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);//将该设备的IO模式设置成信号驱动模式

void input_handler(int signum)//应用自己实现的信号处理函数,在此函数中完成读写
{
    //读数据
}

//应用模板
int main()
{
	int fd = open("/dev/xxxx",O_RDONLY);

	fcntl(fd, F_SETOWN, getpid());

	oflags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, oflags | FASYNC);

	signal(SIGIO,xxxx_handler);

	//......
}
    
void xxxx_handle(int signo)
{//读写数据
    
}


4.2 驱动层:实现fasync函数

制造与使用模板:

struct fasync_struct *pasync_obj;


static int xxx_fasync(int fd, struct file *filp, int mode) //函数名初始化给struct file_operations的成员.fasync
{
	struct xxx_device *dev = filp->private_data; 
	return fasync_helper(fd, filp, mode, &dev->pasync_obj);
}


if(dev->pasync_obj)
       kill_fasync(&dev->pasync_obj, SIGIO, POLL_IN);
       

if(dev->pasync_obj) 
	fasync_helper(-1, filp, 0, &dev->pasync_obj);

int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **pp);


void kill_fasync(struct fasync_struct **, int, int);


实用例子:


到这里就结束啦!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/893377.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号