在IPC上工作我被要求编写一个C程序,作为其他两个C可执行文件之间的管道:
名为'sln1.out'的第一个可执行文件接收六个参数并打印三个数字 .
名为'sln2.out'的第二个可执行文件接收三个参数并打印一个数字 .
我将以下代码分为两部分 - 第一部分是对管道的写入,并且我知道它的工作原理 . 这个问题开始第二部分:我关闭了 stdin
所以现在当我使用 dup(fd[0])
新文件描述符重复列支,其中 stdin
是,我想我可以使用 scanf
从管道在这些情况下阅读 - 但对于一些它不起作用的原因
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
// check number of arguments.
if(argc != 7)
{
printf("Wrong parameters");
exit(1);
}
// creating the pipe.
int fd[2];
pipe(fd);
/* PART ONE: forking a child for 'sln1.out' that writes to fd[1] */
// I want to fork this process, and change the image of the child process to the 'sln1.out' process.
pid_t pid_sln1 = fork();
int sln1_status;
if (pid_sln1 < 0)
{
perror("fork error, sln1");
}
else if(pid_sln1 == 0)
{
char* const parmListSln1[] = {"./sln1.out",argv[1],argv[2],argv[3],
argv[4],argv[5],argv[6],NULL};
// i closed the stdout, and used 'dup' that return the file descriptor
// of stdout as duplicate of fd[1]!
close(STDOUT_FILENO);
dup(fd[1]);
execv("./sln1.out",parmListSln1);
printf("Return not expected, exacv error.\n");
exit(1);
}
// wait untill the child process terminated.
wait(&sln1_status);
if(sln1_status == 0)
{
printf("child process terminated successfully\n");
// if we want to read from fd[0] we must close the write to fd[1]
close(fd[1]);
}
else
{
printf("child process failed\n");
exit(1);
}
/* PART TWO: forking a child for 'sln2.out' that reads from fd[0] */
// The same idea - forking a child to change its image to the 'sln2.out' process.
pid_t pid_sln2 = fork();
int sln2_status;
if(pid_sln2 < 0)
{
printf("fork error, sln2.\n");
exit(1);
}
else if(pid_sln2 == 0)
{
// closing 'stdin' and the use fo 'dup' create a duplicate to the readable
// side of the pipe where the standard input should be
close(STDIN_FILENO);
dup(fd[0]);
// reading the input from the pipe - with the same method used to 'stdin'!
char* in[3];
scanf("%s %s %s",in[0],in[1],in[2]);
// build the parameters list for 'sln2.out'
char* const paramListSln2[] = { "./sln2.out", in[0], in[1], in[2], NULL };
// execute 'sln2.out'
execv("./sln2.out",paramListSln2);
printf("Return not expexted, execv error");
exit(1);
}
// wait untill the child process terminated and determine success.
wait(&sln2_status);
if (sln2_status == 0)
{
printf("2nd child process terminated successfully!\n");
exit(0);
}
else
{
printf("error with 'sln2.out' child process.\n");
exit(1);
}
exit(0);
}
我得到的输出可以提供更多细节:
child process terminated successfully
error with 'sln2.out' child process.
我很确定 sln2.out
进程的问题是因为我尝试打印扫描的参数后 scanf
失败了...
1 回答
主要问题 - 未初始化的指针
当我使用命令行编译问题(源文件,
ctrl61.c
)中的代码时:(GCC 8.2.0在带有macOS 10.14.2 Mojave的Mac上运行),我得到了如下警告:
对于
in[0]
,in[1]
和in[2]
中的每一个,标识的行是对scanf()
的调用 . 未初始化的指针是崩溃的来源,实际上,检查代码表明指针未初始化 . 您需要为要指向的指针分配存储空间 . 最简单的改变是使用:(虽然你应该再使用在
scanf()
格式字符串每次%49S),或者你可以使用m
修饰符%s
和其他相应的变化,使scanf()
分配内存给你 . 请注意,某些系统(例如macOS)不支持POSIX强制的sscanf()修饰符 .您没有在子项中(或实际上在父项中)关闭足够的文件描述符 .
Rule of thumb :如果dup2()管道的一端到标准输入或标准输出,请尽快关闭pipe()返回的两个原始文件描述符 . 特别是,在使用任何exec*()系列函数之前,应该关闭它们 .
如果使用dup()或fcntl()与`F_DUPFD复制描述符,则该规则也适用 .
在这个程序中,它可能无关紧要,但如果您更普遍地使用管道,确保关闭所有未使用的管道通常是至关重要的,因为进程可能无法在需要时获得EOF .
错误报告
在评论中,您提到使用
perror()
来报告问题 . 就个人而言,我不喜欢perror()
报告错误;它的格式不够强大 . 但是,它比一些替代品更好 .我通常在GitHub上的SOQ(Stack Overflow Questions)存储库中使用一些代码作为
stderr.c
子目录中的文件stderr.c
和stderr.h
. 这可以对格式进行广泛的控制 .在Linux和BSD(包括macOS)上有一个概念上类似的包err(3) . 我更喜欢我的,只因为它是我的(并且因为它比
err(3)
包具有更强大的控制) .控制代码ctrl61.c
这段代码中存在严重的重复,应该通过编写函数来修复 .
请注意,此版本在等待退出之前启动两个程序 .
辅助程序sln1.out.c
这是基于注释中假设的代码,但修复了注释使用
argv[1]
但应该使用argv[0]
的错误 .程序
sln2.out.c
的不同之处在于需要3个参数并打印321
而不是1 2 3
.运行示例
这表明
sln2.out
传递了从sln1.out
的标准输出读取的三个参数 .