Reputation: 113
I'm at the beginning with these things and I'm trying to do this exercise but I'm clearly doing something wrong but I don't know what.
Write a C program that takes 4 file names as parameters. The program will have to create 3 child processes. Each child will read three characters from his file and communicate them to his father via PIPE. The father will check the characters read, if the sequence of 3 characters received by the 3 children is the same, this will be printed in the fourth file. As soon as one of the children finishes reading its file, all other child processes must also end.
My code:
#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 can't read 3 characters from each pipe (only one) so it doesn't write anything into fourth file. Any tips?
EDIT Thank you, your answers helped me a lot I tried to improve my code and now it works What do you think?
#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);
}
Upvotes: 0
Views: 456
Reputation: 753475
In addition to the various comments, I note that the three 'figlio' functions are very similar to each other. They also do not explicitly close enough pipe file descriptors. Each child has access to all 6 file descriptors from the three pipes, and should, in theory, close all but the one file descriptor they will use to write on. I created a figlioGenerale()
function which is passed 3 pairs of file descriptors and closes all except the write end of one of those. It also gets a tag string to identify which child it represents, and the exit status it should use. The else
block which does the work for the parent could sensibly be made into a function too.
Note that I use a multiple assignment in the fork()
lines:
else if ((pid = process[0] = fork()) < 0)
else if ((pid = process[1] = fork()) < 0)
else if ((pid = process[2] = fork()) < 0)
That made it unnecessary to add a level of indentation to do the assignment to the process
array (processi
in the original). Indeed, the assignment to pid
could be avoided if the subsequent tests used process[N]
instead of pid
(for an appropriate value of N
— 0
, 1
, or 2
).
I don't like the use of SIGKILL; it would be better to use SIGTERM or SIGHUP to request processes exit. There really isn't a need to check which process terminated; it is sufficient to send the terminate signal to each of the three children. I use standard error for error messages. I also arranged for the children to ignore SIGPIPE. That forces the write()
to return with an error, which is spotted and breaks the writing loop.
There are other changes that could be made — some of them are outlined in the comments.
All that leads to code like this:
#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);
}
}
When run (program pipe89
compiled from pipe89.c
), I get outputs such as:
$ (
> 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
$
For some reason, when the program is run from a sub-shell, the processes are all terminated by the SIGTERM signal (0x000F
), but when run directly from the command line, one of the processes (the one reading /dev/null
) exits with its own exit status. I have to put that down to the quirks of process scheduling.
It might be better if the pipes were created in a single array of file descriptors, and the child processes were told which pipe descriptor to read from. The child might duplicate that to file descriptor 1 (standard output — dup2(pipes[out], STDOUT_FILENO)
) and then simply close all 6 descriptors passed to it.
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);
Upvotes: 1