Linux read() 系统调用比我预期的要长(串口编程)

Linux read() system call takes longer than my expectation ( serial port programming )

我正在尝试读取从 tty/USB0 发送的数据并以字节格式打印出来。

问题:

  1. 我希望在读取字节数达到 40 时打印出数据,但是,时间比我预期的要长得多。 read() 系统调用挂起,我相信数据应该已经大于 40。数据最终会被打印出来,但不会花费这么长时间。我在这个编程中有什么错误吗?

感谢

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

#define BAUDRATE B9600            
#define MODEMDEVICE "/dev/ttyUSB0"

#define FALSE 0
#define TRUE 1

main()
{
int fd,c, res;
struct termios oldtio,newtio;
unsigned char buf[40];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }

tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));

newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 1;
newtio.c_lflag = ICANON;

tcflush(fd, TCIOFLUSH);
tcsetattr(fd,TCSANOW,&newtio);

int i;
while (1) {
    res = read(fd,buf,40);
    if(res==40){
        printf("res reaches 40 \n");
    }
    printf("res: %d\n",res);
    for(i=0;i<res;++i){
    printf("%02x ", buf[i]);
    }
    return;
    }
}

--------------------原始模式代码-------------------- --

  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <termios.h>
  #include <stdio.h>

  #define BAUDRATE B9600
  #define MODEMDEVICE "/dev/ttyUSB0"
  #define _POSIX_SOURCE 1 /* POSIX compliant source */
  #define FALSE 0
  #define TRUE 1

  volatile int STOP=FALSE;

  main()
  {
    int fd,c, res;
    struct termios oldtio,newtio;
    unsigned char buf[255];

    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
    if (fd <0) {perror(MODEMDEVICE); exit(-1); }

    tcgetattr(fd,&oldtio); /* save current port settings */

    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;

    /* set input mode (non-canonical, no echo,...) */
    newtio.c_lflag = 0;

    newtio.c_cc[VTIME]    = 0;   
    newtio.c_cc[VMIN]     = 40;   

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW,&newtio);

    int i;
    while (STOP==FALSE) {       
      res = read(fd,buf,255);   
      for( i=0;i<res;++i){
        printf("%02x \n", buf[i]);
      }
    }
    tcsetattr(fd,TCSANOW,&oldtio);
  }

一旦缓冲区容量已满(即 40 ),它现在可以打印出数据。 1 个问题:

  1. 当我修改printf时

    printf("%02x ", buf[i]); ( remove "\n" ) 当缓冲区已满时,它不会打印出来,直到接收到更多字节。为什么会这样?

谢谢

您需要将终端切换到 raw 模式以禁用行缓冲。

引用 this answer:

The terms raw and cooked only apply to terminal drivers. "Cooked" is called canonical and "raw" is called non-canonical mode.

The terminal driver is, by default a line-based system: characters are buffered internally until a carriage return (Enter or Return) before it is passed to the program - this is called "cooked". This allows certain characters to be processed (see stty(1)), such as Cntl-D, Cntl-S, Ctrl-U Backspace); essentially rudimentary line-editing. The terminal driver "cooks" the characters before serving them up.

The terminal can be placed into "raw" mode where the characters are not processed by the terminal driver, but are sent straight through (it can be set that INTR and QUIT characters are still processed). This allows programs like emacs and vi to use the entire screen more easily.

You can read more about this in the "Canonical mode" section of the termios(3) manpage.

参见例如this or this如何以编程方式实现(没有检查代码,但应该很容易找到)。

或者您可以使用例如straceltrace 检查 stty -F /dev/ttyUSB0 raw 的作用(或阅读描述它的手册页)。

编辑>

关于没有换行符的 printf -- fflush(stdout); 紧随其后应该有所帮助(正在进行另一个行缓冲)。

您可以考虑阅读 this and maybe this