所以当我不使用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的问题就消失了。
谁能解释一下为什么?
为什么会发生这种情况
你是 fork
ing你的进程,所以现在有两个进程。
一个是父进程,也就是你的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 &
会导致与你的程序类似的问题。
你的程序是正确的;用 "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;
}