从/dev/pts/x读取时的竞争条件



我在这篇文章中读到,当进程从tty设备(比如/dev/pts/1(读取数据时,tty驱动程序/行规程(在熟模式下(会缓冲数据,只有当按下enter时,数据才会传递给进程。我进行了以下实验:

我打开一个终端,记下它正在使用的tty。假设它是/dev/pts/0
现在我打开另一个终端,为了方便起见,使用/dev/pts/1,并运行一个进程,该进程只执行以下功能:


// passing /dev/pts/0 to the function
int read_from_tty(char *tty)
{
int bytes_read = 0;
int fd = 0;
char buffer[100];

fd = open(tty, O_RDWR);
if(-1 == fd)
{
printf("Couldn't open ptsn");

return 1;
}
printf("File opened: %dn", fd);

while(1)
{
bytes_read = read(fd, buffer, 100);
if(-1 == bytes_read)
{
perror("read");
}
if(-1 == write(1, buffer, bytes_read))
{
perror("write:");
}
memset(buffer, 0, 100);
}
}

现在我开始在/dev/pts/0终端中键入字符,我看到字符主要出现在/dev/pts/1终端中,但大约10个字符中就有一个字符出现在/dev/pts/0终端中。

如果文章中所写的内容是真的,那么似乎应该在行规则中缓冲字符,并且只有当我按下回车键时,才将其传递给其中一个读取过程(假设bash只是被read挡住了(。

有人能解释一下吗?

编辑

进一步研究一下。我在上面的代码中添加了以下行:

...
bytes_read = read(fd, buffer, 100);
if(-1 == bytes_read)
{
perror("read");
}
printf("Bytes read: %dn", bytes_read);
if(-1 == write(1, buffer, bytes_read))
{
...

我可以看到,当我读取/dev/pts/0时,它一次只读取1个字节。然而,如果我用/dev/pts/1运行它(实际上是从stdin读取(,它确实读取了整行。有人能解释一下吗?

实际上,bash在从终端读取时将终端设置为非规范模式,当它得到行的末尾时,它将终端设置回规范模式以运行命令行。

同样的体验可以在两个终端上完成:

  • 终端#1(/dev/pts/6(:启动strace /bin/bash
  • 2号航站楼:启动strace cat /dev/pts/6

终端#1上的bash-shell停用规范模式,并调用pselect((等待输入:

$ strace /bin/bash
[...]
ioctl(0, TCGETS, {B38400 opost isig -icanon -echo ...}) = 0
[...]
pselect6(1, [0], NULL, NULL, NULL, {[], 8}

在终端#2上,cat命令只调用阻塞read((从终端获取字符:

$ strace cat /dev/pts/6
[...]
openat(AT_FDCWD, "/dev/pts/6", O_RDONLY) = 3
[...]
read(3, 

因此,bashcat都在终端上同时读取。当我们在终端#1中键入字符时,pselect((返回以指示字符可用,然后bash调用阻塞read((来获取字符。但来自cat的并发read((会使bashpselect((和read((的调用之间的字符保持不变。有时,bash能够在cat之前获得一个字符。

这里有一个例子,pselect((返回,因为有一个字符可用(我键入"Y"(,然后调用下面的read(([/em>将其放在终端#1上:

pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])
read(0,

但是另一个终端上catread((成功地获得了bash:的read(

write(1, "Y", 1Y)                        = 1
read(3, 

有时,bash能够在cat之前获得键入的字符。通常,当它在其read((调用中被阻止时(也就是说,它错过了pselect(([/em>检测到的字符,但它将能够在catread((的一次调用之前获得一个后续的类型化字符(。。。

旁注

当我们在bash下启动stty -a时,显示显示终端处于规范模式:

$ stty -a
[...]
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

这是因为bash在启动stty命令之前重新激活规范模式。

最新更新