在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;
}
ICANON
,则可以正确工作。 - Hasturkunts.c_cc[VEOF] = _POSIX_VDISABLE;
,这样就不完全是原始模式,但 EOF 被禁用了(有点像,如果我正确地阅读内核代码,NUL 将充当 EOF 并被替换为 NUL)。顺便说一下,如果我没有弄错的话,Linux 的 pty 代码实际上会使输入处理成为写操作的结果。 - Hasturkun