如何使用单独的进程读取/写入到Linux伪terminal,但没有分叉?

我想写一个模拟串口上的设备的程序。 我试图用pseudoterminals来实现这一点。 我想要一个独特的过程来控制主人。 该过程用作串行设备仿真器。 我想要另一个进程(例如kermit)能够使用从terminal与主站进行通信。 由于不同的工艺要求,我没有使用任何叉子。 几乎互联网上的每个伪terminal示例都显示了fork()对主/从的使用。

我有它在一个方向工作。 也就是说,我可以让从属进程把数据写到从属伪terminal上,而且主服务器会把它从主要的伪terminal读出来。

问题是在另一个方向。 我不能让主人写数据和从服务器读取数据。

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

非工作双向主控:

#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); } 

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

 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 

    我注意到,如果我们使用终端模拟器,如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 ...