当前位置: 首页 > news >正文

Linux串口IO模式的一些心得

众所周知,在Linux系统下所有设备都是以文件的形式存在,串口也一样。

通常I/O操作都是有阻塞与非阻塞的两种方式。

其中"超时"这个概念其实是阻塞中的一种处理手段,本质还是属于阻塞的I/O模式.

在Linux中串口的IO操作 本文将它分为三种状态:

阻塞状态

超时状态

非阻塞状态

这三种状态的转换组合有这么几种:

阻塞 --> 超时

阻塞 --> 非阻塞

超时 --> 阻塞

超时 --> 非阻塞

非阻塞 --> 阻塞

我们一个一个来分析

首先在一个串口的描述符打开的时候指定它的模式是阻塞还是阻塞

fd = open("/dev/tttyS0",O_RDWR | O_NOCTTY);//以阻塞模式打开串口
fd = open("/dev/tttyS0",O_RDWR | O_NOCTTY | O_NDELAY);//以非阻塞
模```  
式打开串口
有些地方使用的是
fd = open("/dev/tttyS0",O_RDWR | O_NOCTTY | O_NOBLOCK);
引用《UNIX环境高级编程》(第二版):

O_NOCTTY:O_NOCTTY  如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端

O_NDELAY 与 O_NOBLOCK区别 

较早的系统 V版本引入了 O _ N D E L AY(不延迟)标志,它与 O _ N O N B L O C K

(不阻塞)选择项类似,但在读操作的返回值中具有两义性。如果不能从管道、

F I F O或设备读得数据,则不延迟选择项使 r e a d返回0,这与表示已读到文件尾端的

返回值0相冲突。 S V R 4仍支持这种语义的不延迟选择项,但是新的应用程序应当

使用不阻塞选择项以代替之。

 

当一个串口是阻塞状态的时候便可以设置它为超时状态。

利用 struct termios 的 cc_t c_cc[NCCS] 成员

    c_cc[VTIME] 非规范模式读取时的超时时间(单位:百毫秒)

    c_cc[VMIN]  非规范模式读取时的最小字符数

如需需要设置超时则c_cc[VMIN] 必须等于0

‍‍这代表能够读取的最小字符是0个,即使用read读取数据超时read返回0‍‍

 

有一个需要注意的地方!

当c_cc[VTIME] 设置为 0 且 c_cc[VMIN] == 0 的时候,代表超时0秒(姑且这么叫吧!)

这个时候使用read读取数据会立即返回(有读到数据时返回字节数,没有数据和一般超时一样返回0)

但是!

虽然这时候在现象上看起来和非阻塞模式一样(read都不会阻塞)但返回值不同

 

非阻塞模式: read没有读到数据立即返回-1

超时0秒时:  read没有读到数据立即返回 0  (设置了超时的阻塞模式)

ret = read(fd,recvbuf,BUF_SIZE);
if(ret == -1)//非阻塞模式时"无数据返回"
{

//do something

}

ret = read(fd,recvbuf,BUF_SIZE);
if(ret == 0)//阻塞模式设置超时0秒时"超时返回"
{

//do something

}

补充:在非阻塞模式下修改c_cc[VMIN] 和 c_cc[VTIME] 的情况

若在非阻塞模式下修改

c_cc[VMIN]为0并且c_cc[VTIME]也为0时read无数据会返回 0 (现象同"超时0秒"一样)

这时倘若将c_cc[VMIN]或者c_cc[VTIME]中任意一个项修改成>0,那么read就返回-1了。 

 

虽然表现形式一样,但在编程时必须要了解自己使用的是哪一种模式和串口当前的状态才能更好的分析和处理问题。

这里说一下我曾经遇到过的一个问题:

我在打开串口时使用阻塞模式打开,但是没有设置c_cc[VMIN]的值,而它初始化后就是0,所以发现串口没有被阻塞,其实原因就是串口模式还是阻塞模式没错,但是它是超时0秒的状态,所以在没有数据到达时read也返回了。 

 

关于阻塞模式下c_cc[VMIN] 和 c_cc[VTIME]的取值与现象,以下简称为VMIN和VTIME

这两个值有这些组合

 

![image](https://yqfile.alicdn.com/17d349ef2a48e4353de22fcdfad5ad8c1a59c5cf.png)

当接收到第一个字节时开始计算超时。
如果超时时间未到但数据已经达到VMIN个read立即返回。
如果超时时间到了就返回当前读到的个数。
 

阻塞状态和非阻塞状态的切换

非阻塞状态时使用

flag = fcntl(fd,F_GETFL);
//先获取原文件状态(假定函数执行成功返回文件状态)

flag &= ~O_NONBLOCK;
//去除非阻塞的flag

fcntl(fd,F_SETFL,flag);
//设置新的文件状态

使用以上的方式可转换成阻塞状态,这时就可以可以进行超时设置了。

如果在非阻塞状态已经设置了超时时,在转换成阻塞状态后超时随之生效。

  

阻塞状态时使用

flag = fcntl(fd,F_GETFL);
//先获取原文件状态(假定函数执行成功返回文件状态)

flag |= O_NONBLOCK;
//增加阻塞的flag

fcntl(fd,F_SETFL,flag);
//设置新的文件状态
使用以上的方式可转换成非阻塞状态,超时效果失效。


也可以使用以下方法设置成非阻塞状态:

 

fcntl(fd,F_SETFL,FNDELAY);
有些代码中使用
fcntl(fd,F_SETFL,FNONBLOCK);
这里提一下 <fcntl.h>头文件中几个宏的定义

gcc 版本 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

/* Define some more compatibility macros to be backward compatible with
BSD systems which did not managed to hide these kernel macros. */

ifdef __USE_BSD

define FAPPEND O_APPEND

define FFSYNC O_FSYNC

define FASYNC O_ASYNC

define FNONBLOCK O_NONBLOCK

define FNDELAY O_NDELAY

endif / Use BSD. /

/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
located on a few file systems. */

define O_ACCMODE 0003

define O_RDONLY 00

define O_WRONLY 01

define O_RDWR 02

define O_CREAT 0100 / not fcntl /

define O_EXCL 0200 / not fcntl /

define O_NOCTTY 0400 / not fcntl /

define O_TRUNC 01000 / not fcntl /

define O_APPEND 02000

define O_NONBLOCK 04000

define O_NDELAY O_NONBLOCK

define O_SYNC 04010000

define O_FSYNC O_SYNC

define O_ASYNC 020000

可以得出一下结论:

O_NDELAY == O_NONBLOCK == 0x4000

FNONBLOCK == O_NONBLOCK

FNDELAY == O_NDELAY

所以 FNDELAY == FNONBLOCK

上面设置非阻塞模式的方法归根结底就是要把文件状态加上O_NONBLOCK一个非阻塞的标志。

 

2015-01-21补充:

如果在非阻塞模式下调用read时没有马上读到数据会立即返回-1,错误提示是(Resource temporarily unavailable)

相关文章:

  • PHPStorm解决Failed to change timestamp of the file
  • Ext JS 5 对平板的支持 【已翻译100%】
  • 在 AngularJS 应用中通过 JSON 文件来设置状态 【已翻译100%】
  • 一款基于jQuery的漂亮弹出层
  • uva 12230 Crossing Rivers
  • 51nod 1010 只包含因子2 3 5的数 二分答案
  • iOS10App如何跳转到系统设置转
  • IPv4检验和计算
  • vue总结
  • java虚拟机:class文件结构
  • tomcat7线程池配置
  • JS中typeof和instanceof用法区别
  • JS中闭包、函数与对象的介绍和用法
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • 从零开始学习Vue(一)
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • 345-反转字符串中的元音字母
  • canvas 五子棋游戏
  • DataBase in Android
  • in typeof instanceof ===这些运算符有什么作用
  • Linux链接文件
  • scala基础语法(二)
  • text-decoration与color属性
  • windows下mongoDB的环境配置
  • XML已死 ?
  • 百度地图API标注+时间轴组件
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 温故知新之javascript面向对象
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​用户画像从0到100的构建思路
  • #HarmonyOS:Web组件的使用
  • (11)MATLAB PCA+SVM 人脸识别
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (搬运以学习)flask 上下文的实现
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (万字长文)Spring的核心知识尽揽其中
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (转载)CentOS查看系统信息|CentOS查看命令
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • ./configure,make,make install的作用
  • .CSS-hover 的解释
  • .net core 控制台应用程序读取配置文件app.config
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .net Stream篇(六)
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET 中 GetProcess 相关方法的性能
  • .NET处理HTTP请求
  • .NET连接MongoDB数据库实例教程
  • .NET中winform传递参数至Url并获得返回值或文件
  • .php结尾的域名,【php】php正则截取url中域名后的内容