Linux串口设备的Canonical and noncanonical mode
Canonical and noncanonical mode
可能翻译为规范/非规范模式?
通过termios结构体的c_lflag成员来设置Canonical or noncanonical mode.
这两种模式是串口输入相关的配置。
Canonical模式中:
- 串口读取的单位是行,行定义为包含行结束符:NL, EOL, EOL2; or EOF at the start of line。read()收到结束符后即返回,其中除了EOF之外(回车符等)与普通字符一样会被read()函数读到缓冲区中。
- 如何read在没有读到结束符之前意外终止,缓存的数据将被保留。下次read调用将包含这些数据。
noncanonical模式中:
- 串口读取的单位是字节,不需要等待任何的结束符,而是由MIN (c_cc[VMIN])和TIME (c_cc[VTIME])的设置来决定read何时返回。
VMIN和VTIME
给出定义:
- VMIN:noncanonical mode中规定read()返回的的最小字节数。
- VTIME:noncanonical mode中规定read()返回的超时时间(100ms为单位)。
比如说,存在以下几种情况:
MIN == 0, TIME == 0 (polling read)
有数据就返回,返回值是min{当前可用数据长度, 请求数据长度},若没有数据则返回0,不等待。
请求数据长度:count in
ssize_t read(int fd, void *buf, size_t count);
MIN > 0, TIME == 0 (blocking read)
直到接受MIN个字节时,read才返回。
但是若请求数据长度<MIN,read会在读到请求数据长度时提前返回。
MIN == 0, TIME > 0 (read with timeout)
调用read()时开始计时,在TIME时间内,若收到数据就返回。
MIN > 0, TIME > 0 (read with interbyte timeout)
在收到字节的数据后开始计时。read()返回的原因包括:
- TIME时间内,MIN长度的数据已到达。
- TIME计时时间到。
- TIME时间内,请求数据长度的数据已到达。(POSIX未规定此情况,所以有可能其他系统未实现!)
所以此类情况下,造成read()返回的返回值必然>0,否则计时器不会开启,read()阻塞在那。
补充:Linux中标准输入/输出的行缓存
在linux中有时调用printf()打印却不显示,可能是因为没有加换行符(\n)导致的,这就是Linux中关于标准输入/输出默认是启用行缓存的。其实是与我们上面提到的Canonical mode同样的原理。
解决的方案有三个:
-
手动添加换行符(\n);这种方式具有局限性,因为有的时候换行符会对原始数据造成干扰。
-
刷新标准输出缓冲区;
执行
fflush(stdout)
将目前缓冲区中的数据发送给输出设备,来刷新缓冲区。 -
关闭行缓存;
setvbuf(stdout,NULL,_IONBF,0);
暴力的关闭输出缓冲区,这样接受到的数据就会直接发送给输出设备了。