gpt4 book ai didi

带 fork 和管道的 C 程序

转载 作者:行者123 更新时间:2023-11-30 14:32:27 26 4
gpt4 key购买 nike

我刚开始做这些事情,我正在尝试做这个练习,但我显然做错了一些事情,但我不知道是什么。

编写一个 C 程序,以 4 个文件名作为参数。该程序必须创建 3 个子进程。每个 child 将从他的文件中读取三个字符,并通过 PIPE 将它们传达给他的父亲。父亲会检查读取的字符,如果3个 child 收到的3个字符的序列相同,则将其打印在第四个文件中。一旦其中一个子进程完成读取其文件,所有其他子进程也必须结束。

我的代码:

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
void primoFiglio(char *file,int pipe[]);
void secondoFiglio(char *file ,int pipe[]);
void terzoFiglio(char *file ,int pipe[]);

void main(int argc,char **argv){
pid_t pid;
pid_t processi[3];
int pipe1[2],pipe2[2],pipe3[2],n1,n2,n3;
int status;
int fd;
char buffer1[10],buffer2[10],buffer3[10];
if(argc!=5){
printf("Numero errato di parametri, riprovare.\n");
exit(1);
}
else{
if(pipe(pipe1)<0||pipe(pipe2)<0||pipe(pipe3)<0){
perror("Pipe error");
exit(1);
}
else{
if((pid=fork())<0){
perror("Fork");
exit(1);
}
else{
if(pid==0){
close(pipe1[0]);
primoFiglio(argv[1],pipe1);
}
else{
processi[0]=pid;
if((pid=fork())<0){
perror("Fork");
exit(1);
}
else{
if(pid==0){
close(pipe2[0]);
secondoFiglio(argv[2],pipe2);
}
else{
processi[1]=pid;
if((pid=fork())<0){
perror("Fork");
exit(1);
}
else{
if(pid==0){
close(pipe3[0]);
terzoFiglio(argv[3],pipe3);
}
else{
processi[2]=pid;
fd=open(argv[4],O_RDWR|O_APPEND|O_CREAT,0666);
if(fd<0){
perror("File");
exit(1);
}
else{
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
while((n1=read(pipe1[0],buffer1,3)>0)&&(n2=read(pipe2[0],buffer2,3)>0)&&(n3=read(pipe3[0],buffer3,3)>0)){
if(n1==3&&n2==3&&n3==3){
printf("%c %c %c\n",buffer1[0],buffer2[0],buffer3[0]);
if(buffer1[0]==buffer2[0]&&buffer2[0]==buffer3[0]){
if(buffer1[1]==buffer2[1]&&buffer2[1]==buffer3[1]){
if(buffer1[2]==buffer2[2]&&buffer2[2]==buffer3[2]){
write(fd,buffer1,n1);
}
}
}
}
}
close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(fd);
if(wait(&status)!=-1){
if(status>>8==2){
kill(processi[1],SIGKILL);
kill(processi[2],SIGKILL);

}
else if(status>>8==3){
kill(processi[0],SIGKILL);
kill(processi[2],SIGKILL);
}

else if(status>>8==4){
kill(processi[0],SIGKILL);
kill(processi[1],SIGKILL);
}

exit(0);
}
}

}
}
}
}

}
}
}
}
}

void primoFiglio(char *file ,int pipe[]){
int fd,nread;
char buffer[10];
fd=open(file,O_RDONLY);
if(fd<0){
perror("File");
exit(1);
}
else{
while((nread=read(fd,buffer,3))>0){
if(nread==3){
write(pipe[1],buffer,nread);
}
}
close(fd);
printf("Primo figlio term\n");
exit(2);
}
}
void secondoFiglio(char *file ,int pipe[]){
int fd,nread;
char buffer[10];
fd=open(file,O_RDONLY);
if(fd<0){
perror("File");
exit(1);
}
else{
while((nread=read(fd,buffer,3))>0){
if(nread==3){
write(pipe[1],buffer,nread);
}
}
close(fd);
printf("Secondo figlio term\n");
exit(3);
}

}
void terzoFiglio(char *file ,int pipe[]){
int fd,nread;
char buffer[10];
fd=open(file,O_RDONLY);
if(fd<0){
perror("File");
exit(1);
}
else{
while((nread=read(fd,buffer,3))>0){
if(nread==3){
write(pipe[1],buffer,nread);
}
}
close(fd);
printf("Terzo figlio term\n");
exit(4);
}
}

Father 无法从每个管道(只能读取一个)读取 3 个字符,因此它不会将任何内容写入第四个文件。有小费吗?

编辑谢谢您的回答对我帮助很大我尝试改进我的代码,现在它可以工作了你觉得怎么样?

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>

void child(char *file,int pipe[]);


int main(int argc,char **argv){
pid_t pid;
pid_t process[3];
int pipe1[2],pipe2[2],pipe3[2],n1,n2,n3;
int status;
int fd;
char buffer1[10],buffer2[10],buffer3[10];
if(argc!=5){
printf("Wrong number of parameters\n");
exit(1);
}
else if(pipe(pipe1)<0||pipe(pipe2)<0||pipe(pipe3)<0){
perror("Pipe error");
exit(1);
}
else if((pid=fork())<0){
perror("Fork");
exit(1);
}
else if(pid==0){
child(argv[1],pipe1);
}
else{
process[0]=pid;
if((pid=fork())<0){
perror("Fork");
exit(1);
}
else if(pid==0){
close(pipe1[0]);
close(pipe1[1]);
child(argv[2],pipe2);
}
else{
process[1]=pid;
if((pid=fork())<0){
perror("Fork");
exit(1);
}
else if(pid==0){
close(pipe2[0]);
close(pipe2[1]);
child(argv[3],pipe3);
}
else{
process[2]=pid;
fd=open(argv[4],O_RDWR|O_APPEND|O_CREAT,0666);
if(fd<0){
perror("File");
exit(1);
}
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
while((n1=read(pipe1[0],buffer1,3))>2&&(n2=read(pipe2[0],buffer2,3))>2&&(n3=read(pipe3[0],buffer3,3))>2){
printf("Reading...\n");
if(memcmp(buffer1,buffer2,3)==0&&memcmp(buffer2,buffer3,3)==0)
write(fd,buffer1,n1);
pid=waitpid(0,&status,WNOHANG);
if(pid==process[0]){
kill(process[1],SIGKILL);
kill(process[2],SIGKILL);
}
else if(pid==process[1]){
kill(process[0],SIGKILL);
kill(process[2],SIGKILL);
}
else if(pid==process[2]){
kill(process[0],SIGKILL);
kill(process[1],SIGKILL);
}


}

close(pipe1[0]);
close(pipe2[0]);
close(pipe3[0]);
close(fd);
exit(0);


}
}
}
}

void child(char *file ,int pipe[]){
int fd,nread;
char buffer[10];
fd=open(file,O_RDONLY);
if(fd<0){
perror("File");
exit(1);
}
while((nread=read(fd,buffer,3))>0){
if(nread==3){
write(pipe[1],buffer,nread);
}
}
close(fd);
exit(0);


}

最佳答案

除了各种评论之外,我注意到三个“figlio”函数彼此非常相似。它们也没有显式关闭足够的管道文件描述符。每个子进程都可以访问来自三个管道的所有 6 个文件描述符,并且理论上应该关闭除它们将用于写入的一个文件描述符之外的所有文件描述符。我创建了一个figlioGenerale()函数传递 3 对文件描述符并关闭除其中之一的写入端之外的所有文件描述符。它还获取一个标记字符串来标识它代表哪个子级,以及它应该使用的退出状态。 else为父级完成工作的 block 也可以明智地制作成一个函数。

请注意,我在 fork() 中使用了多重赋值行:

else if ((pid = process[0] = fork()) < 0)
else if ((pid = process[1] = fork()) < 0)
else if ((pid = process[2] = fork()) < 0)

这使得没有必要添加缩进级别来对 process 进行赋值。数组(原版中的 processi )。事实上,分配给pid如果使用后续测试 process[N] 则可以避免而不是pid (对于 N 的适当值 - 012 )。

我不喜欢使用SIGKILL;最好使用 SIGTERM 或 SIGHUP 来请求进程退出。实际上没有必要检查哪个进程终止了;向三个 child 中的每一个发送终止信号就足够了。我使用标准错误作为错误消息。我还安排 children 忽略SIGPIPE。这迫使write()返回错误,该错误被发现并中断了写入循环。

还可以进行其他更改 - 其中一些已在评论中概述。

所有这些都会导致这样的代码:

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static void figlioGenerale(char *file, int estatus, const char *figlio,
int p_use[], int p_spare1[], int p_spare2[]);

int main(int argc, char **argv)
{
pid_t pid;
pid_t process[3];
int pipe1[2], pipe2[2], pipe3[2], n1, n2, n3;
int status;
int fd;

char buffer1[10], buffer2[10], buffer3[10];
if (argc != 5)
{
printf("Numero errato di parametri, riprovare.\n");
fprintf(stderr, "Usage: %s infile1 infile2 infile3 outfile\n", argv[0]);
exit(1);
}
else if (pipe(pipe1) < 0 || pipe(pipe2) < 0 || pipe(pipe3) < 0)
{
perror("Pipe error");
exit(1);
}
else if ((pid = process[0] = fork()) < 0)
{
perror("Fork");
exit(1);
}
else if (pid == 0)
{
figlioGenerale(argv[1], 2, "Primo", pipe1, pipe2, pipe3);
}
else if ((pid = process[1] = fork()) < 0)
{
perror("Fork");
exit(1);
}
else if (pid == 0)
{
figlioGenerale(argv[2], 3, "Secondo", pipe2, pipe1, pipe3);
}
else if ((pid = process[2] = fork()) < 0)
{
perror("Fork");
exit(1);
}
else if (pid == 0)
{
figlioGenerale(argv[3], 4, "Terzo", pipe3, pipe1, pipe2);
}
else
{
fd = open(argv[4], O_RDWR | O_TRUNC | O_CREAT, 0666);
if (fd < 0)
{
perror("File");
exit(1);
}
else
{
close(pipe1[1]);
close(pipe2[1]);
close(pipe3[1]);
while ((n1 = read(pipe1[0], buffer1, 3) == 3) &&
(n2 = read(pipe2[0], buffer2, 3) == 3) &&
(n3 = read(pipe3[0], buffer3, 3) == 3))
{
if (memcmp(buffer1, buffer2, 3) == 0 &&
memcmp(buffer1, buffer3, 3) == 0)
{
write(fd, buffer1, 3);
}
}
close(pipe1[0]); /* Children will get SIGPIPE when writing */
close(pipe2[0]);
close(pipe3[0]);
close(fd);
kill(process[0], SIGTERM);
kill(process[1], SIGTERM);
kill(process[2], SIGTERM);
while ((pid = wait(&status)) != -1)
printf("PID %d exited with status 0x%.4X\n", pid, status);
}
}
return 0;
}

static void figlioGenerale(char *file, int estatus, const char *figlio,
int p_use[], int p_spare1[], int p_spare2[])
{
int fd, nread;
char buffer[3];
fd = open(file, O_RDONLY);
signal(SIGPIPE, SIG_IGN); /* Causes write() to return with error on EOF */

/* Close unused ends of pipes */
close(p_use[0]);
close(p_spare1[0]);
close(p_spare1[1]);
close(p_spare2[0]);
close(p_spare2[1]);

if (fd < 0)
{
fprintf(stderr, "file %s: %s\n", file, strerror(errno));
exit(1);
}
else
{
while ((nread = read(fd, buffer, 3)) > 0)
{
if (nread == 3)
{
if (write(p_use[1], buffer, nread) != nread)
break;
}
}
close(fd);
close(p_use[1]);
printf("%s figlio term\n", figlio);
exit(estatus);
}
}

运行时(从 pipe89 编译的程序 pipe89.c ),我得到如下输出:

$ (
> set -x
> rmk -u pipe89
> pipe89 pipe89.c pipe89.c pipe89.c pipe.output
> diff pipe89.c pipe.output
> ls -ld pipe89*
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> ls -dl pipe89*
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> pipe89 pipe89.c pipe89.c /dev/null pipe.output
> )
+++ rmk -u pipe89
gcc -O3 -g -I./inc -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes pipe89.c -o pipe89 -L./lib -lsoq
+++ pipe89 pipe89.c pipe89.c pipe89.c pipe.output
Primo figlio term
Terzo figlio term
Secondo figlio term
PID 24675 exited with status 0x0400
PID 24674 exited with status 0x0300
PID 24673 exited with status 0x0200
+++ diff pipe89.c pipe.output
+++ ls -ld pipe89 pipe89.c pipe89.dSYM pipe89.sh
-rwxr-xr-x 1 jleffler staff 13704 Jan 18 14:25 pipe89
-rw-r--r-- 1 jleffler staff 3444 Jan 18 14:11 pipe89.c
drwxr-xr-x 3 jleffler staff 96 Jan 18 13:45 pipe89.dSYM
-rw-r--r-- 1 jleffler staff 315 Jan 18 14:24 pipe89.sh
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
Secondo figlio term
PID 24681 exited with status 0x000F
PID 24680 exited with status 0x000F
PID 24679 exited with status 0x000F
+++ ls -dl pipe89 pipe89.c pipe89.dSYM pipe89.sh
-rwxr-xr-x 1 jleffler staff 13704 Jan 18 14:25 pipe89
-rw-r--r-- 1 jleffler staff 3444 Jan 18 14:11 pipe89.c
drwxr-xr-x 3 jleffler staff 96 Jan 18 13:45 pipe89.dSYM
-rw-r--r-- 1 jleffler staff 315 Jan 18 14:24 pipe89.sh
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
PID 24685 exited with status 0x000F
PID 24686 exited with status 0x000F
PID 24684 exited with status 0x000F
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
PID 24689 exited with status 0x000F
PID 24690 exited with status 0x000F
PID 24688 exited with status 0x000F
+++ pipe89 pipe89.c pipe89.c /dev/null pipe.output
Terzo figlio term
Secondo figlio term
PID 24694 exited with status 0x000F
PID 24693 exited with status 0x000F
PID 24692 exited with status 0x000F
$ rmk pipe89 && pipe89 pipe89.c pipe89.c /dev/null pipe.output
'pipe89' is up to date.
Terzo figlio term
PID 24699 exited with status 0x0400
PID 24698 exited with status 0x000F
PID 24697 exited with status 0x000F
$ rmk pipe89 && pipe89 pipe89.c /dev/null pipe89.c pipe.output
'pipe89' is up to date.
Secondo figlio term
PID 24708 exited with status 0x0300
PID 24709 exited with status 0x000F
PID 24707 exited with status 0x000F
$ rmk pipe89 && pipe89 /dev/null pipe89.c pipe89.c pipe.output
'pipe89' is up to date.
Primo figlio term
PID 24713 exited with status 0x0200
PID 24714 exited with status 0x000F
PID 24715 exited with status 0x000F
$

由于某种原因,当程序从子 shell 运行时,所有进程都会被 SIGTERM 信号 ( 0x000F ) 终止,但是当直接从命令行运行时,其中一个进程(读取的进程) /dev/null ) 以其自己的退出状态退出。我必须将其归因于进程调度的怪癖。

如果管道是在单个文件描述符数组中创建的,并且子进程被告知要从哪个管道描述符中读取,可能会更好。子进程可能会将其复制到文件描述符 1(标准输出 - dup2(pipes[out], STDOUT_FILENO) ),然后简单地关闭传递给它的所有 6 个描述符。

int pipes[6];
if (pipe(&pipes[0]) < 0 ||
pipe(&pipes[2]) < 0 ||
pipe(&pipes[4]) < 0)



figlioGenerale(argv[1], 2, "Primo", 1, pipes);
figlioGenerale(argv[2], 3, "Secondo", 3, pipes);
figlioGenerale(argv[3], 4, "Terzo", 5, pipes);

关于带 fork 和管道的 C 程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59803965/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com