C 如何避免读取函数的阻塞?

C 如何避免读取函数的阻塞?,c,serial-port,C,Serial Port,我正在尝试从cygwin命令中心的USB端口写入和读取数据。 我已经成功地在设备连接时写入和读取数据,但我希望找到一种方法来检测其他设备是否不存在或无法发送回任何数据。我当前的测试代码如下所示(我尝试了许多不同的方法,但似乎都不起作用) #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括 #包括“USB_com.h” #包括“unistd.h” void main() { int fd,值,读取字节,写入字节,n字节,i,j; char-buffer[20]; 炭抛光剂[20]; f

我正在尝试从cygwin命令中心的USB端口写入和读取数据。 我已经成功地在设备连接时写入和读取数据,但我希望找到一种方法来检测其他设备是否不存在或无法发送回任何数据。我当前的测试代码如下所示(我尝试了许多不同的方法,但似乎都不起作用)

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括“USB_com.h”
#包括“unistd.h”
void main()
{
int fd,值,读取字节,写入字节,n字节,i,j;
char-buffer[20];
炭抛光剂[20];
fd=USB_init(“/dev/com1”);
printf(“输入消息(写入退出以终止会话):”;
fgets(buff,19,stdin);
while(strncmp(“退出”,buff,4)!=0)
{
字节写入=写入(fd,buff,19);
睡眠(1);
字节_read=read(fd,buffer,19);
printf(“收到的字符串:%s\n”,buffer);
memset(buff,'\0',19);
printf(“输入消息(写入退出以终止会话):”;
fgets(buff,19,stdin);
}
USB_清理(fd);
}
我的USB_init.c用于从USB设备进行写入和读取,如下所示

#include "USB_init.h"

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


/* baudrate settings are defined in <asm/termbits.h>, which is
 * included by <termios.h> */
#ifndef BAUDRATE
#define BAUDRATE B9600
#endif

#define _POSIX_SOURCE 1     /* POSIX compliant source */

static int fd, c, res;
static struct termios oldtio, newtio;
static char *device;

int USB_init(char *modemdevice)
{
    /* 
     * Open modem device for reading and writing and not as controlling tty
     * because we don't want to get killed if linenoise sends CTRL-C.
     **/
    device = modemdevice;
    //fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY);
    fd = open (device, O_RDWR | O_NOCTTY );
    if (fd < 0)
      {
      perror (device);
      exit(-1);
      }

    tcgetattr (fd, &oldtio);    /* save current serial port settings */
    bzero (&newtio, sizeof (newtio));   /* clear struct for new port settings */

    /* 
     *BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
     *CRTSCTS : output hardware flow control (only used if the cable has
     *all necessary lines. )
     *CS8     : 8n1 (8bit,no parity,1 stopbit)
     *CLOCAL  : local connection, no modem contol
     *CREAD   : enable receiving characters
     **/
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

    /*
     *IGNPAR  : ignore bytes with parity errors
     *ICRNL   : map CR to NL (otherwise a CR input on the other computer
     *          will not terminate input)
     *          otherwise make device raw (no other input processing)
     **/
    newtio.c_iflag = IGNPAR | ICRNL;

    /*
     * Map NL to CR NL in output.
     *                  */
#if 0
    newtio.c_oflag = ONLCR;
#else
    newtio.c_oflag = 0;
#endif


    /*
     * ICANON  : enable canonical input
     *           disable all echo functionality, and don't send signals to calling program
     **/
#if 1
    newtio.c_lflag = ICANON;
#else
    newtio.c_lflag = 0;
#endif

    /* 
     * initialize all control characters 
     * default values can be found in /usr/include/termios.h, and are given
     * in the comments, but we don't need them here
     *                                       */
    newtio.c_cc[VINTR] = 0; /* Ctrl-c */
    newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
    newtio.c_cc[VERASE] = 0;    /* del */
    newtio.c_cc[VKILL] = 0; /* @ */
    newtio.c_cc[VEOF] = 4;  /* Ctrl-d */
    newtio.c_cc[VTIME] = 0; /* inter-character timer unused*/
    newtio.c_cc[VMIN] = 1;  /* blocking read until 1 character arrives*/
    newtio.c_cc[VSWTC] = 0; /* '\0' */
    newtio.c_cc[VSTART] = 0;    /* Ctrl-q */
    newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
    newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
    newtio.c_cc[VEOL] = 0;  /* '\0' */
    newtio.c_cc[VREPRINT] = 0;  /* Ctrl-r */
    newtio.c_cc[VDISCARD] = 0;  /* Ctrl-u */
    newtio.c_cc[VWERASE] = 0;   /* Ctrl-w */
    newtio.c_cc[VLNEXT] = 0;    /* Ctrl-v */
    newtio.c_cc[VEOL2] = 0; /* '\0' */

    /* 
     * now clean the modem line and activate the settings for the port
     **/
    tcflush (fd, TCIFLUSH);
    tcsetattr (fd, TCSANOW, &newtio);

    /*
     * terminal settings done, return file descriptor
     **/

    return fd;
}

void USB_cleanup(int ifd){
    if(ifd != fd) {
        fprintf(stderr, "WARNING! file descriptor != the one returned by serial_init()\n");
    }
    /* restore the old port settings */
    tcsetattr (ifd, TCSANOW, &oldtio);
}
#包括“USB_init.h”
#包括
#包括
#包括
#包括
#包括
#包括
#包括
/*波特率设置在中定义,即
*包括在*/
#ifndef波特率
#定义波特率B9600
#恩迪夫
#定义_POSIX_SOURCE 1/*兼容POSIX的源*/
静态int-fd、c、res;
静态结构术语oldtio、newtio;
静态字符*设备;
int USB_init(字符*调制解调器设备)
{
/* 
*打开调制解调器设备进行读写,而不是控制tty
*因为如果linenoise发送CTRL-C,我们不想被杀。
**/
设备=调制解调器设备;
//fd=打开(设备,O|RDWR | O|NOCTTY | O|NDELAY);
fd=开路(装置,O|RDWR | O|NOCTTY);
如果(fd<0)
{
perror(装置);
出口(-1);
}
tcgetattr(fd,&oldtio);/*保存当前串行端口设置*/
bzero(&newtio,sizeof(newtio));/*清除新端口设置的结构*/
/* 
*波特率:设置bps速率。您也可以使用cfsetispeed和cfsetospeed。
*CRTSCTS:输出硬件流量控制(仅当电缆已连接时使用)
*所有必要的线路。)
*CS8:8n1(8位,无奇偶校验,1个停止位)
*CLOCAL:本地连接,无调制解调器控制
*CREAD:启用接收字符
**/
newtio.c|cflag=波特率| CS8 | CLOCAL | CREAD;
/*
*IGNPAR:忽略具有奇偶校验错误的字节
*ICRNL:将CR映射到NL(否则,另一台计算机上的CR输入)
*不会终止输入)
*否则,使设备为原始(无其他输入处理)
**/
newtio.c_iflag=IGNPAR | ICRNL;
/*
*将NL映射到输出中的CR NL。
*                  */
#如果0
newtio.c_of lag=ONLCR;
#否则
newtio.c_of lag=0;
#恩迪夫
/*
*ICANON:启用规范输入
*禁用所有回显功能,不向呼叫程序发送信号
**/
#如果1
newtio.c_lflag=ICANON;
#否则
newtio.c_lflag=0;
#恩迪夫
/* 
*初始化所有控制字符
*默认值可以在/usr/include/termios.h中找到,并给出了
*在评论中,但我们这里不需要它们
*                                       */
newtio.c_cc[VINTR]=0;/*Ctrl-c*/
newtio.c_cc[VQUIT]=0;/*Ctrl-\*/
newtio.c_cc[VERASE]=0;/*del*/
newtio.c_cc[VKILL]=0;/*@*/
newtio.c_cc[VEOF]=4;/*Ctrl-d*/
newtio.c_cc[VTIME]=0;/*未使用字符间计时器*/
newtio.c_cc[VMIN]=1;/*在到达1个字符之前阻止读取*/
newtio.c_cc[VSWTC]=0;/*'\0'*/
newtio.c_cc[VSTART]=0;/*Ctrl-q*/
newtio.c_cc[VSTOP]=0;/*Ctrl-s*/
newtio.c_cc[VSUSP]=0;/*Ctrl-z*/
newtio.c_cc[VEOL]=0;/*'\0'*/
newtio.c_cc[vreprit]=0;/*Ctrl-r*/
newtio.c_cc[VDISCARD]=0;/*Ctrl-u*/
newtio.c_cc[vwrase]=0;/*Ctrl-w*/
newtio.c_cc[VLNEXT]=0;/*Ctrl-v*/
newtio.c_cc[VEOL2]=0;/*'\0'*/
/* 
*现在清洁调制解调器线路并激活端口设置
**/
tcflush(fd,TCIFLUSH);
tcsetattr(fd、TCSANOW和newtio);
/*
*终端设置完成,返回文件描述符
**/
返回fd;
}
无效USB_清理(int ifd){
如果(ifd!=fd){
fprintf(stderr,“警告!文件描述符!=串行_init()\n返回的文件描述符”);
}
/*恢复旧的端口设置*/
tcsetattr(ifd、TCSANOW和oldtio);
}
有人能告诉我,如果我没有收到任何数据,我怎么能像read(fd,buffer,19)这样做,但过了一段时间后,如果我没有收到任何数据,怎么能像read(fd,buffer,19)这样打印 printf(“与设备无接触”)


我非常感谢任何关于如何解决这个问题的建议

您可能想看看如何监视文件描述符,以便随时可以从中读取。可以通过暂停来调用它


或者将文件描述用于它或以后使用,这样如果没有可用数据,它将从返回,这将允许您实现on逻辑,以决定何时重试或取消读取。

处理序列时,您有两个选项:使用
选择
/
轮询
超时,或者使用termios配置端口

对于
select
poll
,方法是首先在描述符上等待事件,并在成功结果后读取。此方法的优点在于,它也适用于网络套接字和其他一些描述符类型:

int fd = ...
fd_set fds;
stuct timeval timeout;

timeout.tv_sec = 10; /* timeout in secs */
timeout.tv_usec = 0;
FD_ZERO(&fds)
FD_SET(fd, &fds)
if (select(fd, fds, NULL, NULL, &timeout) > 0)
  read(...)
else ... timeout
poll
示例与上面的示例非常相似

但是,当使用任何类型的串行设备时,有另一种方法可以使读取正确超时。您需要使用
VMIN
VTIME
属性:

newtio.c_cc[VTIME] = timeout; /* timeout in 1/10 of second  1==100ms, 10==1sec*/
newtio.c_cc[VMIN] = 0;

就是这样。

答案的第二部分是不完整和误导性的。VMIN和VTIME仅在使用非规范输入模式时才突出。@sawdust您怎么看
newtio.c_cc[VTIME] = timeout; /* timeout in 1/10 of second  1==100ms, 10==1sec*/
newtio.c_cc[VMIN] = 0;