带有 TCSADRAIN 标志的 tcsetattr() 阻塞时间很奇怪
tcsetattr() blocking time with TCSADRAIN flag is weird
我在Linux中使用串行端口编码。
并且通信的要求是5ms inter-byte time
.
它要求我在调用之前根据字节的值更改每个字节write()
奇偶模式(偶数和奇数(。
所以我像下面这样编码(我简单地描述代码(
void setWakeupMode(int fd, bool mode) {
struct termios tio;
bzero(&tio, sizeof(tio));
tcgetattr(fd, &tio);
if (mode == false) {
tio.c_cflag &= ~PARODD;
} else if (mode == true) {
tio.c_cflag |= PARODD;
}
if(tcsetattr(fd, TCSADRAIN, &tio) < 0){
perror("tcsetattr Error");
}
}
int main(){
unsigned char a[2] = {0x01, 0x0F};
write(fd, a, 1);
setWakeupMode(fd, true);
write(fd, a+1, 1);
}
但是代码不能满足字节间时间,导致近 20 毫秒。
所以我尝试打印每个系统调用之间的确切时间,如下所示。
int main(){
unsigned char a[2] = {0x01, 0x0F};
printf("write1 start : %s", /*time*/);
write(fd, a, 1);
printf("write1 end : %s", /*time*/);
setWakeupMode(fd, true);
printf("write2 start : %s", /*time*/);
write(fd, a+1, 1);
printf("write2 end : %s, /*time*/);
}
这就是结果
write1 start : 34.755201
write1 end : 34.756046
write2 start : 34.756587
write2 end : 34.757349
这个结果突然满足5ms的字节间时间,导致1ms inter-byte time
。
所以我尝试了几种方法。
最后,我认识到,只有当我在 tcsetattr(( 之前打印一些东西时,字节间时间才得到满足。
例如,如果我删除如下所示printf("write1 end : %s, /*time*/);
int main(){
unsigned char a[2] = {0x01, 0x0F};
printf("write1 start : %s", /*time*/);
write(fd, a, 1);
// printf("write1 end : %s", /*time*/); //remove this
setWakeupMode(fd, true);
printf("write2 start : %s", /*time*/);
write(fd, a+1, 1);
printf("write2 end : %s", /*time*/);
}
结果出奇的不同,看write1 start
和write2 start
之间的间隔,这是18ms
.
write1 start : 40.210111
write2 start : 40.228332
write2 end : 40.229187
如果我使用 std::cout 而不是 printf,结果是相同的。
为什么会发生这种奇怪的情景?
-------------------------------编辑--------------------------------
由于我看到了一些答案,有些人误解了我的问题。
我不担心开销printf()
。
简单地说。
- 我想用 1 字节调用
write()
s,但write()
s 之间的间隔必须在 5ms 以内 - 在调用
write()
之前,我必须使用tcsetattr()
更改奇偶校验模式 - 但间隔结果是
18ms
,几乎tcsetattr()
时间被阻止。 - 但是如果我在
tcsetattr()
之前打电话给printf()
或std::cout
,间隔减少到1~2ms
。
也就是说,不知何故,在tcsetattr()
之前调用 printf tcsetattr()
更快地从阻塞中返回。
--------------------------更新----------------------------
我在这个问题上取得了进展。
我说我必须放printf()
或std::cout
,以使阻塞时间缩短tcsetattr()
。
但它并没有打印一些东西来影响这个问题。
它只是需要一些延迟,也就是说,如果我在调用tcsetattr()
之前放usleep(500)
,它也会对字节间时间减少1~2ms
产生影响,从tcsetattr()
更快地返回。
我假设,如果我用TCSADRAIN
标志调用tcsetattr()
,它会等到串行缓冲区中的所有数据都传输完毕,然后更改为我想要的设置。
它可能会造成一些延迟。
但是如果我专门调用延迟,在我调用tcsetattr()
之前,缓冲区状态已经是空的(因为在延迟时间内,串行缓冲区中的数据被传输(,因此没有阻塞。
这是我假设的情况,可能吗?
这是我假设的情况,可能吗?
你一定是对的。 锯末 写道:
uart_wait_until_sent(( in
drivers/tty/serial/serial_core.c
解释了为什么当涉及"耗尽"时,字符之间的间隔会被量化:串行端口驱动程序使用仅毫秒分辨率的延迟轮询TIOCSER_TEMP(发射器空(状态。
这几乎是对的,除了轮询周期分辨率不是毫秒,而是 jiffies:
while (!port->ops->tx_empty(port)) {
msleep_interruptible(jiffies_to_msecs(char_time));
…
因此,如果 HZ 为 100,char_time
为 1(最小值(,则每 10 毫秒检查一次状态。正如你所写:
但是如果我打电话专门延迟,在我打电话给
tcsetattr()
之前, 缓冲区状态已经为空(因为在延迟时间内,数据 在串行缓冲区中被传输(,因此没有阻塞。
换句话说,如果你延迟这个过程的时间足够长,以至于当它到达发射机空测试时,!port->ops->tx_empty(port)
发射机已经是空的,就不会发生睡眠;否则它至少会休眠 1 分钟。
你为什么不试试 sprintf/snprintf 而不是 printf??因为关于snprintf,你可以加快时间,如果你缩短了字符串字节?
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 从文本文件中读取时钟时间和事件时间并进行处理
- 具有未知值时的时间复杂性
- 如何减少花费的时间
- C++在变量给定的指定时间内关闭电脑
- rcpp函数中的清理时间很长
- 对于等待以 std::future wait() 返回的函数的 CPU 使用率或检查标志在循环中休眠一段时间哪个更好?
- 标志以将IPDB和IOBJ文件从链接时间代码生成优化(LTCG)移动
- 是否可以在编译时间作为宏上检测GCC编译标志
- 带有 TCSADRAIN 标志的 tcsetattr() 阻塞时间很奇怪
- 哪些标志将改进gcc/clang编译时间
- 要编译的标志和编译的时间