java 非阻塞读取_termios VMIN VTIME和阻塞/非阻塞读取操作

本文深入探讨了在非阻塞模式下使用VMIN/VTIME参数的影响,特别是在不同串口数据速率下的行为表现。揭示了VTIME作为间歇定时器的作用机制,并通过实验验证了其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

安德烈是对的 . 在非阻塞模式下,VMIN / VTIME无效(FNDELAY / O_NDELAY似乎是O_NONBLOCK的linux变体,便携式,POSIX标志) .

将select()与非阻塞模式的文件一起使用时,会为每个到达的字节获取一个事件 . 在高串行数据速率下,这会破坏CPU . 最好在VMIN中使用阻塞模式,以便select()在触发事件之前等待数据块,而VTIME用于限制小于VMIN的块的延迟 .

Sam说:“如果你想确保每半秒获得一次数据就可以设置vtime”(VTIME = 5) .

直觉上,你可能会认为这是真的,但事实并非如此 . BSD termios手册页比linux更好地解释了它(尽管它们的工作方式相同) . VTIME定时器是一个间歇定时器 . 它从每个新字节到达串行端口开始 . 在最坏的情况下,select()可以在触发事件之前等待最多20秒 .

假设您有VMIN = 250,VTIME = 1和串行端口为115200 bps . 还假设您有一个连接设备缓慢发送单个字节,以9 cps的一致速率 . 字节之间的时间为0.11秒,足以使0.10的字节间定时器到期,而select()则报告每个字节的可读事件 . 一切都很好 .

现在假设您的设备将其输出速率提高到11 cps . 字节之间的时间是0.09秒 . 它不足以让间歇计时器到期,并且每个新字节都会重新开始 . 要获得可读事件,必须满足VMIN = 250 . 在11 cps,这需要22.7秒 . 您的设备似乎已停滞不前,但VTIME设计是导致延迟的真正原因 .

我使用两个Perl脚本,发送器和接收器,双端口串行卡和零调制解调器电缆对此进行了测试 . 我证明了它的工作原理正如man page所说的那样 . VTIME是一个间歇定时器,随着每个新字节的到来而复位 .

更好的设计会使计时器锚定,而不是滚动 . 它将继续滴答,直到它到期,或VMIN满意,以先到者为准 . 现有设计可以修复,但需要克服30年的遗产 .

在实践中,您可能很少遇到这种情况 . 但它潜伏着,所以要小心 .

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <termios.h> #include <pthread.h> //串口初始化功能函数,fd:串口驱动设备的文件描述符 int tty_init(int fd) { //串口参数结构体 struct termios old_uart,new_uart; //清空结构体变量内容,memset bzero(&old_uart,sizeof(struct termios)); bzero(&new_uart,sizeof(struct termios)); //获取串口属性,旧的串口 tcgetattr(fd,&old_uart); //设置原始属性,重新设置属性 cfmakeraw(&new_uart); //激活本地连接CLOCAL与接收使能CREAD的选项 new_uart.c_cflag |= CLOCAL|CREAD; //设置输出、输入波特率 cfsetospeed(&new_uart, B115200); cfsetispeed(&new_uart, B115200); //设置数据位为8位 new_uart.c_cflag &= ~CSIZE; new_uart.c_cflag |= CS8; //无校验位 new_uart.c_cflag &= ~PARENB; //1个停止位 new_uart.c_cflag &= ~CSTOPB; //设置等待时间、最小接收字符个数 new_uart.c_cc[VTIME] = 0; new_uart.c_cc[VMIN] = 1; new_uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //本地模式设置为原始模式 //清空缓冲区 tcflush(fd,TCIOFLUSH); //设置串口属性到文件中 -- 生效属性 tcsetattr(fd, TCSANOW,&new_uart); return 0; } void *BLE_Send(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *s_msg = (char *)malloc(1024); //申请写入数据的空间 while(1) { printf("send:"); //从键盘输入数据保存到msg中 fgets(s_msg,1024,stdin); write(ble_fd,s_msg,strlen(s_msg)); //写数据 if(strncmp(s_msg,"quit",4)==0) { memset(s_msg,0,1024); break; } //清空内存 memset(s_msg,0,1024); } //释放堆空间 free(s_msg); } void *BLE_Recv(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *r_msg = (char *)malloc(1024); //申请接收数据的空间 while(1) { //读取串口接收的数据 read(ble_fd,r_msg,1024); printf("recv:%s\n",r_msg); if(strncmp(r_msg,"quit",4)==0) { memset(r_msg,0,1024); break; } //清空内存 memset(r_msg,0,1024); } //释放堆空间 free(r_msg); } int main(int argc,char *argv[]) { pthread_t ble_tid1=0,ble_tid2=0; //访问串口1驱动 int ble_fd = open("/dev/ttySAC1",O_RDWR); if(ble_fd==-1) { printf("open BLE error...\n"); return -1; } //串口初始化 tty_init(ble_fd); //串口数据读写线程 pthread_create(&ble_tid1,NULL,BLE_Send,(void *)&ble_fd); pthread_create(&ble_tid2,NULL,BLE_Recv,(void *)&ble_fd); //接合线程 pthread_join(ble_tid1,NULL); pthread_join(ble_tid2,NULL); //关闭文件 close(ble_fd); return 0; }#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <termios.h> #include <pthread.h> //串口初始化功能函数,fd:串口驱动设备的文件描述符 int tty_init(int fd) { //串口参数结构体 struct termios old_uart,new_uart; //清空结构体变量内容,memset bzero(&old_uart,sizeof(struct termios)); bzero(&new_uart,sizeof(struct termios)); //获取串口属性,旧的串口 tcgetattr(fd,&old_uart); //设置原始属性,重新设置属性 cfmakeraw(&new_uart); //激活本地连接CLOCAL与接收使能CREAD的选项 new_uart.c_cflag |= CLOCAL|CREAD; //设置输出、输入波特率 cfsetospeed(&new_uart, B115200); cfsetispeed(&new_uart, B115200); //设置数据位为8位 new_uart.c_cflag &= ~CSIZE; new_uart.c_cflag |= CS8; //无校验位 new_uart.c_cflag &= ~PARENB; //1个停止位 new_uart.c_cflag &= ~CSTOPB; //设置等待时间、最小接收字符个数 new_uart.c_cc[VTIME] = 0; new_uart.c_cc[VMIN] = 1; new_uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //本地模式设置为原始模式 //清空缓冲区 tcflush(fd,TCIOFLUSH); //设置串口属性到文件中 -- 生效属性 tcsetattr(fd, TCSANOW,&new_uart); return 0; } void *BLE_Send(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *s_msg = (char *)malloc(1024); //申请写入数据的空间 while(1) { printf("send:"); //从键盘输入数据保存到msg中 fgets(s_msg,1024,stdin); write(ble_fd,s_msg,strlen(s_msg)); //写数据 if(strncmp(s_msg,"quit",4)==0) { memset(s_msg,0,1024); break; } //清空内存 memset(s_msg,0,1024); } //释放堆空间 free(s_msg); } void *BLE_Recv(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *r_msg = (char *)malloc(1024); //申请接收数据的空间 while(1) { //读取串口接收的数据 read(ble_fd,r_msg,1024); printf("recv:%s\n",r_msg); if(strncmp(r_msg,"quit",4)==0) { memset(r_msg,0,1024); break; } //清空内存 memset(r_msg,0,1024); } //释放堆空间 free(r_msg); } int main(int argc,char *argv[]) { pthread_t ble_tid1=0,ble_tid2=0; //访问串口1驱动 int ble_fd = open("/dev/ttySAC1",O_RDWR); if(ble_fd==-1) { printf("open BLE error...\n"); return -1; } //串口初始化 tty_init(ble_fd); //串口数据读写线程 pthread_create(&ble_tid1,NULL,BLE_Send,(void *)&ble_fd); pthread_create(&ble_tid2,NULL,BLE_Recv,(void *)&ble_fd); //接合线程 pthread_join(ble_tid1,NULL); pthread_join(ble_tid2,NULL); //关闭文件 close(ble_fd); return 0; }#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <termios.h> #include <pthread.h> //串口初始化功能函数,fd:串口驱动设备的文件描述符 int tty_init(int fd) { //串口参数结构体 struct termios old_uart,new_uart; //清空结构体变量内容,memset bzero(&old_uart,sizeof(struct termios)); bzero(&new_uart,sizeof(struct termios)); //获取串口属性,旧的串口 tcgetattr(fd,&old_uart); //设置原始属性,重新设置属性 cfmakeraw(&new_uart); //激活本地连接CLOCAL与接收使能CREAD的选项 new_uart.c_cflag |= CLOCAL|CREAD; //设置输出、输入波特率 cfsetospeed(&new_uart, B115200); cfsetispeed(&new_uart, B115200); //设置数据位为8位 new_uart.c_cflag &= ~CSIZE; new_uart.c_cflag |= CS8; //无校验位 new_uart.c_cflag &= ~PARENB; //1个停止位 new_uart.c_cflag &= ~CSTOPB; //设置等待时间、最小接收字符个数 new_uart.c_cc[VTIME] = 0; new_uart.c_cc[VMIN] = 1; new_uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //本地模式设置为原始模式 //清空缓冲区 tcflush(fd,TCIOFLUSH); //设置串口属性到文件中 -- 生效属性 tcsetattr(fd, TCSANOW,&new_uart); return 0; } void *BLE_Send(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *s_msg = (char *)malloc(1024); //申请写入数据的空间 while(1) { printf("send:"); //从键盘输入数据保存到msg中 fgets(s_msg,1024,stdin); write(ble_fd,s_msg,strlen(s_msg)); //写数据 if(strncmp(s_msg,"quit",4)==0) { memset(s_msg,0,1024); break; } //清空内存 memset(s_msg,0,1024); } //释放堆空间 free(s_msg); } void *BLE_Recv(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *r_msg = (char *)malloc(1024); //申请接收数据的空间 while(1) { //读取串口接收的数据 read(ble_fd,r_msg,1024); printf("recv:%s\n",r_msg); if(strncmp(r_msg,"quit",4)==0) { memset(r_msg,0,1024); break; } //清空内存 memset(r_msg,0,1024); } //释放堆空间 free(r_msg); } int main(int argc,char *argv[]) { pthread_t ble_tid1=0,ble_tid2=0; //访问串口1驱动 int ble_fd = open("/dev/ttySAC1",O_RDWR); if(ble_fd==-1) { printf("open BLE error...\n"); return -1; } //串口初始化 tty_init(ble_fd); //串口数据读写线程 pthread_create(&ble_tid1,NULL,BLE_Send,(void *)&ble_fd); pthread_create(&ble_tid2,NULL,BLE_Recv,(void *)&ble_fd); //接合线程 pthread_join(ble_tid1,NULL); pthread_join(ble_tid2,NULL); //关闭文件 close(ble_fd); return 0; }#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <termios.h> #include <pthread.h> //串口初始化功能函数,fd:串口驱动设备的文件描述符 int tty_init(int fd) { //串口参数结构体 struct termios old_uart,new_uart; //清空结构体变量内容,memset bzero(&old_uart,sizeof(struct termios)); bzero(&new_uart,sizeof(struct termios)); //获取串口属性,旧的串口 tcgetattr(fd,&old_uart); //设置原始属性,重新设置属性 cfmakeraw(&new_uart); //激活本地连接CLOCAL与接收使能CREAD的选项 new_uart.c_cflag |= CLOCAL|CREAD; //设置输出、输入波特率 cfsetospeed(&new_uart, B115200); cfsetispeed(&new_uart, B115200); //设置数据位为8位 new_uart.c_cflag &= ~CSIZE; new_uart.c_cflag |= CS8; //无校验位 new_uart.c_cflag &= ~PARENB; //1个停止位 new_uart.c_cflag &= ~CSTOPB; //设置等待时间、最小接收字符个数 new_uart.c_cc[VTIME] = 0; new_uart.c_cc[VMIN] = 1; new_uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //本地模式设置为原始模式 //清空缓冲区 tcflush(fd,TCIOFLUSH); //设置串口属性到文件中 -- 生效属性 tcsetattr(fd, TCSANOW,&new_uart); return 0; } void *BLE_Send(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *s_msg = (char *)malloc(1024); //申请写入数据的空间 while(1) { printf("send:"); //从键盘输入数据保存到msg中 fgets(s_msg,1024,stdin); write(ble_fd,s_msg,strlen(s_msg)); //写数据 if(strncmp(s_msg,"quit",4)==0) { memset(s_msg,0,1024); break; } //清空内存 memset(s_msg,0,1024); } //释放堆空间 free(s_msg); } void *BLE_Recv(void *arg) { int ble_fd = *(int *)arg; //获取文件描述符 char *r_msg = (char *)malloc(1024); //申请接收数据的空间 while(1) { //读取串口接收的数据 read(ble_fd,r_msg,1024); printf("recv:%s\n",r_msg); if(strncmp(r_msg,"quit",4)==0) { memset(r_msg,0,1024); break; } //清空内存 memset(r_msg,0,1024); } //释放堆空间 free(r_msg); } int main(int argc,char *argv[]) { pthread_t ble_tid1=0,ble_tid2=0; //访问串口1驱动 int ble_fd = open("/dev/ttySAC1",O_RDWR); if(ble_fd==-1) { printf("open BLE error...\n"); return -1; } //串口初始化 tty_init(ble_fd); //串口数据读写线程 pthread_create(&ble_tid1,NULL,BLE_Send,(void *)&ble_fd); pthread_create(&ble_tid2,NULL,BLE_Recv,(void *)&ble_fd); //接合线程 pthread_join(ble_tid1,NULL); pthread_join(ble_tid2,NULL); //关闭文件 close(ble_fd); return 0; }练习:使用蓝牙通信,手机发送“prev”显示上一张图片,发送"next"显示下一张图片
最新发布
07-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值