为什么Linux的pty驱动程序会用NUL替换VEOF?

12

在Linux上,似乎pty驱动程序会将终端设置为非规范模式(non-canonical mode)并在从主设备那里读取之前替换已写入的数据中的VEOF字符(^D, \4)为NUL字节(\0)。

这为什么会发生?它是否有任何合理的解释或者只是一个错误?

是否有任何方法可以避免这种情况? - 除了在写入任何内容之前等待从从设备那里输入,这是不切实际的,因为在从设备那里可能会有另一个程序 - 设置终端到原始模式的例程(我在此简化了)通常是任何具有行编辑功能的shell所做的。

虽然预期会将例如\r替换为\n(因为已应用ICRNL标志),但我无法理解这些NUL字节从哪里出现。

下面是测试用例:在Linux上将打印foo\x00\x00\x00\x00,而在*BSD上将打印foo\x04\x04\x04\x04,在Solaris上将打印foo

#define _XOPEN_SOURCE   600
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <termios.h>
#include <err.h>

#ifdef __sun
#include <stropts.h>
#define push_streams(fd)\
        if(ioctl(fd, I_PUSH, "ptem")) err(1, "ioctl(I_PUSH, ptem)");\
        if(ioctl(fd, I_PUSH, "ldterm")) err(1, "ioctl(I_PUSH, ldterm)");
#else
#define push_streams(sd)        /* no need */
#endif

int main(void){
        int mt, st; char *sname;

        /* openpty()-like boilerplate */
        if((mt = posix_openpt(O_RDWR|O_NOCTTY)) == -1) err(1, "posix_openpt");
        if(grantpt(mt)) err(1, "grantpt");
        if(unlockpt(mt)) err(1, "unlockpt");
        if(!(sname = ptsname(mt))) err(1, "ptsname");
        if((st = open(sname, O_RDWR|O_NOCTTY)) == -1) err(1, "open %s", sname);
        push_streams(st);

        /* master */ {
                char test[] = "foo\4\4\4\4";
                if(write(mt, test, sizeof test - 1) < sizeof test - 1)
                        err(1, "write");
        }
        /* slave */ {
                unsigned char buf[512]; int i, r;
                struct termios ts;
                usleep(1000);
                if(tcgetattr(st, &ts)) err(1, "tcgetattr");
                ts.c_lflag &= ~ICANON;
                if(tcsetattr(st, TCSANOW, &ts)) err(1, "tcsetattr");
                if((r = read(st, buf, sizeof buf)) < 0)
                        err(1, "read");
                for(i = 0; i < r; i++)
                        if(isprint(buf[i])) putchar(buf[i]);
                        else printf("\\x%02x", buf[i]);
                putchar('\n');
        }
        return 0;
}

1
我不确定这是否算作答案,但如果主程序在写入之前清除 ICANON ,则可以正确工作。 - Hasturkun
我无法这样做,因为在从设备pty中运行的程序可能不希望其处于原始模式。从主设备的角度来看,终端中运行的程序是否使用某些行编辑功能并不重要——向命令行程序发送^D,无论是tty驱动程序还是readline/editline库处理都一样。 - user10678532
如果我可以假设在从属程序中运行会打印出提示符(在写入任何内容之前,我会等待提示符),那就很容易了。但是我也不能做出这样的假设。 - user10678532
2
另一种解决方法是在主控端设置 ts.c_cc[VEOF] = _POSIX_VDISABLE;,这样就不完全是原始模式,但 EOF 被禁用了(有点像,如果我正确地阅读内核代码,NUL 将充当 EOF 并被替换为 NUL)。顺便说一下,如果我没有弄错的话,Linux 的 pty 代码实际上会使输入处理成为写操作的结果。 - Hasturkun
1个回答

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接