我想写一个模拟串口上的设备的程序。 我试图用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
我注意到,如果我们使用终端模拟器,如screen
或picocom
而不是你的奴隶程序,你的第一个例子工程。
我认为差异来自各种“烹饪”设置。 这是创建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 ...