使用STDOUT_FILENO,STDIN_FILENO和 n解释读取,写入系统调用行为

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用STDOUT_FILENO,STDIN_FILENO和 n解释读取,写入系统调用行为相关的知识,希望对你有一定的参考价值。

我知道这里有很多问题在C中讨论写,读系统调用和文件描述符。我尝试了一下但我真的找不到我想要的东西所以...

EOF on new line when reading from STDIN_FILENO?

假设我在test.c中有这个代码:

int main(void){
    char buff[8];
    int res;
    res=read(STDIN_FILENO, buff, 8);
    assert(res!=-1);
    res=write(STDOUT_FILENO, buff, res);
    assert(res!=-1);
    return 0
}

包含头文件和所有。执行文件test时为什么会出现这种情况?

$ ./test
1
1
$ ./test
11$

在第一次执行中,我使用新行(enter),而在第二次执行中,我使用ctrl + d来放置eof。在这种情况下,为什么新的线条字符会像eof一样?

我的意思是读应该尝试读取8个字符。它不应该在第一次执行中等待吗?如果我想在缓冲区中键入'1 2 3'怎么办?如何让我的程序工作?

我有两个想法,但我不知道如何验证它们:

  1. 这种行为是否与进程从管道读取的方式有关?即使有更多的东西可以到来,只想读那里的东西的想法?如果是这样:如何使读取调用成为阻塞系统调用? (我希望我不会滥用这个词)。
  2. 当键入换行符时,终端是否可以输入EOF字符?我不认为这是真的,但是如果我输入123456789 + enter而执行test在输入键后停止输入,则使用1到8之间的字符,终端执行9作为命令(这意味着它需要9和换行符) 。我只是不明白。

Why is the program still working if STDIN_FILENO and STDOUT_FILENO are switched by mistake?

所以现在我有另一个代码,test2.c

int main(void){
    char buff[8];
    int res;
    res=read(STDOUT_FILENO, buff, 8);
    assert(res!=-1);
    res=write(STDIN_FILENO, buff, res);
    assert(res!=-1);
    return 0;
}

在这两种情况下我都有相同的行为。为什么不用EBADF读写失败?这可能是我不知道的一些微妙的tty-shell互动吗?

谢谢!!

N.B:如果您对如何改进这个问题或标题有任何建议,请告诉我。

答案

将一堆在iPhone上输入的评论旋转到一个答案的外表。

Program 1

换行符就像行尾,并将数据发送到从终端读取的程序。当read()返回0时,你得到EOF。如果出现错误,它只返回-1

那么为什么read系统调用会返回?为什么不等待EOF?如果我使用read来读取文件,如果遇到行尾,它就不会返回吗?为什么stdin的处理方式不同?

它不是标准输入,被区别对待;它是被区别对待的终端。 (另请参阅Canonical vs non-canonical terminal input。)通常(在规范模式下),当您键入换行符或键入Control-D时,终端驱动程序会使输入可用。在任何情况下你都没有得到EOF。在第一个中,有两个字符要读;在第二个,一个。读取返回可用的限制。它不等待缓冲区被填充。如果你键入换行符(并且程序读取它)然后键入Control-D,则没有等待读取的字符,因此read()返回0,表示EOF。程序可以忽略它并再次尝试阅读;根据文件类型,它可能会或可能不会立即获得另一个EOF指示。当您键入1和Control-D时,则有一个字符可用,因此read()报告有一个字符可用。要获得EOF(在循环程序中),您必须再次键入Control-D,它将报告可用的0字节或EOF。请注意,如果文件中没有剩余的字节,即使磁盘文件也不等待填充缓冲区;短读取是完全正常的,但终端甚至比使用磁盘文件更常见。管道,FIFO,插座都有略微不同的规则;特殊设备(如/dev/null/dev/zero/dev/random等)也有不同的规则。

而且,从根本上说,终端的处理方式与磁盘文件和管道不同,因为合理的行为需要这样做。想一想。终端的一部分是键盘;另一部分是屏幕。它与磁盘文件非常不同。此外,必须猜测要键入多少(和哪些)字符会很麻烦,因为在填充缓冲区之前终端输入不可用。

Program 2

对于交换通道,打开终端的经典机制是将其打开以进行读写,并使每个标准I / O描述符引用它。如果没有文件描述符打开(并且没有获得getty / login进程),则open()dup()将返回最低的未打开文件描述符。标准代码运行如下:

char *tty_name = …;

int fd = open(tty_name, O_RDWR);
dup(fd);
dup(fd);

所以现在文件描述符0,1,2都引用tty_name中命名的终端。这意味着您通常可以读取标准输出或标准错误,并写入标准输入。它不能保证(如果标准输入通过管道从一个程序传输并且标准输出通过管道连接到另一个程序,它将无法工作),但它通常是以终端作为标准输入和标准输出运行的程序的选项(和标准错误) )。

以上是关于使用STDOUT_FILENO,STDIN_FILENO和 n解释读取,写入系统调用行为的主要内容,如果未能解决你的问题,请参考以下文章

使用STDOUT_FILENO,STDIN_FILENO和 n解释读取,写入系统调用行为

19重定向管道与popen模型

文件描述符和exec() close_on_exec

使用ioctl不会更新终端窗口大小

终端窗口大小不使用 ioctl 更新

使用STM32 Nucleo将数据打印到文件中