Вопрос
Я подготовил программу, которая эмулирует интерфейс Shell (CMD) с помощью труб. Существует две версии программы: 1. Использование одной трубы (используя трубу от родителя к дочернему общению) 2. Использование двойной трубы (используя две трубы от родителя к ребенку и от ребенка до родителя для связи).
Итак, первая программа обеспечивает желаемый интерфейс и работает, как хочу, но я не могу достичь того же результата (интерфейс) во второй программе (с помощью DUP2 () и аналогичных).
Итак, я реси по вашей помощи и поставлю оба кода ниже.
BS: Вы можете компилировать и попробовать обе программы таким же образом, используя эти команды:
$ gcc prog1.c -o prog1
Далее давайте запустим:
$ ./Prog1.
Давайте давайте запустим новый терминал и попробуйте написать некоторые данные для ввода .txt:
$ echo pwd> input.txt
А затем посмотрите результат в первом терминале.
(Это работает нормально для первой программы, но мне нужно сделать это работать с тем же интерфейсом во второй программе)
Код первой программы (работает нормально):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
void do_child(int data_pipe[]) {
int c;
int rc;
close(data_pipe[1]);
dup2(data_pipe[0], 0); /* This string provides the desired interface of the program */
char* cmd[] = { "bash", (char *)0 };
execvp("bash", cmd);
while ((rc = read(data_pipe[0], &c, 1)) > 0)
{
putchar(c);
}
exit(0);
}
void do_parent(int data_pipe[])
{
int c;
int rc;
FILE *in;
close(data_pipe[0]);
while (1)
{
in = fopen("input.txt", "r");
while ((c = fgetc(in)) > 0)
{
rc = write(data_pipe[1], &c, 1);
if (rc == -1)
{
perror("Parent: write");
close(data_pipe[1]);
exit(1);
}
}
fclose(in);
}
close(data_pipe[1]);
exit(0);
}
int main(int argc, char* argv[])
{
int data_pipe[2];
int pid;
int rc;
umask(0);
mknod("input.txt", S_IFIFO|0666, 0);
rc = pipe(data_pipe);
if (rc == -1)
{
perror("pipe");
exit(1);
}
pid = fork();
switch (pid)
{
case -1:
perror("fork");
exit(1);
case 0:
do_child(data_pipe);
default:
do_parent(data_pipe);
}
return 0;
}
Код второй программы (необходимо исправить немного):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
/* Original version got from http://www.iakovlev.org */
int parent_to_child[2];
int child_to_parent[2];
void do_parent()
{
int c;
char ch;
int rc;
FILE *in;
close(child_to_parent[1]); /* we don't need to write to this pipe. */
close(parent_to_child[0]); /* we don't need to read from this pipe. */
while (1)
{
in = fopen("input.txt", "r");
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
/* read back from child */
rc = read(child_to_parent[0], &ch, 1);
c = (int)ch;
if (rc <= 0) {
perror("parent: read");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
putchar(c);
}
fclose(in);
}
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(0);
}
void do_child()
{
int c;
char ch;
int rc;
close(parent_to_child[1]); /* we don't need to write to this pipe. */
close(child_to_parent[0]); /* we don't need to read from this pipe. */
//dup2(parent_to_child[0], STDIN_FILENO);
//dup2(child_to_parent[1], STDOUT_FILENO);
/* Some dup2() routines must be added here
to get this working as the first program above */
char* cmd[] = { "bash", (char *)0 };
execvp("bash", cmd);
while (read(parent_to_child[0], &ch, 1) > 0) {
c = (int)ch;
ch = (char)c;
putchar(ch);
rc = write(child_to_parent[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(parent_to_child[0]);
close(child_to_parent[1]);
exit(1);
}
}
close(parent_to_child[0]);
close(child_to_parent[1]);
exit(0);
}
int main(int argc, char* argv[])
{
int pid;
int rc;
umask(0);
mknod("input.txt", S_IFIFO|0666, 0);
rc = pipe(parent_to_child);
if (rc == -1) {
perror("main: pipe parent_to_child");
exit(1);
}
rc = pipe(child_to_parent);
if (rc == -1) {
perror("main: pipe child_to_parent");
exit(1);
}
pid = fork();
switch (pid) {
case -1:
perror("main: fork");
exit(1);
case 0:
do_child();
default:
do_parent();
}
return 0;
}
Решение
Основная разница здесь:
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
/* .... */
/* read back from child */
rc = read(child_to_parent[0], &ch, 1);
/* .... */
putchar(c);
}
Поскольку мне ленивый, чтобы компилировать / тестировать для вас, я бы просто предположил, что родитель заблокирован в чтении (). Потому что другая сторона (Bash в дочернем процессе) не гарантируется эхо каждого письменного характера. Или это может даже решить распечатать более одного символа, что ваш код не способен обрабатывать.
В том случае вы должны опросить (), чтобы увидеть, есть ли что читать или нет. Или установите флаг O_NONBLOCK на Child_To_Parent [0] с fcntl (f_etfl) и когда errno == eaegain, просто пропустите ветку Read (). И петля, пока есть еще символы для чтения.
Редактировать1. Кстати, я полностью пропустил часть: вы в Do_parent () петли должны использовать опрос () на обоих child_to_parent[0]
и in
, так как другая сторона может написать что-то (читать () не будет блокировать), даже если вы не пишете () любого символа к нему.
Другие советы
Спасибо вам, кажется, я получил это работает.
Итак, вот обновлен код do_parent:
void do_parent()
{
int c;
char ch;
int rc;
FILE *in;
struct pollfd fds[2];
int pol_ret;
fds[0].fd = child_to_parent[0];
close(child_to_parent[1]); /* we don't need to write to this pipe. */
close(parent_to_child[0]); /* we don't need to read from this pipe. */
while (1)
{
in = fopen("input.txt", "r");
fds[1].fd = fileno(in);
pol_ret = poll(fds, 2, 500);
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
/* read back from child */
if (fds[0].revents & POLLIN)
{
rc = read(child_to_parent[0], &ch, 1);
c = (int)ch;
if (rc <= 0) {
perror("parent: read");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
putchar(c);
}
}
fclose(in);
}
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(0);
}
Также я добавил это в do_child ():
dup2(parent_to_child[0], STDIN_FILENO);