I am trying to write a C program that uses pipes to send information between the parent and two children. The goal of the program is to achieve something similar to merge sort, for strings. I read the number of strings and then the Strings. The strings get divided between the 2 children, recursively until each child has only one string. I have to redirect the stdin of the child to read from the stdout of the parent.
For some reason none of the children read more than the first string.
How could I solve this problem?
int main(int argc, char * argv[]) {
int nrrows = 0;
char * buffer = NULL;
size_t n = 0;
getline(&buffer, &n, stdin);
char * endptr;
nrrows = strtol(buffer, &endptr, 10);
char rows[nrrows][MAX_LEN];
int i = 0;
n = 0;
while(i < nrrows) {
char * row = NULL;
getline(&row, &n, stdin);
strcpy(rows[i], row);
i++;
}
if(nrrows == 1) {
fprintf(stderr, "%s", rows[0]);
return 0;
}
int fdcp1[2];
int fdcp2[2];
if(pipe(fdcp1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if(pipe(fdcp2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
pid_t chpid1 = fork();
if(chpid1 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
else if(chpid1 == 0) {
close(fdcp2[0]);
close(fdcp2[1]);
close(fdcp1[1]);
dup2(fdcp1[0], STDIN_FILENO);
execlp("./forksort", "child1", NULL);
}else {
close(fdcp1[0]);
dup2(fdcp1[1], STDOUT_FILENO);
double half = (nrrows / 2);
int h = half;
char b[2];
b[0] = '0' + h;
b[1] = '\n';
write(fdcp1[1], b, sizeof(b));
for(i = 0; i < h; i ++) {
rows[i][strlen(rows[i])] = '\0';
write(fdcp1[1], rows[i], sizeof(rows[i]));
}
pid_t chpid2 = fork();
if(chpid2 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}else if(chpid2 == 0) {
close(fdcp1[0]);
close(fdcp1[1]);
close(fdcp2[1]);
dup2(fdcp2[0], STDIN_FILENO);
execlp("./forksort", "child2", NULL);
}else {
close(fdcp2[0]);
dup2(fdcp2[1], STDOUT_FILENO);
half = (nrrows / 2);
h = half;
char b[2];
b[0] = '0' + (nrrows - h);
b[1] = '\n';
write(fdcp2[1], b, sizeof(b));
for(i = h; i < nrrows; i ++) {
rows[i][strlen(rows[i])] = '\0';
write(fdcp2[1], rows[i], sizeof(rows[i]));
}
}
}
return 0;
}
It's bad news to modify a file descriptor that is associated with an open stream. I would account it highly likely to cause you trouble, and there is, moreover, no need to do that here. The parent should instead use fdopen() to open new streams on top of its ends of the pipes, and conduct I/O with its children via those instead of via the standard streams. In addition to being safer, that leaves the process's original standard streams available for it to communicate with its parent process.
With that approach, you could even stream the strings to be sorted back and forth among the processes, instead of redundantly buffering blocks of them in each process's memory. For instance, you might do something like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[]) {
char * buffer = NULL;
size_t buflen = 0;
int nrrows;
int fdpc1[2];
int fdcp1[2];
int fdpc2[2];
int fdcp2[2];
pid_t chpid1;
pid_t chpid2;
FILE *pipeout;
FILE *pipein1;
FILE *pipein2;
int half;
int i;
fprintf(stderr, "%s!!!!!!!!!!!!!!!!!\n", argv[0]);
getline(&buffer, &buflen, stdin);
fprintf(stderr, "number: %s from %s\n", buffer, argv[0]);
nrrows = strtol(buffer, NULL, 10);
if(nrrows <= 0) {
fprintf(stderr, "This is not a valid >0 number\n");
return EXIT_FAILURE;
} else if (nrrows == 1) {
/* ... read and echo back the one row ... */
getline(&buffer, &buflen, stdin);
fprintf(stderr, "%s", buffer);
return EXIT_SUCCESS;
}
/* There are at least two rows to sort */
if (pipe(fdcp1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if (pipe(fdpc1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
chpid1 = fork();
if (chpid1 == 0) {
/* this is child process 1 */
close(fdcp1[1]);
close(fdpc1[0]);
dup2(fdcp1[0], STDIN_FILENO);
close(fdcp1[0]);
dup2(fdpc1[1], STDOUT_FILENO);
close(fdpc1[1]);
execlp("./forksort", "child1", NULL);
} else if (chpid1 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
/* this is the parent process */
close(fdcp1[0]);
close(fdpc1[1]);
if (pipe(fdcp2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if (pipe(fdpc2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
chpid2 = fork();
if (chpid2 == 0) {
/* this is child process 2 */
close(fdcp1[1]);
close(fdpc1[0]);
close(fdcp2[1]);
close(fdpc2[0]);
dup2(fdcp2[0], STDIN_FILENO);
close(fdcp2[0]);
dup2(fdpc2[1], STDOUT_FILENO);
close(fdpc2[1]);
execlp("./forksort", "child2", NULL);
} else if (chpid2 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
/* this is the parent process */
close(fdcp2[0]);
close(fdpc2[1]);
/* copy the first half of the lines from input to child 1 */
pipeout = fdopen(fdcp1[1], "w");
if (pipeout == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
half = nrrows / 2;
fprintf(pipeout, "%d\n", half);
for (i = 0; i < half; i += 1) {
getline(&buffer, &buflen, stdin);
fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer);
fputs(buffer, pipeout);
}
fclose(pipeout);
/* copy the second half of the lines from input to child 2 */
pipeout = fdopen(fdcp2[1], "w");
if (pipeout == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
fprintf(pipeout, "%d\n", nrrows - half);
for (; i < nrrows; i += 1) {
getline(&buffer, &buflen, stdin);
fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer);
fputs(buffer, pipeout);
}
fclose(pipeout);
/* now read and merge sorted lines from the children */
pipein1 = fdopen(fdpc1[0], "r");
pipein2 = fdopen(fdpc2[0], "r");
if (pipein1 == NULL || pipein2 == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
/* ... */
fclose(pipein1);
fclose(pipein2);
return 0;
}
Related
I am trying to use multiple processes with the fork and pipe functions to modify a program that reads through a file and return the total number of lines, words, and characters. The program compiles and runs fine, but currently the output is only correct when the user inputs 1 for the number of child processes. For every other positive integer the program returns the correct value multiplied by the number of processes. (ex: if number of lines in file is 4 and user inputs 2 for number of processes, the returned value is 8) Does anyone know how I can fix this so that each process divides the work of reading through the file instead of each reading through the entire thing? The user enters the file name and the number of child processes they want when they run the program. I do not care about efficiency at the moment, only that the output is correct. Here is my code:
//wc.h
#ifndef WC_H
#define WC_H
#include <stdio.h>
typedef struct count_t {
int linecount;
int wordcount;
int charcount;
} count_t;
count_t word_count(FILE* fp, long offset, long size);
extern int crashRate;
#endif
//wc_mul.c
#include "wc.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#define MAX_PROC 100
#define MAX_FORK 100
int crashRate = 0;
count_t word_count(FILE* fp, long offset, long size)
{
char ch;
long rbytes = 0;
count_t count;
// Initialize counter variables
count.linecount = 0;
count.wordcount = 0;
count.charcount = 0;
printf("[pid %d] reading %ld bytes from offset %ld\n", getpid(), size, offset);
if(fseek(fp, offset, SEEK_SET) < 0) {
printf("[pid %d] fseek error!\n", getpid());
}
while ((ch=getc(fp)) != EOF && rbytes < size) {
// Increment character count if NOT new line or space
if (ch != ' ' && ch != '\n') { ++count.charcount; }
// Increment word count if new line or space character
if (ch == ' ' || ch == '\n') { ++count.wordcount; }
// Increment line count if new line character
if (ch == '\n') { ++count.linecount; }
rbytes++;
}
srand(getpid());
if(crashRate > 0 && (rand()%100 < crashRate))
{
printf("[pid %d] crashed.\n", getpid());
abort();
}
return count;
}
int main(int argc, char **argv)
{
long fsize;
FILE *fp;
int numJobs;
//plist_t plist[MAX_PROC];
count_t total, count, buf;
int i, j, pid, status, p[2];
int nFork = 0;
if(argc < 3) {
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
if(argc > 3) {
crashRate = atoi(argv[3]);
if(crashRate < 0) crashRate = 0;
if(crashRate > 50) crashRate = 50;
}
printf("crashRate RATE: %d\n", crashRate);
numJobs = atoi(argv[1]);
if(numJobs > MAX_PROC) numJobs = MAX_PROC;
total.linecount = 0;
total.wordcount = 0;
total.charcount = 0;
// Open file in read-only mode
fp = fopen(argv[2], "r");
if(fp == NULL) {
printf("File open error: %s\n", argv[2]);
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fclose(fp);
// calculate file offset and size to read for each child
for(i = 0; i < numJobs; i++) {
if(nFork++ > MAX_FORK) return 0;
if (pipe(p) != 0)
exit(1);
pid = fork();
if(pid < 0) {
printf("Fork failed.\n");
} else if(pid == 0) {
// Child
fp = fopen(argv[2], "r");
count = word_count(fp, 0, fsize);
write(p[1], &count, sizeof(count));
close(p[0]);
close(p[1]);
// send the result to the parent through the message queue
fclose(fp);
return 0;
}
}
waitpid(pid, &status, 0);
close(p[1]);
for (j=0; j < numJobs; j++) {
read(p[0], &buf, sizeof(count));
total.linecount += buf.linecount;
total.wordcount += buf.wordcount;
total.charcount += buf.charcount;
}
// Parent
// wait for all children
// check their exit status
// read the result from normalliy terminated child
// re-crete new child if there is one or more failed child
printf("\n========== Final Results ================\n");
printf("Total Lines : %d \n", total.linecount);
printf("Total Words : %d \n", total.wordcount);
printf("Total Characters : %d \n", total.charcount);
printf("=========================================\n");
return(0);
}
The pipe must be opened before you fork. Otherwise it's not available in the parent.
Pass a pointer to count, not count itself, to read and write:
write(p[1], &count, sizeof(count));
..
read(p[0], &buf, sizeof(count));
Each child needs to close the write-end of the pipe after the write via close(p[1]). Now, you're only closing the read-end.
PS: And adding the usual result checking for read and write would be advisable too.
If you use a single pipe for multiple processes, once the first child closes the pipe, it's closed and can't be read anymore.
You need an array of pipes, one for each process:
int p[numJobs][2];
Below code worked for me. I added some printf for better understanding.
int main(int argc, char **argv)
{
long fsize;
FILE *fp;
int numJobs;
count_t total, count, buf;
int i, j, pid, status;
if(argc < 3) {
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
if(argc > 3) {
crashRate = atoi(argv[3]);
if(crashRate < 0) crashRate = 0;
if(crashRate > 50) crashRate = 50;
}
printf("crashRate RATE: %d\n", crashRate);
numJobs = atoi(argv[1]);
if(numJobs > MAX_PROC) numJobs = MAX_PROC;
int p[numJobs][2];
total.linecount = 0;
total.wordcount = 0;
total.charcount = 0;
// Open file in read-only mode
fp = fopen(argv[2], "r");
if(fp == NULL) {
printf("File open error: %s\n", argv[2]);
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fclose(fp);
// calculate file offset and size to read for each child
for(i = 0; i < numJobs; i++) {
if (pipe(p[i]) != 0) exit(1);
pid = fork();
if(pid < 0) {
printf("Fork failed.\n");
exit(1);
} else if(pid == 0) {
// Child
fp = fopen(argv[2], "r");
count = word_count(fp, 0, fsize);
fclose(fp);
close(p[i][0]);
// send the result to the parent through the message queue
long bytes_sent;
if ( (bytes_sent = write(p[i][1], &count, sizeof(count)) ) ==-1) {
printf("Writing into pipe failed.\n");
exit(1);
};
printf("Child process %d sent %ld bytes (%'d lines, %'d words, %'d chars) \n",getpid(), bytes_sent, count.linecount, count.wordcount, count.charcount);
close(p[i][1]);
_exit(0);
}
}
// wait for all child processes to close
while(wait(NULL) != -1){};
long bytes_read;
for (j=0; j < numJobs; j++) {
if ((bytes_read = read(p[j][0], &buf, sizeof(buf)) ) ==-1) {
printf("Reading from pipe failed.\n");
exit(1);
};
if (bytes_read){
printf("Parent process %d read %ld bytes (%'d lines, %'d words, %'d chars) \n",getpid(), bytes_read, buf.linecount, buf.wordcount, buf.charcount);
total.linecount += buf.linecount;
total.wordcount += buf.wordcount;
total.charcount += buf.charcount;
}
close(p[j][0]);
close(p[j][1]);
}
// Parent
// wait for all children
// check their exit status
// read the result from normalliy terminated child
// re-create new child if there is one or more failed child
printf("\n========== Final Results ================\n");
printf("Total Lines : %d \n", total.linecount);
printf("Total Words : %d \n", total.wordcount);
printf("Total Characters : %d \n", total.charcount);
printf("=========================================\n");
return(0);
}
Ok so short story is:
I have a program that need to do X and it does 0.25X. I use 2 forks and 4 pipes, and I dont know how to debug this. (using eclipse c/c++ in linux env).
Long story:
I have a program that need to calculate gcd (Greatest common divisor) from a text file containing pairs of ints. This program has a father (main) and 2 childerns (forks) that need to talk to the father through pipes. (2 pipes for each childern.)
When I complie and run the program in ubuntu, I get no error but the proram doesnt complete its tasks. I have no idea where/why it breaks. How can I debug this? Im coding in eclipse c/c++ and debugger can't handle the forks.. When I debug it is reading all numbers from file (doesnt calculate gcd) but when I run in ubuntu terminal it only reads first line and breaks. here is the full code:
int main(int argc, char **argv) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}
int pfd_child1_r[2], pfd_child1_w[2], pfd_child2_r[2], pfd_child2_w[2];
if (pipe(pfd_child1_r) == -1 || pipe(pfd_child1_w) == -1
|| pipe(pfd_child2_r) == -1 || pipe(pfd_child2_w) == -1) {
perror("cannot pipe()");
return EXIT_FAILURE;
}
createChilds(pfd_child1_r, pfd_child1_w, pfd_child2_r, pfd_child2_w);
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}
char line[100];
char *token;
int numbers[2], num, line_count = 1, counter = 0, result = 0;
while (fgets(line, sizeof(line), fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "illegal input at line %d\n",
line_count);
return EXIT_FAILURE;
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
counter = 0;
if (line_count % 2 == 0) { // use first child
write(pfd_child1_w[1], &numbers[0], sizeof(int));
write(pfd_child1_w[1], &numbers[1], sizeof(int));
} else { // use second child
write(pfd_child2_w[1], &numbers[0], sizeof(int));
write(pfd_child2_w[1], &numbers[1], sizeof(int));
}
if (line_count > 1) { // after first run alternate to get result
if (line_count % 2 == 0) { // read from second child
read(pfd_child2_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else { // read from first child
read(pfd_child1_r[0], &result, sizeof(int));
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
}
line_count++;
}
fclose(fp);
return EXIT_SUCCESS;
}
void createChilds(int pfd_child1_r[2], int pfd_child1_w[2], int pfd_child2_r[2],
int pfd_child2_w[2]) {
switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);
case 0: /* First child: */
if (close(pfd_child1_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd_child1_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child1_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd_child1_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child1_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child1_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);
default: /* Parent go to next child */
break;
}
switch (fork()) {
case -1:
perror("cannot fork()");
exit(EXIT_FAILURE);
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd_child2_r[0]) == -1) { /* Read end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[1]) == -1) { /* Write end is unused */
perror("cannot close()");
exit(EXIT_FAILURE);
}
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd_child2_w[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd_child2_r[1], STDOUT_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_r[1]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd_child2_w[1] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd_child2_w[0], STDIN_FILENO) == -1) {
perror("cannot dup2()");
exit(EXIT_FAILURE);
}
if (close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
execlp("./v1_child", "./v1_child", NULL); /* Writes to pipe */
exit(EXIT_SUCCESS);
default: /* Parent falls through */
break;
}
/* Parent closes unused file descriptors for pipe */
if (close(pfd_child1_r[1]) == -1 || close(pfd_child1_w[0]) == -1
|| close(pfd_child2_r[1]) == -1 || close(pfd_child2_w[0]) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
the second file is the gcd file, i still have not finish it and the loop the should keep geting the numbers is not present. but i just want to get the first line working properly then i will fix the rest.
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}
int main(int argc, char **argv) {
int n1, n2, result;
if (scanf("%d %d", &n1,&n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2,n1);
printf("%d", result);
}
How to Debug
An easy way to debug is always to add fprintf(stderr, "...") statements to the child program. Then you can run the program and also see what the child processes are doing.
Transfer Values
Since you redirect stdin and stdout and use sscanf/printf in the v1_child program which calculates gcd, I assume you want to transfer the values as strings.
An easy way could be to use fprintf to write ints as formatted strings. You could use fdopen to associate a stream to an existing pipe file descriptor.
Accordingly, you must convert numbers from and to strings.
Variable Length Data and Buffered I/O
If you use strings to transfer values, each pair of values has a variable length. Typically, a newline character is used in a C program to signal a complete input record.
Another reason for reading/writing a whole line is that read/write calls can also transfer only a partial number of bytes. You must therefore know when an input record is completed. An alternative would be to have a binary format, which would automatically represent a format with fixed lengths.
By using streams you work with buffered I/O, with fflush you can ensure that all buffered data is written through the underlying write function of the stream.
Functions
One could divide the features into several functions to make the flow a little easier to understand.
Possible Improvements
This should perhaps already be a start.
Another possible improvement could be the use of strtol instead of atoi, since atoi does not perform error checking. Similar sscanf does not report conversion errors (e.g. non-numeric characters at the end of the line), at least we look at the number of assigned input items.
There are presumably still possibilities to improve the readability of the code.
With waitpid the exit status code of the child could be checked in the parent.
Program
Your code, slightly modified in the points mentioned above, could then look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void create_pipe(int *);
void close_fd(int);
int child(const int *, const int *);
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count);
void write_to_child(FILE *fp, const int *numbers);
int read_from_child(FILE *fp);
int main(int argc, char *argv[]) {
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "usage: %s <FILE NAME>\n", argv[0]);
return EXIT_FAILURE;
}
int pfd_child1_r[2];
int pfd_child1_w[2];
int pfd_child2_r[2];
int pfd_child2_w[2];
create_pipe(pfd_child1_r);
create_pipe(pfd_child1_w);
create_pipe(pfd_child2_r);
create_pipe(pfd_child2_w);
pid_t pid1 = fork();
if (pid1 == 0) { //child 1
close_fd(pfd_child2_r[0]);
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
close_fd(pfd_child2_w[1]);
return child(pfd_child1_r, pfd_child1_w);
} else if (pid1 > 0) {
close_fd(pfd_child1_r[1]);
close_fd(pfd_child1_w[0]);
pid_t pid2 = fork();
if (pid2 == 0) { //child 2
close_fd(pfd_child1_r[0]);
close_fd(pfd_child1_w[1]);
return child(pfd_child2_r, pfd_child2_w);
} else if (pid2 > 0) {
close_fd(pfd_child2_r[1]);
close_fd(pfd_child2_w[0]);
FILE *fp_child1_w = fdopen(pfd_child1_w[1], "w");
FILE *fp_child2_w = fdopen(pfd_child2_w[1], "w");
FILE *fp_child1_r = fdopen(pfd_child1_r[0], "r");
FILE *fp_child2_r = fdopen(pfd_child2_r[0], "r");
if (!fp_child1_w || !fp_child2_w || !fp_child1_r || !fp_child2_r) {
perror("fdopen() failed");
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("fopen(): ");
return EXIT_FAILURE;
}
char line[100];
int numbers[2], line_count = 0;
while (read_input_line(fp, line, sizeof(line), numbers, line_count) == 2) {
if (line_count % 2 == 0) {
write_to_child(fp_child1_w, numbers);
} else {
write_to_child(fp_child2_w, numbers);
}
if (line_count % 2 == 0) {
int result = read_from_child(fp_child1_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
} else {
int result = read_from_child(fp_child2_r);
printf("%d %d\t\tgcd: %d\n", numbers[0], numbers[1], result);
}
line_count++;
}
//fclose closes also associated file descriptor
fclose(fp_child1_w);
fclose(fp_child2_w);
fclose(fp_child1_r);
fclose(fp_child2_r);
fclose(fp);
return EXIT_SUCCESS;
} else {
perror("second fork failed");
return EXIT_FAILURE;
}
} else {
perror("first fork failed");
return EXIT_FAILURE;
}
}
int read_input_line(FILE *fp, char *line, int max, int *numbers, int line_count) {
char *token;
int num, counter = 0;
line[0] = '\0';
if (fgets(line, max, fp) != NULL) {
token = strtok(line, " ");
while (token != NULL) {
num = atoi(token);
if (num < 1 || counter == 2) {
fprintf(stderr, "illegal input at line %d\n", line_count + 1);
exit(EXIT_FAILURE);
}
numbers[counter] = num;
counter++;
token = strtok(NULL, " ");
}
}
return counter;
}
int read_from_child(FILE *fp) {
char buf[128];
int result = -1;
if (fgets(buf, sizeof(buf), fp)) {
if (sscanf(buf, "%d", &result) == 1)
return result;
}
return -1;
}
void write_to_child(FILE *fp, const int *numbers) {
fprintf(fp, "%d %d\n", numbers[0], numbers[1]);
fflush(fp);
}
int child(const int *pfd_child_r, const int *pfd_child_w) {
dup2(pfd_child_r[1], STDOUT_FILENO);
dup2(pfd_child_w[0], STDIN_FILENO);
close_fd(pfd_child_r[0]);
close_fd(pfd_child_r[1]);
close_fd(pfd_child_w[0]);
close_fd(pfd_child_w[1]);
execlp("./v1_child", "./v1_child", NULL);
fprintf(stderr, "execution of v1_child failed\n");
exit(EXIT_FAILURE);
}
void create_pipe(int *fd) {
if (pipe(fd) == -1) {
perror("cannot pipe()");
exit(EXIT_FAILURE);
}
}
void close_fd(int fd) {
if (close(fd) == -1) {
perror("cannot close()");
exit(EXIT_FAILURE);
}
}
The corresponding v1_child.c could look like:
#include <stdio.h>
#include <stdlib.h>
int gcd(int n1, int n2) {
if (n2 == 0)
return n1;
return gcd(n2, n1 % n2);
}
int main(void) {
int n1, n2, result;
char buf[128];
while(fgets(buf, sizeof(buf), stdin)) {
if (sscanf(buf, "%d %d", &n1, &n2) != 2) {
fprintf(stderr, "error reading numbers in child\n");
return -1;
}
if (n1 > n2)
result = gcd(n1, n2);
else
result = gcd(n2, n1);
printf("%d\n", result);
fflush(stdout);
}
return EXIT_SUCCESS;
}
Test
With the input of
5 25
49 14
64 462
1155 84
the output would be
5 25 gcd: 5
49 14 gcd: 7
64 462 gcd: 2
1155 84 gcd: 21
You write binary values to the pipes, but try to read text representation. So either use a variation of printf (like dprintf) in the parent, or (better) use read (and write) in the child.
I've written code for a shell that we call opsh (the OPen SHell).
Now my friend found an issue about backslash preceeded characters that don't behave as expected.
Example
opsh does not seem to pass on the -character to echo.
In opsh:
$
$echo "TEXT\n"
TEXTn
$
whereas in ksh:
$
$echo "TEXT\n"
TEXT
I don't know how to do it. In my code I just use a pipeline for the commands (you may look at the entire code in the repo) where the relevant code looks as follows
int run_cmd(char *cmd, bool background) {
char buffer[2];
buffer[0] = '|';
buffer[1] = '\0';
struct str_list *chunks = list_split(cmd, buffer);
struct pipeline *pipe = malloc(chunks->pipes * sizeof *pipe);
if (pipe == NULL) {
fprintf(stderr, "malloc failed!\n");
}
int i = 0;
for (i = 0; i < chunks->pipes; i++) {
pipe[i].data = malloc(sizeof(char **) * BUFFER_SIZE * chunks[i].size);
if (pipe[i].data == NULL) {
fprintf(stderr, "malloc failed!\n");
}
int j = 0;
pipe[i].size = chunks[i].size;
for (j = 0; j < chunks[i].size; j++) {
if (chunks[i].argv[j] == NULL) {
chunks[i].argv[j] = '\0';
break;
}
pipe[i].option = malloc(sizeof(int) * 10);
if (pipe[i].option == NULL) {
fprintf(stderr, "malloc failed!\n");
}
pipe[i].data[j] = strdup(chunks[i].argv[j]);
if (pipe[i].data[j] == NULL) {
perror("strdup");
exit(EXIT_FAILURE);
}
* pipe[i].option = * chunks[i].option;
}
pipe[i].data[j] = '\0';
}
int status = execute_pipeline(chunks->pipes, pipe, background);
return status;
}
int execute_pipeline(int n, struct pipeline *pipe, bool background) {
// background = false;
int status = 0;
pid_t pid = -2;
if (n > -1)
pid = fork();
if (pid < 0) {
perror("fork failed");
return -1;
}
/* If we are the child process, then go execute the string.*/
if (pid == 0) {
/* spawn(cmd);*/
fork_pipeline(n, pipe);
}
/*
* We are the parent process.
* Wait for the child to complete.
*/
if (!background) {
while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR));
fprintf(stderr, "%d: executed\n", (int) pid);
}
if (pid < 0) {
fprintf(stderr, "Error from waitpid: %s", strerror(errno));
return -1;
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "pid %ld: killed by signal %d\n",
(long) pid, WTERMSIG(status));
return -1;
}
return WEXITSTATUS(status);
}
/* Helper function that forks pipes */
void fork_pipeline(int n, struct pipeline *structpipeline) {
int i;
int in = 0;
int fd[2];
for (i = 0; i < n - 1; ++i) {
if (pipe(fd) == -1) {
err_syserr("Failed creating pipe");
}
spawn_pipe(in, fd[1], structpipeline + i);
close(fd[1]);
in = fd[0];
}
if (dup2(in, 0) < 0) {
err_syserr("dup2() failed on stdin for %s: ", structpipeline[i].data[0]);
}
if (strstr(structpipeline[i].data[0], "=")) {
/* do nothing for now, handle in parser instead... */
}
else {
fprintf(stderr, "%d: executing %s\n", (int) getpid(), structpipeline[i].data[0]);
//fprintf(stderr, "\n");
if (* (structpipeline[i].option) == 1) { /* output redirection */
int length = structpipeline[i].size;
char *filename = structpipeline->data[length - 1];
for (int k = length - 2; k < length; k++)
structpipeline->data[k] = '\0';
fd[1] = open(filename, O_WRONLY | O_CREAT, 0666);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
} /* TODO: input redirection */
execvp(structpipeline[i].data[0], structpipeline[i].data);
err_syserr("failed to execute %s: ", structpipeline[i].data[0]);
}
}
Here I have a program where a parent process creates several child processes, passes
a distinct integer to each of them. Then each child process writes back the integer read to the parent process, which prints the result to standard output:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define R 0
#define W 1
void run_child(int in, int out){
int r;
int it;
while((r = read(in, &it, sizeof(it))) != 0){
if(r == -1){
perror("read");
exit(1);
}
//word[r] = '\0';
int w = write(out, &it, sizeof(it));
if(w == -1){
perror("write");
exit(1);
}
if(close(out) == -1){
perror("close");
exit(1);
}
}
}
int main(int argc, char **argv) {
// Process fan
int i;
int n;
int num_kids;
int from_parent[2];
int to_parent[2];
if(argc != 2) {
fprintf(stderr, "Usage: fan_write <numkids>\n");
exit(1);
}
num_kids = atoi(argv[1]);
int status;
char word[32];
for(i = 0; i < num_kids; i++) {
if(pipe(from_parent) == -1){
perror("pipe");
exit(1);
}
if(pipe(to_parent) == -1){
perror("pipe");
exit(1);
}
int g = i;
write(from_parent[W], &g, sizeof(int));
n = fork();
if(n < 0) {
perror("fork");
exit(1);
}
if(n == 0){
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1){
perror("close");
exit(1);
}
dup2(from_parent[R], STDIN_FILENO);
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
run_child(STDIN_FILENO, to_parent[W]);
close(to_parent[W]);
exit(0);
}
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
for(i=0;i<num_kids;i++){
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
}
for(i = 0; i < num_kids; i++){
wait(&status);
}
return 0;
}
With num_kids = 4 I would expect the program to read 4 bytes each time and print the
distinct integer. However, when run it reads 4 bytes in one iteration, and then it reads
0 bytes on the following iterations, and prints the same integer over and over. I'm not sure how to fix it.
Edit: Solved! Hint: use a matrix of file descriptors for pipes.
you have wrong concept to read numbers from all num_kids child process in a loop, your are reading every time from single child and in each child, by loop:
for(i=0;i<num_kids;i++){
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
Is code runs for each child including parent too because you are unconditionally running this loop in child/parent process. But parent can read from single child that the reason you fist time get 4 byte then 0. because child return a number one time. Remove above for loop from your code and do like I am suggesting below:
you should do like(read comments):
if(n == 0){
//child
}
else{
// read in parent without loop
}
So correct way is:
if(n == 0){//child
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1){
perror("close");
exit(1);
}
dup2(from_parent[R], STDIN_FILENO);
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
run_child(STDIN_FILENO, to_parent[W]);
close(to_parent[W]);
exit(0);
}
else{ // parent
write(from_parent[W], &g, sizeof(int));
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
And its working like:
:~$ ./a.out 4
read 4 bytes
0
read 4 bytes
1
read 4 bytes
2
read 4 bytes
3
if (n == 0) { //child
if(close(from_parent[W]) == -1) {
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1) {
perror("close");
exit(1);
}
run_child(from_parent[R], to_parent[W]);
close(from_parent[R]); // ignore checking return code here!
close(to_parent[W]); // ignore checking return code here!
exit(0);
}
// And this is what run_child looks like
void run_child(int in, int out){
int r;
int it;
while((r = read(in, &it, sizeof(it))) != 0){
if(r == -1){
perror("read");
exit(1);
}
int w = write(out, &it, sizeof(it));
if(w == -1){
perror("write");
exit(1);
}
}
}
I've mixed up the aswers code and this is the final result..
All succesful
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define R 0
#define W 1
void run_child(int in, int out){
int r;
int it;
while((r = read(in, &it, sizeof(it))) != 0){
if(r == -1){
perror("read");
exit(1);
}
int w = write(out, &it, sizeof(it));
//perror("write:");
//close(out) ;
exit(1);
}
}
int main(int argc, char **argv) {
// Process fan
int i;
int n;
int num_kids;
int from_parent[2];
int to_parent[2];
if(argc != 2) {
fprintf(stderr, "Usage: fan_write <numkids>\n");
exit(1);
}
num_kids = atoi(argv[1]);
int status;
char word[32];
for(i = 0; i < num_kids; i++) {
if(pipe(from_parent) == -1){
perror("pipe");
exit(1);
}
if(pipe(to_parent) == -1){
perror("pipe");
exit(1);
}
int g = i;
write(from_parent[W], &g, sizeof(int));
n = fork();
if(n < 0) {
perror("fork");
exit(1);
}
if(n == 0){//child
if(close(from_parent[W]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[R]) == -1){
perror("close");
exit(1);
}
run_child(from_parent[R], to_parent[W]);
if(close(from_parent[R]) == -1){
perror("close");
exit(1);
}
if(close(to_parent[W]) == -1){
perror("close");
exit(1);
}
exit(0);
}
else{ // parent
write(from_parent[W], &g, sizeof(int));
int read_int;
int r = read(to_parent[R], &read_int, sizeof(int));
printf("read %d bytes\n", r);
if(r == -1){
perror("read");
exit(1);
}
printf("%d\n", read_int);
}
}
for(i = 0; i < num_kids; i++){
wait(&status);
}
return 0;
}
I can't exit or terminate children processes sending a signal.
Could you please tell me what I'm doing wrong in this code:
//###################################### INVERTER.C (main)
#include <semaphore.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include "timeprofiler.h"
#include "ppmtools.h"
//Global vars:
int shmids[4], shmPixelId, *total_lines, *processed_lines, *next_line, *buf_vars;
//To share unnamed semaphores between processes, they must be allocated in a shared memory.
mem_struct *sh_mm;
//unnamed semaphores
sem_t *mutex1, *mutex2, *mutex3, *sem_remaining_lines;
//struct that will hold the image in shared memory
image_struct *image;
pid_t *workersPID;
header *h;
int main(int argc, char *argv[]) {
int i, j, k, cur = 0, id;
pixel *row;
double start, stop, startms, stopms;
if (argc < 3) {
printf("Incorrect usage.\nPlease use \"./invert input_filename.ppm output_filename.ppm\"\n");
return -1;
}
//BLOCK ALL SIGNAL
sigset_t block_ctrlc;
sigfillset(&block_ctrlc);
sigdelset(&block_ctrlc, SIGINT);
sigprocmask(SIG_BLOCK, &block_ctrlc, NULL);
//start timer
start = getCurrentTimeMicro();
startms = getCurrentTimeMili();
printf("Opening input file [%s]\n", argv[1]);
FILE *fpin = fopen(argv[1], "r");
if (fpin == NULL) {
printf("Could not open input file\n");
return -1;
}
printf("Opening output file [%s]\n", argv[2]);
FILE *fpout = fopen(argv[2], "w");
if (fpout == NULL) {
printf("Could not open output file\n");
return -1;
}
printf("Getting header\n");
h = getImageHeader(fpin);
if (h == NULL) {
printf("Error getting header from file\n");
return -1;
}
printf("Got file Header: %s - %u x %u - %u\n", h->type, h->width, h->height, h->depth);
printf("Saving header to output file\n");
if (writeImageHeader(h, fpout) == -1) {
printf("Could not write to output file\n");
return -1;
}
init();
printf("After init...\n");
//alloc mem space for one row (width * size of one pixel struct)
row = (pixel *) malloc(h->width * sizeof (pixel));
printf("Starting work\n");
for (i = 0; i < h->height; i++) {
printf("Reading row... \n");
if (getImageRow(h->width, row, fpin) == -1) {
printf("Error while reading row\n");
}
printf("Got row %d || \n", (i + 1));
for (j = cur, k = 0; j < cur + h->width; j++, k++) {
image->pixel_data[j].red = row[k].red;
image->pixel_data[j].blue = row[k].blue;
image->pixel_data[j].green = row[k].green;
}
cur += h->width;
}
/*Creates workers*/
workersPID = (pid_t*) malloc(sizeof (pid_t) *((NUM_WORKERS)));
for (i = 0; i < NUM_WORKERS; i++) {
id = fork();
if (id == -1) {
printf("Error creating worker no %d\n", i);
return (EXIT_FAILURE);
} else if (id == 0) {
workersPID[i] = getpid();
printf("Launching son with pid %d\n", getpid());
worker(i);
}
}
cur = 0;
sem_wait(mutex2);
/*Writes the invert image on the output file*/
for (i = 0; i < h->height; i++) {
for (j = cur, k = 0; j < cur + h->width; j++, k++) {
row[k].red = image->pixel_data[j].red;
row[k].blue = image->pixel_data[j].blue;
row[k].green = image->pixel_data[j].green;
}
cur += h->width;
printf("Saving row... \n");
if (writeRow(h->width, row, fpout) == -1) {
printf("Error while writing row\n");
}
printf("Done\n");
}
printf("Cleaning up...\n");
//clean up row
free(row);
//clean up header
free(h);
printf("Closing file pointers.\n");
fclose(fpin);
fclose(fpout);
//stop timer
stop = getCurrentTimeMicro();
stopms = getCurrentTimeMili();
for (i = 0; i < NUM_WORKERS; i++) {
if (workersPID[i]) {
kill(workersPID[i], SIGTERM);
waitpid(workersPID[i], NULL, 0);
}
}
terminate();
printTimeElapsed(start, stop, "microseconds");
printTimeElapsed(startms, stopms, "miliseconds");
printf("Done!\n");
return 0;
}
void init() {
//create shared memory to hold the source image:
if ((shmids[0] = shmget(IPC_PRIVATE, sizeof (image_struct), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate image struct failed. Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
image = (image_struct*) shmat(shmids[0], NULL, 0);
//shared memory to allocate the pointer to pointer pixel_data
if ((shmids[1] = shmget(IPC_PRIVATE, h->width * h->height * sizeof (pixel), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate pixel_data array failed. Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
image->pixel_data = (pixel*) shmat(shmids[1], NULL, 0);
/*Shared Memory segment for 3 integers*/
if ((shmids[2] = shmget(IPC_PRIVATE, 3 * sizeof (int), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate the 3 integers failed. Errno returned; %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
buf_vars = (int*) shmat(shmids[2], NULL, 0);
total_lines = &buf_vars[0];
processed_lines = &buf_vars[1];
next_line = &buf_vars[2];
*processed_lines = *next_line = 0;
*total_lines = h->height;
if ((shmids[3] = shmget(IPC_PRIVATE, sizeof (mem_struct), IPC_CREAT | 0700)) == -1) {
printf("shmget to allocate mem_Struct for semaphores failed. Errno returned %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
sh_mm = (mem_struct*) shmat(shmids[3], NULL, 0);
if (sem_init(&sh_mm->mutex1, 1, 1) == -1) {
printf("Error initializing semaphore mutex1.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
mutex1 = &sh_mm->mutex1;
if (sem_init(&sh_mm->mutex2, 1, 0) == -1) {
printf("Error initializing semaphore mutex2.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
mutex2 = &sh_mm->mutex2;
if (sem_init(&sh_mm->mutex3, 1, 1) == -1) {
printf("Error initializing semaphore mutex3.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
mutex3 = &sh_mm->mutex3;
if (sem_init(&sh_mm->sem_remaining_lines, 1, h->height) == -1) {
printf("Error initializing semaphore sem_remaining_lines.Errno returned: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
sem_remaining_lines = &sh_mm->sem_remaining_lines;
}
/*Worker process*/
void worker(int id) {
int i, k, cur = 0;
pixel *row;
//Block all signals, except SIGINT and SIGKILL which are handled
sigset_t block_ctrlc;
sigfillset(&block_ctrlc);
sigdelset(&block_ctrlc, SIGINT);
sigdelset(&block_ctrlc, SIGTERM);
sigprocmask(SIG_BLOCK, &block_ctrlc, NULL);
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
while (sem_wait(sem_remaining_lines)!= -1) { //if there are still lines to read, go on
sem_wait(mutex3);
cur = *next_line; //current image's line
*next_line += h->width; //refreshs line for the next worker
sem_post(mutex3);
row = (pixel *) malloc(h->width * sizeof (pixel));
for (i = cur, k = 0; i < cur + h->width; i++, k++) {
row[k].red = image->pixel_data[i].red;
row[k].blue = image->pixel_data[i].blue;
row[k].green = image->pixel_data[i].green;
}
//printf("% - Inverting row... \n",id);
invertRow(h->width, row); //invert
//printf("Done || \n");
for (i = cur, k = 0; i < cur + h->width; i++, k++) {
image->pixel_data[i].red = row[k].red;
image->pixel_data[i].blue = row[k].blue;
image->pixel_data[i].green = row[k].green;
}
sem_wait(mutex1);
*processed_lines += 1; //increases the number of inverted lines
if (*processed_lines == *total_lines) { //check if it reaches last line
sem_post(mutex2); //if so, wakes the master telling that is ready
}
sem_post(mutex1);
}
//printf("Son %d is exiting\n",id);
exit(0);
}
void handle_signal(int signum) {
if(signum == SIGINT)
signal(SIGINT, handle_signal);
else
signal(SIGTERM, handle_signal);
exit(0);
}
void terminate() {
int i;
//close semaphores
sem_destroy(mutex1);
sem_destroy(mutex2);
sem_destroy(mutex3);
sem_destroy(sem_remaining_lines);
//cleans up shared memory = removes shared memory segments
for (i = 0; i < 4; i++) {
shmctl(shmids[i], IPC_RMID, NULL);
}
}
I'm gonna leave the explanation of the assignment (that has already finished btw)here:
1 page pdf
Your worker threads have SIGTERM blocked (because it was blocked in main, and sigprocmask doesn't remove signals from the blocked set unless explicitly told to do so)
You may want to do something like this in the worker instead:
sigemptyset(&block_ctrlc);
sigaddset(&block_ctrlc, SIGINT);
sigaddset(&block_ctrlc, SIGTERM);
sigprocmask(SIG_UNBLOCK, &block_ctrlc, NULL);
Alternately, call sigprocmask with SIG_SETMASK.