3

这是我在测试了一些东西后编写的一段 c 代码。

我知道这不是漏洞问题,但我不明白为什么在程序正常返回后没有刷新标准输入,此时提示返回标准输入、标准输出、标准错误。我的意思是为什么标准输入上的剩余字符在程序正常执行结束后被重定向到标准输出而不被刷新?

$cat dummy.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int main(){

    char rbuf[100];

        if (read(0, rbuf,5) == -1){
        perror("learn to count");
        printf("errno = %d.\n", errno);
        exit(1);
    }
        //printf("rbuf : %s\n",rbuf);
    return 1;   
}

这里执行:

$ gcc -o dummy dummy.c
$ ./dummy 
AAAAA /bin/sh
$  /bin/sh
sh-3.2$ exit
exit
$

我想这只是打印在提示的新标准输出上的标准输入的剩余字符串。加上最后的换行符,它以某种方式模拟用户按下的输入以执行命令。这是怎么回事?我只是想知道更多。

4

2 回答 2

2

是的,您的猜测是正确的,这些是 中的额外字符stdin

做这个:

void flush_stdin()
{
   while(getchar() != '\n');
}

注意:不要使用fflush()onstdin因为那是未定义的行为

编辑

连接到启动程序的stdin终端(即bash)。这将启动一个新程序dummy,并且stdinofdummy连接到stdinof bash

从那里开始,dummy进程read的五个字符,忽略其他字符(将它们留在stdin缓冲区中)。当控件返回时,bash它会等待直到在buffer. 低,看哪,缓冲区中有字符stdin,因此程序 - 而不是等待,开始从stdin和 因为stdin在最后,包含\n实际执行的过程。这就开始了/bin/sh。剩下的就靠/bin/sh自己操心了!

于 2013-02-15T15:08:51.427 回答
0

为了执行你的程序,shell 调用 fork(2) 来创建一个子进程,并在子进程中调用 exec(3) 用“虚拟”程序替换它自己。

我想在shell的源代码中有这样的东西(如果它是用C编写的):

if (fork() == 0)
    execlp(program, arguments)

子进程继承父进程的文件描述符;在这种情况下是外壳。因此子进程具有与执行它的 shell 相同的标准输入/标准输出,即虚拟终端。

我不确定到底是怎么回事,但我想父进程(您输入命令的原始 shell)在子进程运行时会以某种方式忽略标准输入。

当程序退出时,shell 会取回它的标准输入。程序未读取的任何额外字符都将进入 shell。当然,shell 只是将它们视为命令。

如果您首先尝试使用 fgetc(3) 而不是 read(2),则似乎多余的字符会丢失,而不是发送到 shell...但是,如果您取消缓冲 stdin,则使用 fgetc(2) 可以获得相同的效果,即:额外的字符返回到外壳。

char rbuf[100];
setbuf(stdin, NULL); // with this line - same effect as using read(2)
                     // without it - extra characters are lost
for (int i = 0; i < 5; ++i)
    rbuf[i] = (char)fgetc(stdin);

默认情况下,标准输入是行缓冲的。所以看起来在使用缓冲标准输入时可以避免这种行为,因为整行被读取,多余的字符被丢弃,而非缓冲标准输入(或低级读取)直到行尾才会读取,额外的字符仍然存在程序退出后由父级(shell)读取。

于 2013-02-15T15:41:43.050 回答