今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。

一、基本概念:

阻塞操作 : 是指在执行设备操作时,若不能获得资源,则挂起进程直到满足操作条件后再进行操作。被挂起的进程进入休眠, 被从调度器移走,直到条件满足。

非阻塞操作 :在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作。非阻塞应用程序通常使用select系统调用查询是否可以对设备进行无阻塞的访问最终会引发设备驱动中 poll函数执行。

二、轮询操作

阻塞的读取一个字符:

char buf;

fd = open("/dev/ttyS1",O_RDWR);

.....

res = read(fd,&buf,1); //当串口上有输入时才返回,没有输入则进程挂起睡眠

if(res == 1)

{

printf("%c/n",buf);

}

char buf;

fd = open("/dev/ttyS1",O_RDWR);

.....

res = read(fd,&buf,1); //当串口上有输入时才返回,没有输入则进程挂起睡眠

if(res == 1)

{

printf("%c/n",buf);

}

非阻塞的读一个字符:

char buf;

fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK);//O_NONBLOCK 非阻塞标识

.....

while(read(fd,&buf,1)!=1);//串口上没有输入则返回,所以循环读取

printf("%c/n",buf);

char buf;

fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK);//O_NONBLOCK 非阻塞标识

.....

while(read(fd,&buf,1)!=1);//串口上没有输入则返回,所以循环读取

printf("%c/n",buf);

阻塞操作常常用等待队列来实现,而非阻塞操作用轮询的方式来实现。非阻塞I/O的操作在应用层通常会用到select()和poll()系统调用查询是否可对设备进行无阻塞访问。select()和poll()系统调用最终会引发设备驱动中的poll()函数被调用。这里对队列就不多介绍了,大家可以看看数据结构里面的知识点。

应用层的select()原型为:

int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptionfds,struct timeval *timeout);

numfds 的值为需要检查的号码最高的文件描述符加1,若select()在等待timeout时间后,若没有文件描述符准备好则返回。

int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptionfds,

struct timeval *timeout); numfds 的值为需要检查的号码最高的文件描述符加1,若select()在等待timeout时间后,若没有文件描述符准备好则返回。

应用程序为:

#inlcude------

main()

{

int fd,num;

char rd_ch[BUFFER_LEN];

fd_set rfds,wfds; //读写文件描述符集

//以非阻塞方式打开/dev/globalfifo设备文件

fd=open("/dev/globalfifo",O_RDWR|O_NONBLOCK);

if(fd != -1)

{

//FIFO 清零

if(ioctl(fd,FIFO_CLEAR,0) < 0)

{

printf("ioctl cmd failed /n");

}

while(1)

{

FD_ZERO(&rfds);

FD_ZERO(&wfds);

FD_SET(fd,&rfds);

FD_SET(fd,&wfds);

select(fd+1,&rfds,&wfds,null,null);

}

}

}

#inlcude------

main()

{

int fd,num;

char rd_ch[BUFFER_LEN];

fd_set rfds,wfds; //读写文件描述符集

//以非阻塞方式打开/dev/globalfifo设备文件

fd=open("/dev/globalfifo",O_RDWR|O_NONBLOCK);

if(fd != -1)

{

//FIFO 清零

if(ioctl(fd,FIFO_CLEAR,0) < 0)

{

printf("ioctl cmd failed /n");

}

while(1)

{

FD_ZERO(&rfds);

FD_ZERO(&wfds);

FD_SET(fd,&rfds);

FD_SET(fd,&wfds);

select(fd+1,&rfds,&wfds,null,null);

}

}

}

下面说说设备驱动中的poll()函数,函数原型如下:

static unsigned int poll(struct file *file, struct socket *sock,poll_table *wait) //第一个参数是file结构体指针,第三个参数是轮询表指针,这个函数应该进行两项工作

static unsigned int poll(struct file *file, struct socket *sock,poll_table *wait) //第一个参数是file结构体指针,第三个参数是轮询表指针,这个函数应该进行两项工作

对可能引起设备文件状态变化的等待队列调用poll_wait()函数,将对应的等待队列头添加到poll_table

返回表示是否能对设备进行无阻塞读,写访问的掩码

这里还要提到poll_wait()函数,很多人会以为是和wait_event()一样的函数,会阻塞的等待某件事情的发生,其实这个函数并不会引起阻塞,它的工作是把当前的进程增添到wait参数指定的等待列表poll_table中去,poll_wait()函数原型如下:

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

从中可以看出是将等待队列头wait_address添加到p所指向的结构体中(poll_table)

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

从中可以看出是将等待队列头wait_address添加到p所指向的结构体中(poll_table)

驱动函数中的poll()函数典型模板如下:

static unsigned int xxx_poll(struct file *filp,struct socket *sock,

poll_table *wait)

{

unsigned int mask = 0;

struct xxx_dev *dev = filp->private_data;//获得设备结构体指针

...

poll_wait(filp,&dev->r_wait,wait);//加读等待队列头到poll_table

poll_wait(filp,&dev->w_wait,wait);//加写等待队列头到poll_table

...

if(...)//可读

mask |= POLLIN | POLLRDNORM;

if(...)//可写

mask |= POLLOUT | POLLRDNORM;

...

return mask;

}

static unsigned int xxx_poll(struct file *filp,struct socket *sock,

poll_table *wait)

{

unsigned int mask = 0;

struct xxx_dev *dev = filp->private_data;//获得设备结构体指针

...

poll_wait(filp,&dev->r_wait,wait);//加读等待队列头到poll_table

poll_wait(filp,&dev->w_wait,wait);//加写等待队列头到poll_table

...

if(...)//可读

mask |= POLLIN | POLLRDNORM;

if(...)//可写

mask |= POLLOUT | POLLRDNORM;

...

return mask;

}

三、支持轮询操作的globalfifo驱动

在globalfifo的poll()函数中,首先将设备结构体重的r_wait和w_wait等待队列头加到等待队列表,globalfifo设备驱动的poll()函数如下:

static unsigned int gloablfif0_poll(struct file *filp,poll_table *wait)

{

unsigned int mask = 0;

struct globalfifo_dev *dev = filp->private_data;

down(&dev->sem);

poll_wait(filp,&dev->r_wait , wait) ;

poll_wait(filp,&dev->r_wait , wait) ;

if(dev->current_len != 0)

{

mask |= POLLIN | POLLRDNORM;

}

if(dev->current_len != GLOBALFIFO_SIZE)

{

mask |= POLLOUT | POLLWRNORM;

}

up(&dev->sem);

return mask;

}

static unsigned int gloablfif0_poll(struct file *filp,poll_table *wait)

{

unsigned int mask = 0;

struct globalfifo_dev *dev = filp->private_data;

down(&dev->sem);

poll_wait(filp,&dev->r_wait , wait) ;

poll_wait(filp,&dev->r_wait , wait) ;

if(dev->current_len != 0)

{

mask |= POLLIN | POLLRDNORM;

}

if(dev->current_len != GLOBALFIFO_SIZE)

{

mask |= POLLOUT | POLLWRNORM;

}

up(&dev->sem);

return mask;

}

四、总结

阻塞与非阻塞操作:

定义并初始化等待对列头;

定义并初始化等待队列;

把等待队列添加到等待队列头

设置进程状态(TASK_INTERRUPTIBLE(可以被信号打断)和TASK_UNINTERRUPTIBLE(不能被信号打断))

调用其它进程

poll机制:

把等待队列头加到poll_table

返回表示是否能对设备进行无阻塞读,写访问的掩码

linux 设备驱动阻塞,深入浅出:Linux设备驱动中的阻塞和非阻塞I/O相关推荐

  1. linux 管道非阻塞,在Linux中管道上的非阻塞读取

    可以在管道上进行非阻塞I / O吗? fcntl无法设置O_NONBLOCK. Linux编程接口的页面918包括一个表'从管道读取n个字节或FIFO(p)'的语义.此表列出了管道和FIFO的行为,其 ...

  2. Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器

    2019独角兽企业重金招聘Python工程师标准>>> 本文无太多内容,主要是几个前面提到过的注意点: 一是epoll的fd需要重新装填.我们将tcp_connection_t的指针 ...

  3. 在Python中对子进程进行非阻塞读取

    我正在使用子流程模块来启动子流程并连接到其输出流(stdout). 我希望能够在其stdout上执行非阻塞读取. 有没有一种方法可以使.readline成为非阻塞状态,或者在调用.readline之前 ...

  4. Redis 中的 持久化 RDB持久化 SAVE:阻塞服务器并创建RDB文件 BGSAVE:以非阻塞方式创建RDB文件 通过配置选项自动创建RDB文件

    这里写目录标题 15.1 RDB持久化 15.1.1 SAVE:阻塞服务器并创建RDB文件 其他信息 15.1.2 BGSAVE:以非阻塞方式创建RDB文件 其他信息 15.1.3 通过配置选项自动创 ...

  5. java网络编程阻塞_Java网络编程由浅入深三 一文了解非阻塞通信的图文代码示例详解...

    本文详细介绍组成非阻塞通信的几大类:Buffer.Channel.Selector.SelectionKey 非阻塞通信的流程ServerSocketChannel通过open方法获取ServerSo ...

  6. AndroidStudio_Android中使用Handler实现非阻塞线程间通讯_跨线程更新UI_耗时处理完成后主动通知更新UI_在同一个activity用---Android原生开发工作笔记219

    其实也是很简单的,比如,我们要是在android中去访问,http请求服务器的话,那么这个时候,UI线程不会一直等着,你访问完成,再去更新UI,因为这样 会导致看起来卡顿. 这个时候可以这样: 例如我 ...

  7. begin end中阻塞语句与非阻塞语句执行顺序的问题

    1 module fsm_2(clk,A,Y); input clk,A; output reg Y; reg q1; always@(posedge clk) begin Y<=q1& ...

  8. Linux 设备驱动--- 阻塞型字符设备驱动 --- O_NONBLOCK --- 非阻塞标志【转】

    阅读目录 1,以阻塞方式运行: 2,以非阻塞方式运行: 转自:http://blog.csdn.net/yikai2009/article/details/8653697 版权声明:本文为博主原创文章 ...

  9. 嵌入式Linux 阻塞和非阻塞 IO 驱动设备访问模式

    阻塞和非阻塞 IO 是 Linux 驱动开发里面很常见的两种设备访问模式, 在编写驱动的时候一定要考虑到阻塞和非阻塞. 阻塞与非阻塞简介 阻塞操作是指在执行设备操作时, 若不能获得资源, 则挂起进程直 ...

最新文章

  1. 各种机器学习方法的优缺点
  2. 蓝桥杯:入门训练 圆的面积
  3. exce中让两列数据一一对应_工作中被重复数据所烦恼?学会这几个Excel技巧,少加班...
  4. MongoDB使用小结:一些常用操作分享
  5. V8 Design Elements(翻译)
  6. JS高级——深入剖析函数中的this指向问题
  7. unique函数_C++核心准则C.35:基类的析构函数必须满足的条件
  8. 清明出游,在高速上堵了16个小时。
  9. C语言 gcc API
  10. 如何实现文件上传 - JavaWeb
  11. [最后几天]蓝桥杯如何优雅控分
  12. php mpm,Ubuntu Apache 切换到php-fpm+mpm_event模式
  13. 编译时:virtual memory exhausted: Cannot allocate memory
  14. origin2018软件下载和安装教程
  15. 抖音小店营业执照怎么办理?新手做抖店营业执照范围应该怎么选?
  16. 3D产品展示程序-古董
  17. 【广度优先搜索】leetcode 994. 腐烂的橘子
  18. 亚马逊国际站通过ASIN获取商品信息
  19. 项目经理start法则_开放科学项目的7条经验法则
  20. mysql57是什么_关于mysql57的详细介绍

热门文章

  1. 台大李宏毅Machine Learning 2017Fall学习笔记 (13)Semi-supervised Learning
  2. Linux下Qt使用QAudio相关类进行音频采集,使用Windows下的Matlab软件播放
  3. dtree树形结构异步传输
  4. EntityFramework 事务的使用
  5. Git for Windows之分支管理、分支合并、解决分支冲突
  6. 结对作业_core组
  7. .NET CORE——Console中使用依赖注入
  8. 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表
  9. [翻译]XNA在线俱乐部网站即将开站!
  10. noi 9271 奶牛散步