Основная проблема - неинициализированные указатели
Когда я компилировал код в вопросе (исходный файл, ctrl61.c
) с помощью командной строки:
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes ctrl61.c -o ctrl61
(с GCC 8.2.0работающий на Mac с macOS 10.14.2 Mojave), я получил такие предупреждения, как:
ctrl61.c:78:13: error: ‘in[0]’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
для каждого из in[0]
, in[1]
и in[2]
, и указанная строка была вызовом scanf()
.Неинициализированные указатели являются источником сбоев, и, действительно, проверка кода показывает, что указатели не инициализированы.Вам нужно выделить место для указателей, на которые нужно указать.Самое простое изменение - использовать:
char in[3][50];
(хотя вы должны затем использовать% 49 каждый раз в строке формата scanf()
.) Или вы можете использовать модификатор m
для %s
и другиепоследовательные изменения, так что scanf()
выделяет память для вас.Обратите внимание, что некоторые системы (например, macOS) не поддерживают обязательный для POSIX модификатор sscanf()
.
Вы не закрываете достаточно файловых дескрипторов в дочернем (или, действительно, вparent).
Правило большого пальца : Если вы dup2()
один конец канала для стандартного ввода или стандартного вывода, закройте оба возвращенных дескриптора исходного файлана pipe()
как можно скорее.В частности, вы должны закрыть их перед использованием любого из семейства функций exec*()
.
Правило также применяется, если вы дублируете дескрипторы с dup()
или fcntl()
с `F_DUPFD.
В этой программе это может не иметь значения, но если вы работаете с трубами в более общем плане, часто очень важно убедиться, что вы закрыли всенеиспользуемые каналы, поскольку процессы могут не получить EOF, когда это необходимо.
Сообщение об ошибке
В комментариях вы упомянули об использовании perror()
для сообщения о проблемах.Лично мне не нравится perror()
за сообщения об ошибках;его форматирование недостаточно мощное.Однако это лучше, чем некоторые альтернативы.
Я обычно использую некоторый код, который доступен в моем репозитории SOQ (вопросы о переполнении стека) на GitHub в виде файлов stderr.c
и stderr.h
вподкаталог src / libsoq .Он имеет широкий контроль над форматированием.
Существует концептуально подобный пакет, err(3)
, доступный в Linux и BSD (включая macOS).Я предпочитаю мой, просто потому, что он мой (и потому что он имеет более мощные элементы управления, чем пакет err(3)
).
Контрольный код ctrl61.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc != 7)
{
fprintf(stderr, "Usage: %s arg1 arg2 arg3 arg4 arg5 arg6\n", argv[0]);
exit(1);
}
int fd[2];
pipe(fd);
pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if (pid_sln1 == 0)
{
char *paramListSln1[] =
{
"./sln1.out", argv[1], argv[2], argv[3],
argv[4], argv[5], argv[6], NULL
};
close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
execv(paramListSln1[0], paramListSln1);
fprintf(stderr, "%s: failed to exec %s\n", argv[0], paramListSln1[0]);
exit(1);
}
pid_t pid_sln2 = fork();
int sln2_status;
if (pid_sln2 < 0)
{
printf("fork error, sln2.\n");
exit(1);
}
else if (pid_sln2 == 0)
{
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);
char in[3][50];
scanf("%49s %49s %49s", in[0], in[1], in[2]);
char *const paramListSln2[] = { "./sln2.out", in[0], in[1], in[2], NULL };
execv(paramListSln2[0], paramListSln2);
fprintf(stderr, "%s: failed to exec %s\n", argv[0], paramListSln2[0]);
exit(1);
}
close(fd[0]);
close(fd[1]);
int pid1 = wait(&sln1_status);
if (sln1_status == 0)
{
fprintf(stderr, "child process %d terminated successfully\n", pid1);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4X\n", pid1, sln1_status);
exit(1);
}
int pid2 = wait(&sln2_status);
if (sln2_status == 0)
{
fprintf(stderr, "child process %d terminated successfully\n", pid2);
close(fd[1]);
}
else
{
fprintf(stderr, "child process %d failed 0x%.4X\n", pid2, sln2_status);
exit(1);
}
return(0);
}
В абсолютных повторенияхэтот код, который должен быть исправлен с помощью написания функций.
Обратите внимание, что эта версия запускает обе программы перед ожиданием выхода любой из них.
Вспомогательная программа sln1.out.c
Это близкооснованный на коде, предложенном в комментарии, но исправляющем ошибку, при которой комментарий использовал argv[1]
, но должен был использовать argv[0]
.
#include <stdio.h>
static inline void dump_args(int argc, char **argv)
{
int argnum = 0;
fprintf(stderr, "%s: %d arguments\n", argv[0], argc);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", argnum++, *argv++);
}
int main(int argc, char **argv)
{
dump_args(argc, argv);
if (argc != 7)
{
fprintf(stderr, "%s: incorrect argument count %d\n", argv[0], argc);
return(1);
}
printf("1 2 3\n");
return(0);
}
Программа sln2.out.c
отличается требованием 3 аргументов и печатью 321
вместо 1 2 3
.
Пример выполнения
$ ./ctrl61 abc zoo def pqr tuv 999
./sln1.out: 7 arguments
0: [./sln1.out]
1: [abc]
2: [zoo]
3: [def]
4: [pqr]
5: [tuv]
6: [999]
child process 15443 terminated successfully
./sln2.out: 4 arguments
0: [./sln2.out]
1: [1]
2: [2]
3: [3]
321
child process 15444 terminated successfully
$
Это показывает, что sln2.out
было передано три аргумента, считанные из стандартного вывода sln1.out
.