如何使用管道进行非阻塞 IPC(UART 仿真)

Posted

技术标签:

【中文标题】如何使用管道进行非阻塞 IPC(UART 仿真)【英文标题】:How to use pipes for nonblocking IPC (UART emulation) 【发布时间】:2010-12-22 11:28:07 【问题描述】:

问题:

我想编写一些模拟串行端口连接的测试/模拟代码。这 真正的代码是这样的:

DUT testtool.exe

我的计划是在 linux 上创建一个测试应用程序 (CodeUnderTest.out),它使用两个(读取和写入)命名管道作为参数来启动 testtool.out。但是我不知道如何使所有管道 IO 不阻塞!

设置如下所示:。

CodeUnderTest.out testTool.out(从 CodeUnderTest.out 启动)

我试过打开管道如下:

open(wpipe,O_WRONLY|O_NONBLOCK);
open(rpipe,O_RDONLY|O_NONBLOCK);

但是写入会阻塞,直到读者打开 wpipe。接下来我尝试了以下方法:

open(wpipe,O_RDWR|O_NONBLOCK);
open(rpipe,O_RDONLY|O_NONBLOCK);

但是第一条消息的读者永远不会得到任何数据(虽然不会阻塞)

我还尝试在每条消息周围添加打开和关闭调用,但这也不起作用...

测试代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

pid_t pid;
char* rpipe, *wpipe,*x;
FILE *rh,*wh;
int rfd,wfd;

void openrpipe( void )
   
   rfd = open(rpipe,O_RDONLY|O_NONBLOCK);
   rh = fdopen(rfd,"rb");
   printf("%sopeningr %x\n",x,rh);
   
void openwpipe( void )
   
   //Fails when reader not already opened
   //wfd = open(wpipe,O_WRONLY|O_NONBLOCK);
   wfd = open(wpipe,O_RDWR|O_NONBLOCK);
   wh = fdopen(wfd,"wb");
   printf("%sopeningw %x\n",x,wh);
   
void closerpipe( void )
   
   int i;
   i = fclose(rh);
   printf("%sclosingr %d\n",x,i);
   
void closewpipe( void )
   
   int i;
   i = fclose(wh);
   printf("%sclosingw %d\n",x,i);
   
void readpipe( char* expect, int len)
   
   char buf[1024];
   int i=0;
   printf("%sreading\n",x);
   while(i==0)
      
      //printf(".");
      i = fread(buf,1,len,rh);
      
   printf("%sread (%d) %s\n",x,i,buf);
   
void writepipe( char* data, int len)
   
   int i,j;
   printf("%swriting\n",x);
   i = fwrite(data,1,len,rh);
   j = fflush(rh); //No help!
   printf("%sflush %d\n",x,j);
   printf("%swrite (%d) %s\n",x,i,data);
   
int main(int argc, char **argv)
   
   rpipe = "readfifo";
   wpipe = "writefifo";
   x = "";
   pid = fork();
   if( pid == 0)
      
      wpipe = "readfifo";
      rpipe = "writefifo";
      x = "   ";
      openrpipe();
      openwpipe();
      writepipe("paul",4);
      readpipe("was",3);
      writepipe("here",4);
      closerpipe();
      closewpipe();
      exit(0);
      
   openrpipe();
   openwpipe();
   readpipe("paul",4);
   writepipe("was",3);
   readpipe("here",4);
   closerpipe();
   closewpipe();
   return( -1 );
   

顺便说一句:

要使用上面的测试代码,您需要在当前目录中创建 2 个管道:

mkfifo ./readfifo

mkfifo ./writefifo

更新:

好的,我想我现在有正确的设置。请告诉我是否可以做得更好

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

pid_t pid;
char* rpipe, *wpipe,*x;
int rfd,wfd;

FILE* openpipe( char* str, char* access )
   
   FILE* fh;
   rfd = open(str,O_RDWR|O_NONBLOCK);
   fh = fdopen(rfd,access);
   printf("%sopen(%s,%s)=%x\n",x,str,access,fh);
   return(fh);
   
void closepipe( FILE* fh )
   
   int i;
   i = fclose(fh);
   printf("%sclosing %d\n",x,i);
   
void readpipe( char* expect, int len, FILE* fh)
   
   char buf[1024];
   int i=0;
   printf("%sreading\n",x);
   while(i==0)
      
      //printf("%c",strlen(x)?'.':'#');
      i = fread(buf,1,len,fh);
      
   buf[i] = 0;
   printf("%sread (%d) %s\n",x,i,buf);
   
void writepipe( char* data, int len, FILE* fh)
   
   int i=0,j;
   printf("%swriting\n",x);
   //while(i==0)
   i = fwrite(data,1,len,fh);
   j=fflush(fh);
   printf("%sflush %d\n",x,j);
   printf("%swrite (%d) %s\n",x,i,data);
   
int main(int argc, char **argv)
   
   FILE *rh,*wh;
   rpipe = "readfifo";
   wpipe = "writefifo";
   x = "";
   pid = fork();
   if( pid == 0)
      
      FILE *rh,*wh;
      wpipe = "readfifo";
      rpipe = "writefifo";
      x = "   ";
      rh=openpipe(rpipe,"rb");
      wh=openpipe(wpipe,"wb");
      writepipe("paul",4,wh);
      readpipe("was",3,rh);
      writepipe("here",4,wh);
      closepipe(rh);
      closepipe(wh);
      exit(0);
      
   rh=openpipe(rpipe,"rb");
   wh=openpipe(wpipe,"wb");
   readpipe("paul",4,rh);
   writepipe("was",3,wh);
   readpipe("here",4,rh);
   closepipe(rh);
   closepipe(wh);
   return( -1 );
   

【问题讨论】:

【参考方案1】:

一般来说,模拟串行端口进行此类测试的最佳方法是使用伪终端,因为串行端口是ttypty 也是。

posix_openpt()grantpt()unlockpt()ptsname() 是您需要的电话。主端由设备仿真器读写,从端作为串口向被测程序开放。

【讨论】:

我不确定与命名管道相比有什么优势/差异?有什么想法吗? @codebauer:通常的终端控制功能在pty上工作(例如tcsetattr()),pty slave的路径可以直接传递给正在测试的进程来代替串口它通常使用的设备(因此不需要对孩子进行修改)。简而言之,它“看起来”更像是从属端的串行端口。【参考方案2】:

在有读者之前不要写入管道。您应该能够使用selectpoll 来了解阅读器何时连接。

【讨论】:

嗯,这是为什么呢?它当然不属于 UART 类比。这只会取代读取轮询 (while(!fread(..)) @codebauer:使用 UART,如果您在连接串行电缆之前写入,则写入只会进入梦幻岛而没有收到任何内容。我假设您也不希望这种行为......或者您可以定期发送。 OTOH,启用了流控制的 UART 的行为可能与管道完全相同(如果没有连接的读卡器,则写入块)。 如果目标是模拟 UART,则可能需要写入未连接线的能力。今天很少使用 UART 级别的流控制——大多数情况下,握手是在应用程序级别完成的,并且应用程序的握手算法可能假设能够写入未连接的线路。要以这种方式进行 UART 仿真,应用程序应该调用一个包装函数,该函数要么在硬件情况下写入真正的 uart,要么在仿真情况下仅在管道连接时写入管道,否则只是静默丢弃写入。

以上是关于如何使用管道进行非阻塞 IPC(UART 仿真)的主要内容,如果未能解决你的问题,请参考以下文章

IPC通信_有名管道(FIFO)

未发出信号的非阻塞 ConnectNamedPipe 事件

使用 /dev/tty* 进行 9 位 uart 仿真

我可以在 Linux 上打开命名管道以在 Python 中进行非阻塞写入吗?

命名管道与 IPC 的 COM

ipc_pipe