fork()和execvp(),当与sudo

问题描述 投票:0回答:1

所以当我不使用sudo调用这个程序时。它工作得很好。

#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char** argv)
{
    if(fork() == 0) execvp(argv[1], &argv[1]);
    // else wait(NULL);
}

但在使用sudo时(当我需要输入密码时),它给出了一个奇怪的输出。

pasha@skynet:~$ sudo ./a.out bash
[sudo] password for pasha:         
pasha@skynet:~$ root@skynet:~# 

然后在任何输入的时候,终端都会终止。此外,它只发生在一个新生成的终端上。而当父终端等待子终端时,sudo的问题就消失了。

谁能解释一下为什么?

c terminal fork sudo execvp
1个回答
1
投票

为什么会发生这种情况

你是 forking你的进程,所以现在有两个进程。

一个是父进程,也就是你的shell运行的进程,比如说 shell -> fork() -> exec(sudo) -> exec(./a.out). 母体终止,因为 fork 返回非零,然后 main() 接近尾声 }. main 默认情况下,返回 0. 所以shell看到你的程序以退出状态0结束,然后你的shell用一个新的 pasha@skynet:~$ 提示行后,你的程序完成。

另一个进程是子进程,从你的程序中运行,其中的 fork 返回零,就像 shell -> fork() -> exec(sudo) -> exec(./a.out) -> fork() -> exec(bash). 子女进程是: bash,它打印 root@skynet:~# (它是在 sudo)并等待输入。

这两个进程同时运行--也就是说,你的shell(你在其中执行了 sudo ./a.out)和新成立的 bash 从你的程序中运行。这两个程序都试图同时对同一个输入和输出进行读写。

子进程,即 bash需要独占终端中的输入。所以子进程 bash 执行 tcsetpgrp. 但你的shell是控制终端的那个,而不是子进程。所以子进程要么收到 信号灯 或者是SIGTTIN,当试图从输入中读取信号时。然后子bash执行了信号的默认处理程序--它终止了。

运行 sudo bash & 会导致与你的程序类似的问题。


-1
投票

你的程序是正确的;用 "ls "代替 "bash "试试。

$ ./a.out ls -al /tmp

原因是它似乎不能与 bash 就是说,基友们期待过程中成为组长的。终端前台流程组,但它不是。

也就是说,虽然程序是正确的,但它严重缺乏错误处理是令人反感的:-)。例如,当调用一个不存在的程序时。execvp() 返回一个错误(而不是根本不返回),这个错误会被忽略。其结果是......嗯......你只能猜测它是否有效。

$ ./a.out frobozzzzz
$ # (hm)

这是我的化身。更长。处理错误。看看子程序终止后的情况。

#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>


int main(int argc, char** argv)
{
    int status;
    pid_t pid, terminated;

    pid = fork();
    if (pid == -1 /*unlikely*/) {
        perror("fork()");
        exit(EXIT_FAILURE);
    }
    if (pid == 0 /*child*/) {
        if (execvp(argv[1], &argv[1]) != 0) { // when argv[1] is no
                                              // progrm in path
            perror("execvp()");
            exit(EXIT_FAILURE);
        }
        else
            assert(!"not getting here because successful exec() never returns");
    }

    // optional: wait for child to terminate, and print diagnostics
    terminated = waitpid(pid, &status, 0);
    if (terminated == -1) {
        perror("waitpid()");
        exit(EXIT_FAILURE);
    }

    if (terminated == pid) { // how come those not be equal?
        if (WIFEXITED(status))
            fprintf(stderr, "child terminated with exit status %d\n", WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            fprintf(stderr, "child terminated by %d\n", WTERMSIG(status));
        else
            fprintf(stderr, "see \"man waidpid\" for what that could be\n");
    }

    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.