#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
void tokenizer(char* input, char** output) { //My tokenizer
char* input_dup = strdup(input);
output[0] = strtok(input_dup, " ");
int i = 1;
while ((output[i] = strtok(NULL, " ")) != NULL) {
i++;
}
}
void run_command(char** args, int* fd) { //no pipe
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
close(fd[0]);
if (fd[1] != 1)
dup2(fd[1], 1);
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
wait(pid);
char buff[1];
while (read(fd[0], buff, 1) > 0) {
if (buff[0] == EOF || buff[0] == '\0') {
printf("Caught something, returning out...");
return;
}
else {
printf("%c", buff[0]);
}
}
}
}
//pipeline function
void run_pipe(char** args, int* fd) {
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
if (fd[1] != 1) {
dup2(fd[1], 1);
}
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
if (fd[0] != 0) {
dup2(fd[0], 0);
}
wait(pid);
}
}
int main(int argc, char** argv) {
printf ("Starting myshell (mysh) \n..\n..\n");
while (1) {
char cwd[1024];
printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));
char ch[1024];
memset(ch, 0, 1023); //for cleanup
char c = 0;
int i = 0;
while (c != '\n') {
c = getchar();
if (c == EOF) {
printf ("EOF Received, exiting...\n");
return 0;
}
if (c != '\n')
ch[i] = c;
i++;
}
if (ch[0] != '\0') {
char* tokens[128];
tokenizer(ch, tokens);
//first check for keywords
if (strcmp(tokens[0], "cd") == 0) {
if (chdir(tokens[1]) < 0) {
printf("ERROR: Directory %s does not exist\n", tokens[1]);
}
}
else if (strcmp(tokens[0], "exit") == 0) {
printf("Leaving shell...\n");
return 0;
}
else {
char* commands[50];
memset(commands, 0, sizeof(commands));
int j = 0;
int k = 0;
int fd[2];
//try something different...
while (tokens[j] != NULL) {
if (strcmp(tokens[j], "|") == 0) {
commands[k] = NULL;
pipe(fd);
run_pipe(commands, fd);
j++;
k = 0;
}
//more cases here
else { //nothing special
commands[k] = tokens[j];
j++;
k++;
}
}
commands[k] = NULL;
pipe(fd);
run_command(commands, fd);
}
}
}
}
以上代码用于模拟shell。它处理单个命令并正确处理流水线操作(即ps | sort | wc返回正确的输出)但是当流水线操作完成时,它返回一个EOF,它被循环中的条件用getchar()捕获。如果我试图忽略这个EOF就会出现段错误。我在某个地方打开管道并且stdin被淹了吗?任何帮助表示赞赏。
答案 0 :(得分:1)
您需要添加#include <sys/wait.h>
,然后修复对wait()
的调用。我用过(两次):
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
可以说,这应该是一个寻找特定PID的循环,或者你应该使用waitpid()
代替。在调试shell时,您希望了解退出的每个PID及其状态。
我跑了`ps | wc'并得到:
Starting myshell (mysh)
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
4 16 117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...
如果你的意思是“代码应该继续而不是获得EOF”,那么还有一些工作要做。
我注意到这一行:
if (buff[0] == EOF || buff[0] == '\0')
buff[0]
中的字符来自read()
来电。永远不会有意义上的EOF; EOF与每个字符都不同(因此getchar()
返回int
)。这后来变得很重要:
char c = 0;
while (c != '\n')
{
c = getchar();
if (c == EOF)
由于c
是char
,因此您无法将其与EOF
进行可靠的比较。您必须将getchar()
的结果存储在int
。
我还不相信这些是造成麻烦的原因,但你必须小心。
我认为麻烦在于父代码中的run_pipe()
(修改后):
else
{
close(fd[1]);
if (fd[0] != 0)
{
dup2(fd[0], 0);
}
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
}
fd[0] != 0
条件将始终为真(非常不可能是错误的),因此您将shell的输入更改为从fd[0]
读取。你应该检讨一下;这意味着您正在读取从管道读取端到子节点的标准输入。那很糟;你丢失了原来的输入!
您的代码似乎也让父母等待孩子死亡,然后读取管道并回显标准输出。这不是一个好主意;最好让孩子(管道中的最后一个孩子)直接写入标准输出。这有两个原因:
孩子可能写的数据多于管道中的数据,所以它会阻止等待某些东西读取它的输出,但是读者将被阻止等待孩子死亡,所以你会遇到死锁
它减慢了速度,孩子的输出可能会被缓冲而不是及时出现在终端上。
我对如何处理三部分管道持怀疑态度。在运行三者的中间过程之前,您需要创建两个管道;我在你的代码中没有看到它。
答案 1 :(得分:0)
我能解决这个问题。它可能不是正确的方法,但我保存了stdin的副本,并在流水线完成时使用dup2重置它。
int in_bak = dup(0);
//stuff
dup2(in_bak, 0);
close(in_bak);