为什么当 exec 提供无效命令时子进程的行为不同?

问题描述

        pid_t pid;
        pid = fork(); //Two processes are made
        const char* ptr = secondlinecopy;
        if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
        {
            int status;
            if (waitpid(pid,&status,0) == pid && WIFEXITED(status))
            {
                printf("Exitstatus [");
                for (int i = 0; i < noOfTokens; i++)
                {
                    printf("%s ",commands[i]);
                }
                printf("\b] = %d\n",WEXITSTATUS(status));
            }
        }
        else if (pid == 0)  //Child process. Executes commands and prints error if something unexpected happened
        {
             printf("why does this only print when an invalid command is supplied?");
             if (runBGflag==1) insertElement(getpid(),ptr);
             execvp(commands[0],commands);
             printf ("exec: %s\n",strerror(errno));
             exit(1);
        }

代码摘录中,我们看到通过 fork() 创建进程。当 execvp 被提供一个真正的命令时,例如“ls”,我们得到了输出

/home/kali/CLionProjects/clash/cmake-build-debug: ls
clash      CMakeCache.txt  cmake_install.cmake  Testing
clash.cbp  CMakeFiles      Makefile
Exitstatus [ls] = 0

但是,如果我们提供无效的命令,输出将是:

/home/kali/CLionProjects/clash/cmake-build-debug: sd
why does this only print when an invalid command is supplied?exec: No such file or directory
Exitstatus [sd] = 1

为什么会这样?进程不应该总是调用 printf("Why does ...") 然后运行 ​​exec 吗?

解决方法

为什么会这样?进程不应该总是先调用 printf("Why does ...") 然后运行 ​​exec 吗?

假设它是这样工作的:

printf(...) --> internal buffer --(fflush? newline? max_buffer_size?)--> output

通常 stdout 是行缓冲的,而您的 printf 没有换行符。要打印的数据存储在一些内部缓冲区中。当 exec-ing stdout 未被 fflush ed 并且父进程被子进程替换为整体时 - 所以所有存储在父进程中的数据,包括一些内部stdout 状态,被删除。当 exec 失败时,stdout 会在您 printf(...\n" 时刷新(或在 exit() 被块缓冲时调用 stdout 后)并显示数据。研究:工作室缓冲模式and setvbuf() function

,

为什么会这样?进程不应该总是先调用 printf("Why does ...") 然后运行 ​​exec 吗?

是的,它应该,而且您没有提出任何理由认为它没有。

printf 将输出定向到标准输出流,当它连接到交互式设备时默认为行缓冲,或块缓冲 否则。当 execvp() 成功时,它将整个程序映像替换为新程序的映像,包括任何 I/O 缓冲区的内容。任何已缓冲但未刷新到底层设备的数据都将丢失。

execvp() 失败并且程序随后正常终止时(无论其退出状态如何),所有当时缓冲的缓冲数据都会自动刷新到相关的输出设备。

如果您将换行符附加到正在打印的消息中,或者在 fflush(stdout)printf 调用之间调用 execvp,或者打印到 { {1}} 而不是 stderr,或者如果您关闭 stdout 的缓冲。