6

我想编写一个模拟串行端口上的设备的程序。我正在尝试使用伪终端来完成此操作。我想要一个不同的进程来控制主人。此过程充当串行设备仿真器。我希望另一个进程(例如 kermit)能够使用从终端与主机通信。由于不同的流程要求,我没有使用任何分叉。互联网上几乎每个伪终端示例都显示了将 fork() 用于主/从。

我让它朝一个方向工作。也就是说,我可以让从属进程将数据写入从属伪终端,而主进程将从主伪终端读取数据就好了。

问题在另一个方向。我无法让主机写入数据和从机读取数据。

我将展示非工作双向代码和工作单向代码。

非工作双向master:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[])
{
  // get the master fd
  int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if(masterfd < 0)
  {
    perror("getpt");
    exit(1);
  }

  // grant access to the slave
  if(grantpt(masterfd) < 0)
  {
    perror("grantpt");
    exit(1);
  }

  // unlock the slave
  if(unlockpt(masterfd) < 0)
  {
    perror("unlockpt");
    exit(1);
  }

  // get the path to the slave
  char slavepath[64];
  if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
  {
    perror("ptsname_r");
    exit(1);
  }

  printf("Using %s\n", slavepath);

  char bufout = 'D';
  char bufin;

  int c;
  while(1)
  {
    printf("reading\n");
    c = read(masterfd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    if(bufout == 'D') bufout = 'E';
    else if(bufout == 'E') bufout = 'D';
    printf("writing %c\n", bufout);
    c = write(masterfd, &bufout, 1);
    printf("wrote %i bytes\n", c);
    if(c == -1) break;

    sleep(1);
  }

  close(masterfd);

  return 0;
}

非工作双向奴隶:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
  int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }

  char bufout = 'A';
  char bufin;

  int c;
  while(1)
  {
    if(bufout == 'A') bufout = 'B';
    else if(bufout == 'B') bufout = 'A';
    printf("writing %c\n", bufout);
    c = write(fd, &bufout, 1);
    if(c == -1) break;

    printf("reading\n");
    c = read(fd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    sleep(1);
  }

  close(fd);
}

非工作主机的输出:(注意接收到的第一个字符来自从机,然后是主机写入的字符。换句话说,主机正在读取它写给主机 pts 的相同字符,而忽略从机正在写作,除了第一个字符。)

Using /dev/pts/15
reading
read 1 bytes: B     [<--- FROM THE SLAVE]
writing E
wrote 1 bytes
reading
read 1 bytes: E     [<--- REST FROM THE MASTER]
writing D
wrote 1 bytes
reading
read 1 bytes: D
writing E
wrote 1 bytes
reading
read 1 bytes: E
writing D
wrote 1 bytes
reading
read 1 bytes: D
^C

非工作从机的输出:(从不接收主机正在写入的内容。)

writing B
reading
^C

工作单向大师:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[])
{
  // get the master fd
  int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if(masterfd < 0)
  {
    perror("getpt");
    exit(1);
  }

  // grant access to the slave
  if(grantpt(masterfd) < 0)
  {
    perror("grantpt");
    exit(1);
  }

  // unlock the slave
  if(unlockpt(masterfd) < 0)
  {
    perror("unlockpt");
    exit(1);
  }

  // get the path to the slave
  char slavepath[64];
  if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
  {
    perror("ptsname_r");
    exit(1);
  }

  printf("Using %s\n", slavepath);

  char bufout = 'D';
  char bufin;

  int c;
  while(1)
  {
    printf("reading\n");
    c = read(masterfd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    sleep(1);
  }

  close(masterfd);

  return 0;
}

工作单向奴隶:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
  int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }

  char bufout = 'A';
  char bufin;

  int c;
  while(1)
  {
    if(bufout == 'A') bufout = 'B';
    else if(bufout == 'B') bufout = 'A';
    printf("writing %c\n", bufout);
    c = write(fd, &bufout, 1);
    if(c == -1) break;

    sleep(1);
  }

  close(fd);
}

工作主机的输出:(读取从机成功写入的内容。)

Using /dev/pts/15
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
^C

工作从机的输出:

writing B
writing A
writing B
writing A
writing B
^C
4

1 回答 1

7

我注意到,如果我们使用终端仿真器(例如screenpicocom代替您的从属程序),您的第一个示例就可以工作。

我认为差异来自各种“烹饪”设置。这是创建时的默认状态pts

$ stty -F /dev/pts/2 
speed 38400 baud; line = 0;
-brkint -imaxbel

现在在这里它变成了什么,在它被打开之后screen

$ stty -F /dev/pts/2 
speed 9600 baud; line = 0;
kill = ^H; min = 100; time = 2;
-icrnl -imaxbel
-opost -onlcr
-isig -icanon -echo

然后我尝试pts在读/写操作之前在从程序中设置原始模式。原始模式是线设置的组合,可以尽可能多地禁用烹饪。

struct termios ts;

if(tcgetattr(fd, &ts))
{
  perror("tcgetattr");
  exit(1);
}

cfmakeraw(&ts);
tcsetattr (fd, TCSANOW, &ts);

看起来它使它工作。

掌握:

$ ./pts_master 
Using /dev/pts/2
reading
read 1 bytes: B
writing E
wrote 1 bytes
reading
read 1 bytes: A
writing D
wrote 1 bytes
reading
read 1 bytes: B
writing E
wrote 1 bytes
...

奴隶:

$ ./pts_slave 
writing B
reading
read 1 bytes: E
writing A
reading
read 1 bytes: D
writing B
...
于 2014-05-04T20:49:49.497 回答