我正在尝试创建一个简单的shell,但我似乎无法使这个功能起作用。我发现它没有任何问题。我正在测试一个简单的命令,比如“ls | sort”,但我根本没有输出。
void execute(std::vector<Command *> cmds)
{
int inp[2], out[2];
pipe(inp);
pipe(out);
int status, fd = 0;
bool switcher = true;
for (auto i = 0; i < cmds.size(); i++)
{
auto pid = fork();
if (pid == -1) {
throw std::runtime_error("Could not fork.");
} else if (pid == 0) {
if (i == 0) {
dup2(inp[1], 1);
} else if (i == cmds.size() - 1) {
dup2(out[0], 0);
} else {
if (switcher) {
dup2(inp[0], 0);
dup2(out[1], 1);
switcher = false;
} else {
dup2(inp[1], 1);
dup2(out[0], 0);
switcher = true;
}
}
close(inp[0]);
close(inp[1]);
close(out[0]);
close(out[1]);
if(execvp(cmds[i]->args_char[0], cmds[i]->args_char.data()) < 0) {
std::cout << "Command not found." << std::endl;
exit(1);
}
} else {
close(inp[0]);
close(inp[1]);
close(out[0]);
close(out[1]);
wait(&status);
}
}
}
这是Command
结构:
struct Command {
int identity;
std::vector<char *> args_char;
std::vector<std::string> args_string;
bool redirectin;
bool redirectout;
std::string filename;
Command(int id, std::vector<char *> cmds_char, std::vector<std::string> cmds_string)
{
identity = id;
args_string = cmds_string;
args_char = cmds_char;
redirectin = false;
redirectout = false;
for (auto i = 0; i < args_string.size(); i++) {
if (args_string[i] == ">") {
redirectout = true;
filename = args_string.back();
args_char.erase(args_char.end() - 3, args_char.end());
break;
}
if (args_string[i] == "<") {
redirectin = true;
filename = args_string.back();
args_char.erase(args_char.end() - 3, args_char.end());
break;
}
}
if (redirectin == true && redirectout == true)
throw std::runtime_error("Invalid redirection.");
}
};
正如我在描述中所提到的,我正在创建一个可以处理多个管道的简单shell,以及将来重定向。
非常感谢别人的意见。
答案 0 :(得分:0)
你的问题是:
ls
完成,然后才能启动sort
。sort
之前,您正在关闭父级中的所有管道。在for
循环中,您只进行子处理;父母需要循环回来并启动下一个孩子。
在for
循环之后,您需要关闭父级中的管道。然后你需要一个循环来收集两个子进程的尸体(注意如果shell先前启动了一些在后台运行的进程,那么在当前管道完成之前可能存在除当前管道之外的一些尸体)。
另请注意,报告错误的正确位置为cerr
,而不是cout
。
void execute(std::vector<Command *> cmds)
{
int inp[2], out[2]; // Correct use of 2
if (pipe(inp) != 0 || pipe(out) != 0)
throw std::runtime_error("Failed to create pipes."); // may leak descriptors
int status, fd = 0;
bool switcher = true;
pid_t pids[2]; // Inappropriate use of 2
for (auto i = 0; i < cmds.size(); i++)
{
auto pid = fork();
if (pid == -1)
throw std::runtime_error("Could not fork.");
else if (pid == 0)
{
if (i == 0)
dup2(inp[1], 1);
else if (i == cmds.size() - 1)
dup2(out[0], 0);
else if (switcher)
{
dup2(inp[0], 0);
dup2(out[1], 1);
switcher = false;
}
else
{
dup2(inp[1], 1);
dup2(out[0], 0);
switcher = true;
}
close(inp[0]);
close(inp[1]);
close(out[0]);
close(out[1]);
execvp(cmds[i]->args_char[0], cmds[i]->args_char.data();
std::cerr << "Command not found." << std::endl;
exit(1);
}
}
else
pids[i] = pid;
//} else {
// close(inp[0]);
// close(inp[1]);
// close(out[0]);
// close(out[1]);
// wait(&status);
}
}
// Children launched - wait for the kids to die (morbid business!)
close(inp[0]);
close(inp[1]);
close(out[0]);
close(out[1]);
int corpse;
int kids = 2; // Inappropriate use of 2
while (kids > 0 && (corpse = wait(&status)) > 0)
{
for (int i = 0; i < kids; i++)
{
if (corpse == pids[i])
{
for (j = i+1; j < kids; j++)
pids[i++] = pids[j];
kids--;
}
}
}
}
可能有更好的方法来处理C ++中的wait()
循环,并使用vector<pid_t> kids
和适用的STL算法,但这对你来说是一个练习。显示的代码假设“有两个子进程”在几个点(标记)硬连线;你需要为管道中N个过程的更一般情况对它们进行排序,并且向量也应该有所帮助。
请注意,如果抛出异常,则不会关闭管道。这会泄漏资源。此外,如果第一个进程启动但第二个fork失败,那么您有一个(可能)不会死的进程,因为它的输入管道仍在父进程中打开。一个强大的程序需要解决这个问题。
警告:代码尚未通过编译器传递;很容易出问题。
哦,Futz!当管道中有两个命令时(如“ls | sort”),只需要一个管道。我被问题中的代码所吸引!
我在struct Command
和execute()
函数中清除了“ls | sort”案例中不需要的大部分垃圾。我派生了一个基本的main()
,它对要执行的命令进行硬编码。这导致了SSCCE(Short, Self-Contained, Correct Example),如下所示:
#include <vector>
#include <iostream>
#include <stdexcept>
#include <unistd.h>
struct Command
{
std::vector<char *> args_char;
Command(std::vector<char *> cmds_char)
{
args_char = cmds_char;
}
};
void execute(std::vector<Command *> cmds)
{
int inp[2];
if (pipe(inp) != 0)
throw std::runtime_error("Failed to create pipes.");
pid_t pids[2]; // Inappropriate use of 2
for (auto i = 0U; i < cmds.size(); i++)
{
auto pid = fork();
if (pid == -1)
throw std::runtime_error("Could not fork.");
else if (pid == 0)
{
std::cerr << i << ": PID " << (int)getpid() << "\n";
if (i == 0)
{
std::cerr << i << ": DUP 1\n";
dup2(inp[1], 1);
}
else if (i == cmds.size() - 1)
{
std::cerr << i << ": DUP 2\n";
dup2(inp[0], 0);
}
close(inp[0]);
close(inp[1]);
std::cerr << i << ": CMD " << cmds[i]->args_char[0] << "\n";
execlp(cmds[i]->args_char[0], cmds[i]->args_char[0], (char *)0);
std::cerr << "Command " << cmds[i]->args_char[0] << " not found." << std::endl;
exit(1);
}
else
pids[i] = pid;
}
// Children launched - wait for the kids to die (morbid business!)
std::cerr << "Parent: waiting for the inevitable\n";
close(inp[0]);
close(inp[1]);
int status;
int corpse;
int kids = 2; // Inappropriate use of 2
while (kids > 0 && (corpse = wait(&status)) > 0)
{
std::cerr << "2: KID " << corpse << " status " << status << "\n";
for (int i = 0; i < kids; i++)
{
if (corpse == pids[i])
{
for (int j = i + 1; j < kids; j++)
pids[i++] = pids[j];
kids--;
}
}
}
std::cerr << "2: DONE\n";
}
int main()
{
char prg0[] = "ls";
char prg1[] = "sort";
std::vector<char *> cmd0 = { prg0, 0 };
std::vector<char *> cmd1 = { prg1, 0 };
std::vector<Command *> cmds =
{
new Command(cmd0),
new Command(cmd1),
};
try
{
execute(cmds);
}
catch (std::exception &bug)
{
std::cerr << "Exception: " << bug.what() << "thrown\n";
}
return 0;
}
说明性输出:
Parent: waiting for the inevitable
0: PID 36439
0: DUP 1
0: CMD ls
1: PID 36440
1: DUP 2
1: CMD sort
2: KID 36439 status 0
cmds
cmds.cpp
cmds.dSYM
makefile
2: KID 36440 status 0
2: DONE