Linux系统运维中的阻塞和非阻塞I/O
小标 2018-06-11 来源 : 阅读 665 评论 0

摘要:在Linux系统运维中,今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。希望对大家学习Linux系统运维有所帮助。

在Linux系统运维中,今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的支持用户空间对设备的这两种访问方式。希望对大家学习Linux系统运维有所帮助。

一、基本概念:

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

· 非阻塞操作 :在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作。非阻塞应用程序通常使用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|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时间后,若没有文件描述符准备好则返回。

应用程序为:

#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结构体指针,第三个参数是轮询表指针,这个函数应该进行两项工作

· 对可能引起设备文件状态变化的等待队列调用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)

驱动函数中的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;
 
}

三、支持轮询操作的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;
}

四、总结

阻塞与非阻塞操作:

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

· 定义并初始化等待队列;

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

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

· 调用其它进程

poll机制:

· 把等待队列头加到poll_table

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

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注系统运维Linux频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程