我有一个程序需要executeCommand3
执行的命令列表。目标是执行一个命令列表,这些命令现在存储在此列表中,但将来将被输入要执行的类似shell的环境,这里是代码:
MAIN.C
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> //for getpid()
#include <sys/types.h> // for pid_t
#include "routines.h" //for executeCommand3()
#define SUB_PROCESSES_NUMBER 2
char *command0[] = {"ls", "tos", NULL};
char *command1[] = {"wc", NULL};
char *command2[] = {"wc", NULL};
char **commands[SUB_PROCESSES_NUMBER + 1] = {command0, command1, command2};
int main()
{
char input[1024];
printf("scan: ");
fgets(input,1024,stdin);
int value=0;
while((strcmp(input,"q\n")!=0))
{
value=executeCommand3(commands, SUB_PROCESSES_NUMBER+1);
char buffer[1024];
printf("value %i\nscan: ",value);
fgets(input,1024,stdin);
fgets(buffer,1024,stdin);
printf("%s\n",input);
}
return 0;
}
routines.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> // for open, close,read,write on FD
#include <error.h> // for error handling
#include <sys/types.h> // structs like time_t
#include <sys/wait.h> // wait, waitpid, waitid - wait for process to change state
#include "routines.h"
#define READ 0 // for file descriptor index
#define WRITE 1 // for file descriptor index
#define STDIN 0
#define STDOUT 1
int executeCommand3(char ***args, int processNumb)
{
pid_t pidList[processNumb]; //list of id of cmd processes (one for each child)
int fdBackLog[processNumb][2]; // list of file desrciptors relative for each cmd (a pair for each child)
int lastProcessFlag = 0;
int i;
for (i = 0; i < processNumb; i++) //cycle through the list of commands
{
if ((i + 1) == processNumb){
lastProcessFlag = 1;
}
if (lastProcessFlag != 1)
{
int retPipe = pipe(fdBackLog[i]);
if (retPipe < 0) // generating pipe for comunication between cmd(i) and cmd(i+1)
{
perror("pipe error");
if (i > 0)
{
close(fdBackLog[i - 1][READ]);
}
exit(EXIT_FAILURE); //exit with failure code
}
}
pidList[i] = fork();
if (pidList[i] < 0) // error fork
{
perror("error fork()");
if (i > 0)
{
close(fdBackLog[i - 1][READ]);
}
close(fdBackLog[i][READ]); // close the read pipe end of cmd(i)
close(fdBackLog[i][WRITE]); //close the write pipe end of cmd(i)
exit(EXIT_FAILURE);
}
if (pidList[i] == 0) // CHILD PROCESS
{
printf("children %i process parent %i for: %s \n", getpid(), getppid(), args[i][0]);
if (lastProcessFlag != 1)
{
close(fdBackLog[i][READ]);
dup2(fdBackLog[i][WRITE], STDOUT);
close(fdBackLog[i][WRITE]); // duplicated pipes are not useful any more
}
else
{
close(fdBackLog[i][WRITE]); //last process has nothing to write on pipe
}
if (i > 0)
{
// Also need to redirect stdin if this is not first process
dup2(fdBackLog[i - 1][READ], STDIN);
close(fdBackLog[i - 1][READ]);
}
int exitValue = execvp(args[i][0], args[i]);
perror(args[i][0]);
exit(EXIT_FAILURE); // Should not be reached;
}
close(fdBackLog[i][WRITE]);
if (i > 0)
{
close(fdBackLog[i - 1][READ]);
}
}
int wPid, status;
while((wPid = wait(&status))>0)
{
printf("Child #%i (%i)\n", wPid, status);
}
}
我的问题在于错误处理,实际上第一个命令列表的第二项是一些随机的单词。
当我执行命令时输出很好(就像在普通的shell中一样),但问题是MAIN.c中的while循环继续调用函数 executeCommand3()
。我认为这可能会发生,因为 executeCommand3()
中的某个进程会在MAIN.c的stdin中留下一些内容,因此fgets
从流中获取它并保留while循环的cicle。所以我添加了另一个fget来中断循环,但问题仍然存在......
如何修复程序,即使我有executeCommand3()
的错误,while循环停止,我可以正常重做循环?
问题出在executeCommand3()
函数中(可能在某些dup2()
,pipe()
...)或 MAIN.c ?
答案 0 :(得分:0)
一个问题是您没有错误检查fgets()
来电。如果您这样做,您会发现stdin
已关闭。以下是一些以fcntl()
调用形式进行诊断的代码,它使用标准输入文件描述符进行检查仍然有效:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define READ 0
#define WRITE 1
#define STDIN 0
#define STDOUT 1
#define SUB_PROCESSES_NUMBER 2
void executeCommand3(char ***args, int processNumb);
char *command0[] = {"ls", "tos", NULL};
char *command1[] = {"wc", NULL};
char *command2[] = {"wc", NULL};
char **commands[SUB_PROCESSES_NUMBER + 1] = {command0, command1, command2};
int main(void)
{
char input[1024];
printf("scan: ");
fgets(input, 1024, stdin);
if (fcntl(0, F_GETFD, 0) < 0)
{
perror("fcntl() - 1");
exit(1);
}
while ((strcmp(input, "q\n") != 0))
{
executeCommand3(commands, SUB_PROCESSES_NUMBER + 1);
char buffer[1024];
printf("\nscan: ");
if (fcntl(0, F_GETFD, 0) < 0)
{
perror("fcntl() - 2");
exit(1);
}
fgets(input, 1024, stdin);
fgets(buffer, 1024, stdin);
printf("%s\n", input);
}
return 0;
}
void executeCommand3(char ***args, int processNumb)
{
pid_t pidList[processNumb];
int fdBackLog[processNumb][2];
int lastProcessFlag = 0;
int i;
for (i = 0; i < processNumb; i++)
{
if ((i + 1) == processNumb)
{
lastProcessFlag = 1;
}
if (lastProcessFlag != 1)
{
int retPipe = pipe(fdBackLog[i]);
if (retPipe < 0)
{
perror("pipe error");
if (i > 0)
{
close(fdBackLog[i - 1][READ]);
}
exit(EXIT_FAILURE);
}
}
pidList[i] = fork();
if (pidList[i] < 0)
{
perror("error fork()");
if (i > 0)
{
close(fdBackLog[i - 1][READ]);
}
close(fdBackLog[i][READ]);
close(fdBackLog[i][WRITE]);
exit(EXIT_FAILURE);
}
if (pidList[i] == 0)
{
printf("children %i process parent %i for: %s \n", getpid(), getppid(), args[i][0]);
if (lastProcessFlag != 1)
{
close(fdBackLog[i][READ]);
dup2(fdBackLog[i][WRITE], STDOUT);
close(fdBackLog[i][WRITE]);
}
else
{
close(fdBackLog[i][WRITE]);
}
if (i > 0)
{
dup2(fdBackLog[i - 1][READ], STDIN);
close(fdBackLog[i - 1][READ]);
}
execvp(args[i][0], args[i]);
perror(args[i][0]);
exit(EXIT_FAILURE);
}
close(fdBackLog[i][WRITE]);
if (i > 0)
{
close(fdBackLog[i - 1][READ]);
}
}
int wPid, status;
while ((wPid = wait(&status)) > 0)
{
printf("Child #%i (%i)\n", wPid, status);
}
}
当我在没有子目录tos
的目录中运行它时,我得到这样的输出(我的程序版本被称为pipe71
):
$ ./pipe71
scan: pifflebunk
children 35712 process parent 35711 for: ls
children 35713 process parent 35711 for: wc
children 35714 process parent 35711 for: wc
ls: tos: No such file or directory
Child #35712 (256)
Child #35713 (0)
1 3 25
Child #35714 (0)
fcntl() - 2: Bad file descriptor
scan: $
$
请注意fcntl() - 2: Bad file descriptor
行。这表明executeCommand3()
函数中的某些内容关闭了父进程的标准输入。这可能不是你想要的。你可能应该在调用executeCommand3()
之前进行分叉,然后从子进程中调用该函数,让父进程等待。或者你可以在executeCommand3()
中更加小心标准输入的关闭位置。